当前位置:首页>java>代码里的“和平协议”:用MoonBit实现无冲突的多人编辑

代码里的“和平协议”:用MoonBit实现无冲突的多人编辑

  • 2026-02-07 06:55:16
代码里的“和平协议”:用MoonBit实现无冲突的多人编辑

作者:东灯

原标题:用 MoonBit 实现 CRDTs 算法并构建实时协作应用

一.引言

当你在 Google Docs 中与同事同时编辑一份文档,或者在 Figma 中与设计师协作调整界面时,你有没有想过:为什么两个人同时在同一位置输入文字,最终结果却不会互相覆盖或产生乱码?

这背后隐藏着分布式系统中最具挑战性的问题之一: 并发冲突( Concurrent Conflict) 。当多个用户同时编辑同一份数据,且这些编辑需要在不同设备之间同步时,如何保证所有人最终看到相同的结果?

本文将带你深入理解实时协作的核心算法演进——从经典的操作转换(OT)到经典的 CRDTs 算法 RGA,再到现代的 EG-Walker。我们不仅会解释这些算法的原理,还会用 MoonBit 实现算法的核心逻辑,并最终展示如何用它构建一个简单的、断网重连可合并的协作编辑器。

二.实时协作的核心挑战

假设我们要构建一个多人协作的文本编辑器。每个用户在自己的设备上都有一份文档副本,当用户进行编辑时,操作会通过网络同步给其他用户。为了保证流畅的编辑体验,用户的输入应该立即生效,而不是等待服务器确认。

问题来了:当两个用户同时编辑时,会发生什么?让我们考虑这个场景:

这就是并发冲突的本质: 相同的操作序列,以不同顺序应用,可能产生不同的结果 。

我们需要的是一种机制,无论操作以什么顺序到达,最终所有人看到的结果都相同,而且尊重所有编辑参与者的贡献。这个性质叫做 最终一致性 (Eventual Consistency) 。

三.操作转换( OT )

1、原理与简单实现

操作转换(Operational Transformation,OT) 是最早用于解决实时协作冲突的算法,诞生于 1989 年。Google Docs、Etherpad 等早期协作工具都采用了这种方案。OT 的基本思路是:既然操作之间会互相影响,那就在应用操作之前,根据已发生的操作对其进行“转换”,使其适应当前状态。

看看 OT 是如何解决冲突的:

  • 初始文档是 AB 。Alice 在位置 1 插入 X ,本地变成 AXB ;Bob 在位置 1 插入 Y ,本地变成 AYB 。现在双方需要同步对方的操作。

  • 当 Alice 收到 Bob 的操作 insert(1, 'Y') 时,她不能直接执行——因为她已经在位置 1 插入了 X ,后面的字符都向右移了一位。OT 发现 Bob 的插入位置(1)不在 Alice 插入位置之前,于是把位置 +1,变成 insert(2, 'Y') 。Alice 执行后得到 AXYB 。

  • 同样,Bob 收到 Alice 的 insert(1, 'X') 。但 Alice 的插入位置(1)不比 Bob 的(1)大,所以不调整。Bob 直接执行,在位置 1 插入 X ,也得到 AXYB 。

不妨简单用 MoonBit 实现一下:

enum Op {  Insert(IntChar)  // Insert(位置, 字符)  Delete(Int)        // Delete(位置)}/// 转换函数:将 op1 转换为在 op2 已经执行后的等效操作fn transform(op1 : Opop2 : Op-> Op {  match (op1op2) {    (Insert(pos1ch), Insert(pos2_)) =>      Insert(if pos1 <= pos2 { pos1 } else { pos1 + 1 }, ch)    (Insert(pos1ch), Delete(pos2)) =>      Insert(if pos1 <= pos2 { pos1 } else { pos1 - 1 }, ch)    // ... 其他 case 类似  }}

2、OT 存在的问题

OT 在工业界有广泛应用(Google Docs 就基于 OT),但它有一些根本性的问题:

1、需要中央服务器 :OT 需要一个权威的服务器来确定操作的全局顺序。没有服务器,就无法确定谁的操作“先”发生。

2、转换规则复杂度爆炸 :如果有 N 种操作类型,就需要定义 N² 个转换规则。当操作类型增多(如富文本的加粗、斜体、链接等),复杂度急剧上升。

3、长分支合并极慢 :如果两个用户离线编辑了很长时间,重新连接时需要转换大量操作,性能很差。

四.RGA:一种经典的序列 CRDT

CRDT(Conflict-free Replicated Data Type)采用了完全不同的思路: 不是在收到操作后转换它,而是设计一种数据结构,使得无论操作以什么顺序应用,结果都相同 。这就像设计一种特殊的“加法”——无论你按什么顺序把数字加起来,结果都一样。数学上,这要求操作满足 交换律 和 结合律 。

1、RGA:一种经典的序列 CRDT

RGA(Replicated Growable Array)是 2011 年提出的一种序列 CRDT,专门用于解决协同文本编辑中的冲突问题。它的核心思想很简单: 用相对位置代替绝对位置 。

还是用 Alice 和 Bob 的例子。初始文档是 AB ,Alice 和 Bob 同时在位置 1 插入字符——Alice 插入 X ,Bob 插入 Y 。

OT 的做法是调整位置坐标。但 RGA 换了个思路: 不用数字位置,用“插在谁后面”来描述插入点 。

具体来说:

  • Alice 的操作不再是“在位置 1 插入 X”,而是“在 A 后面插入 X”

  • Bob 的操作是“在 A 后面插入 Y”

两个操作都想插在 A 后面,怎么办?RGA 给每个字符分配一个 全局唯一的 ID 。这个 ID 由两部分组成:

  • 用户 ID :每个用户有一个唯一标识(如 Alice 是 A ,Bob 是 B )

  • 本地计数器 :每个用户维护一个递增的计数器,每次插入新字符时 +1

所以 A@1 表示“Alice 的第 1 个操作”, B@1 表示“Bob 的第 1 个操作”。当两个字符想插入同一位置时,比较 ID 来决定顺序——先比本地计数器,计数器相同时再比用户 ID(作为 tie-breaker)。这里两个计数器都是 1,所以比较用户 ID: B > A ,因此 B@1 排在 A@1 前面,结果就是 A → Y → X → B ,即 AYXB 。

如果用 MoonBit 实现,我们可以先定义每个节点的类型和它的 compare 规则:

/// 唯一标识符struct ID {  peer : UInt64    // 用户 ID  counter : Int    // 本地计数器derive(Eq)/// 比较两个 ID,用于解决并发插入冲突impl Compare for ID with compare(selfother) {  // 先比较 counter,再比较 peer(打破平局)  if self.counter != other.counter {    other.counter - self.counter  } else if self.peer > other.peer { -1 }   else if self.peer < other.peer { 1 }   else { 0 }}

插入时,在目标位置之后找到正确的插入点——跳过所有 ID 更大的节点:

/// 在 target 之后插入,返回插入位置fn find_insert_pos(order : Array[ID], target_pos : Intnew_id : ID-> Int {  let mut pos = target_pos + 1  while pos < order.length() && new_id.compare(order[pos]) > 0 {    pos = pos + 1  // new_id 更小,继续往后找  }  pos}

我们刚才假设了只有插入的情况,对于删除问题,RGA 采用 墓碑(Tombstone) 策略:删除字符时不真正移除,只标记为“已删除”。

为什么不能真删除?考虑这个场景:Alice 删除了 B,同时 Bob(还没收到删除)在 B 后面插入 X。如果 B 真的没了,Bob 的“在 B 后面插入”就找不到参照物了。墓碑让 B 保留在数据结构中,只是渲染时跳过,这样 Bob 的操作仍然有效。

/// RGA 节点struct RGANode {  id : ID  char : Char  mut deleted : Bool  // 墓碑标记}/// 删除:标记为墓碑fn RGANode::delete(self : RGANode-> Unit {  self.deleted = true}/// 渲染:跳过墓碑fn render(nodes : Array[RGANode]) -> String {  let sb = StringBuilder::new()  for node in nodes {    if !node.deleted { sb.write_char(node.char) }  }  sb.to_string()}

在上面的简单实现当中,为了更加简洁易懂,我们采用的是数组来存储 RGA 的节点。而熟悉数据结构的读者很轻松就可以发现:RGA 会存在频繁的插入情况,因此链表也许更适配这种算法。而实际的工程中则经常使用更加稳健高效的结构如 B+ Tree 或跳表实现它。

2、RGA 的问题

RGA 解决了并发冲突问题,不需要中央服务器,支持 P2P 同步,但它也有显著的缺点:

  • 元数据膨胀 :每个字符都需要存储 ID(工程上很容易达到 16+ 字节)和前驱引用,一篇 10 万字的文档,元数据可能比内容还大。

  • 墓碑累积 :删除的字符永远保留在内存中。一篇编辑多次的文档,可能 90% 的数据都是墓碑,而且文字上可能还有其他维度,比如富文本,会进一步加剧这个缺点造成的影响。

  • 加载缓慢 :从磁盘加载文档时,需要重建整个数据结构,这是 O(n) 甚至 O(n log n) 的操作。

五.Event Graph Walker:更好的方案

1、原理介绍

Event Graph Walker(简称 Eg-walker)是由 Joseph Gentle 和 Martin Kleppmann 在 2024 年提出的新 CRDT 算法。

前面我们看到,OT 操作简单(只有位置索引)但需要中央服务器;CRDT 支持 P2P 但元数据膨胀严重。Eg-walker 的核心洞察是: 两者可以结合,即存储时用简单索引,合并时临时构建 CRDT。

操作像 OT 一样轻量,只记录 insert(pos, char) 和 delete(pos) 。需要合并并发操作时,临时重放历史、构建 CRDT 状态来解决冲突,合并完就丢掉。

可能很多读者会这种“临时构建 CRDT 解决问题的方式”存在一些性能方面的顾虑,我们的确要承认虽然临时构建确实有开销,但是由于大部分时间并不需要 CRDT 参与编辑工作,只有同步并发编辑的时候才需要,而且 Eg-Walker 的性质很明显支持增量构建与局部构建,只需要从快照构建或者再冲突区域构建 CRDT 解决冲突即可。而且可以设想的是,在操作历史越来越复杂的情况下,临时构建会比维护一个会一直增长的结构更加稳健高效。

2、代码实现

1)基础数据结构

首先是操作的定义。与 RGA 使用“在某个 ID 后面插入”的相对定位不同,Eg-walker 直接使用数字位置索引,就像 OT 一样简单:

enum SimpleOp {  Insert(IntString)   // Insert(位置, 内容)  Delete(IntInt)      // Delete(位置, 长度)}

接下来是 事件(Event) 的定义。事件是对操作的包装,添加了因果关系信息:

struct Event {  id : ID               // 唯一标识符  deps : Array[ID]      // 依赖的事件(父节点)  op : SimpleOp         // 实际的操作内容  lamport : Int         // Lamport 时间戳,用于排序}

然后我们就可以根据他们定义出一个事件图(Event Graph):

struct EventGraph {  events : Map[IDEvent]  // 所有已知事件  frontiers : Array[ID]        // 当前最新的事件 ID 集合}

这里定义中的 frontiers 记录了“当前版本”——那些没有被任何其他事件依赖的事件。如果读者熟悉 Git 的一些概念,那么可以把它理解为 Git 中当前所有分支的 HEAD 指针集合。

2)添加事件与维护 Frontier

当收到新事件时,除了将事件存入当前的事件表中,还需要更新 frontier。由于 frontier 记录的是“没有后续事件的事件”,当新事件依赖某个旧 head 时,说明这个旧 head 已经有了后续,不再是“最新的”了,需要从 frontier 中移除,然后把新事件加入 frontier。

fn EventGraph::add_event(self : EventGraphevent : Event-> Unit {  self.events[event.id] = event  self.frontiers = self.heads.retain(frontier => !event.deps.contains(frontier))  self.frontiers.push(event.id)}

3)LCA(最近公共祖先) 与拓扑排序

合并两个版本需要解决两个问题:

1、找到分叉点(在 Event Graph 上找到 LCA ) :确定从哪里开始重放

2、确定重放顺序(根据 Lamport 拓扑排序 ) :按因果关系排序事件

这两部分都属于比较基本的图论问题,相信读者在查阅资料后可以很快的实现出来。不过需要注意的是,在工业实现 Eg-Walker 算法时,我们通常不使用常规介绍的算法求 LCA,而是对数据结构进行改进,应用一些缓存机制来提高效率。

4)合并算法

现在我们有了所有组件,可以实现完整的合并算法了:

fn EventGraph::merge(  self : EventGraph,  local_frontiers : Array[ID],  remote_frontiers : Array[ID],  // 远程 peer 的 frontiers,随事件一起发送  remote_events : Array[Event]-> String {  // 步骤 1:将远程事件添加到事件图  for event in remote_events {    self.add_event(event)  }  // 步骤 2:找到本地版本和远程版本的 LCA(用 VersionVector 取交集)  let lca = self.find_lca(local_frontiersremote_frontiers)  // 步骤 4:收集从 LCA 到两个分支的所有事件  let events_to_replay = self.collect_events_after(lca)  // 步骤 5:按 Lamport 时间戳拓扑排序  let sorted = self.topological_sort(events_to_replay)  // 步骤 6:创建临时 RGA,重放所有事件  let temp_rga = RGA::new()  for event in sorted {    self.apply_to_rga(temp_rgaevent)  }  // 步骤 7:返回最终文本,丢弃临时 RGA  temp_rga.to_string()}

合并流程可以总结为三个阶段:

  • Retreat(回退) :找到 LCA,确定需要重放的事件范围

  • Collect(收集) :收集两个分支上的所有事件,按 Lamport 时间戳拓扑排序

  • Advance(推进) :创建临时 RGA,按顺序重放所有事件,用 CRDT 解决冲突

六.Lomo 与开发一个协作文本编辑器

1、什么是 Loro/Lomo

Loro 是一个基于 Eg-walker 算法的高性能 CRDT 库,由 Rust 实现。它支持多种数据类型(文本、列表、Map、可移动列表、树结构等),提供丰富的协作功能,被用于构建实时协作应用。而 Lomo 是 Loro 的 MoonBit 移植版本,与 Loro Rust 版本保持二进制兼容,这意味着用 lomo 生成的文档可以被 Loro 读取,反之亦然。

Lomo 的核心 API 非常简洁:

let doc = LoroDoc::new()doc.set_peer_id(1UL)// 获取文本容器并编辑let text = doc.get_text("content")doc.text_insert(text0"Hello, World!")doc.text_delete(text52)// 导出更新(用于同步)let updates = doc.export_updates()// 另一个 peer 导入更新let doc2 = LoroDoc::new()doc2.set_peer_id(2UL)doc2.import_updates(updates)  // 两边内容自动同步

2、做一个协同文本编辑器

因为协同需求经常发生在前端,因此 Loro 发行了 Wasm API 以保证前端也可以使用这一优秀的 CRDTs 库。但 Rust 编译的 Wasm 体积偏大,而且难以根据用户某一项单独需求进行 tree-sharking,因此成为很多前端开发者使用 Loro 的痛点。

但前端如果使用 MoonBit+Lomo 在 JavaScript 后端编写,则编译器只会按需编译 API,最终编译结果非常好。同时,MoonBit 的 Wasm 编译结果往往会更小、更干净,就算是使用 Wasm 后端进行发行也会得到很好的效果。

因此我们可以尝试根据 JavaScript 后端制作一个协同文本编辑器来验证这一点,下面展示了大致的实现方式:

首先在 MoonBit 一侧封装文档操作,供 JavaScript 调用:

///| 创建文档pub fn create_doc(peer_id : Int-> Int {  let doc = LoroDoc::new()  doc.set_peer_id(peer_id.to_uint64())  let text = doc.get_text("body")  // 订阅本地更新,自动收集待发送的数据  let _ = doc.subscribe_local_update((bytes=> {    pending_updates.push(bytes)    true  }  // ...}///| 应用编辑操作pub fn apply_edit_utf16(doc_id : Intstart : Intdelete_len : Intinsert_text : String-> Bool {  let doc = docs[doc_id]  let text = texts[doc_id]  if delete_len > 0 { doc.text_delete_utf16(textstartdelete_len)? }  if insert_text.length() > 0 { doc.text_insert_utf16(textstartinsert_text)? }  true}

JavaScript 侧处理用户输入和同步逻辑:

// 处理用户输入function handleInput(sideother) {  const nextText = side.el.textContent;  const change = diffText(side.text, nextText);  // 计算新旧文本的差异  // 应用到 CRDT(调用 MoonBit 导出的函数)  apply_edit_utf16(side.id, change.start, change.deleteCount, change.insertText);  side.text = nextText;  syncFrom(sideother);  // 同步给另一方}// 同步逻辑function syncFrom(fromto) {  const updates = drain_updates(from.id);  // 获取待发送的更新(MoonBit 导出)  if (state.online) {    apply_updates(to.id, updates);  // 在线:立即应用(MoonBit 导出)  } else {    from.outbox.push(...updates);   // 离线:缓存到发件箱  }}

最终经过一些样式编写和页面编写的工作,我们就可以得到一个基于 CRDTs 的协同编辑器:

该项目的源码在文章末尾已经给出,感兴趣的读者可以自行参考并开发更有意思的项目。

七.总结

本文从并发冲突问题出发,介绍了实时协作算法的演进:

  • OT :通过转换操作解决冲突,但需要中央服务器

  • RGA :用唯一 ID 和相对位置实现去中心化,但元数据膨胀

  • Eg-walker :结合两者优点,存储简单操作,合并时临时构建 CRDT

我们用 MoonBit 实现了上述算法的核心数据结构与关键计算部分、还介绍了 Loro/lomo 库和他们的基本使用,并使用 Lomo 开发了一个简单的协作编辑应用。

从 1989 年 OT 的诞生,到 2011 年 RGA 等 CRDT 的形式化,再到 2024 年 Eg-walker 的创新融合,实时协作算法经历了三十余年的演进。而近年来随着 Local-first 理念的兴起,CRDT 正从学术论文走向生产实践——Figma、Linear 背后都有它的身影。

未来,历史压缩、复杂数据结构、端到端加密等方向仍在快速推进;MoonBit 高效编译到 WebAssembly 的能力,也为 CRDTs 在浏览器和边缘设备上的部署提供了新可能。

八.参考项目/文献

Lomo-Demo(编辑器)演示:

https://lampese.github.io/lomo-demo/

Lomo-Demo(编辑器)源码:

https://github.com/Lampese/lomo-demo

Loro:https://loro.dev/

Lomo:https://github.com/Lampese/lomo

Eg-walker 论文:

https://arxiv.org/abs/2409.14252


最新文章

随机文章

基本 文件 流程 错误 SQL 调试
  1. 请求信息 : 2026-02-08 07:17:09 HTTP/2.0 GET : https://f.mffb.com.cn/a/468270.html
  2. 运行时间 : 0.147083s [ 吞吐率:6.80req/s ] 内存消耗:4,728.35kb 文件加载:140
  3. 缓存信息 : 0 reads,0 writes
  4. 会话信息 : SESSION_ID=8fdaed5cf008b660a5f6388e4d6319ed
  1. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/public/index.php ( 0.79 KB )
  2. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/autoload.php ( 0.17 KB )
  3. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/composer/autoload_real.php ( 2.49 KB )
  4. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/composer/platform_check.php ( 0.90 KB )
  5. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/composer/ClassLoader.php ( 14.03 KB )
  6. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/composer/autoload_static.php ( 4.90 KB )
  7. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-helper/src/helper.php ( 8.34 KB )
  8. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-validate/src/helper.php ( 2.19 KB )
  9. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/helper.php ( 1.47 KB )
  10. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/stubs/load_stubs.php ( 0.16 KB )
  11. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Exception.php ( 1.69 KB )
  12. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-container/src/Facade.php ( 2.71 KB )
  13. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/symfony/deprecation-contracts/function.php ( 0.99 KB )
  14. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/symfony/polyfill-mbstring/bootstrap.php ( 8.26 KB )
  15. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/symfony/polyfill-mbstring/bootstrap80.php ( 9.78 KB )
  16. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/symfony/var-dumper/Resources/functions/dump.php ( 1.49 KB )
  17. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-dumper/src/helper.php ( 0.18 KB )
  18. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/symfony/var-dumper/VarDumper.php ( 4.30 KB )
  19. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/App.php ( 15.30 KB )
  20. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-container/src/Container.php ( 15.76 KB )
  21. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/psr/container/src/ContainerInterface.php ( 1.02 KB )
  22. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/app/provider.php ( 0.19 KB )
  23. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Http.php ( 6.04 KB )
  24. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-helper/src/helper/Str.php ( 7.29 KB )
  25. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Env.php ( 4.68 KB )
  26. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/app/common.php ( 0.03 KB )
  27. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/helper.php ( 18.78 KB )
  28. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Config.php ( 5.54 KB )
  29. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/config/app.php ( 0.95 KB )
  30. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/config/cache.php ( 0.78 KB )
  31. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/config/console.php ( 0.23 KB )
  32. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/config/cookie.php ( 0.56 KB )
  33. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/config/database.php ( 2.48 KB )
  34. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/facade/Env.php ( 1.67 KB )
  35. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/config/filesystem.php ( 0.61 KB )
  36. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/config/lang.php ( 0.91 KB )
  37. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/config/log.php ( 1.35 KB )
  38. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/config/middleware.php ( 0.19 KB )
  39. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/config/route.php ( 1.89 KB )
  40. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/config/session.php ( 0.57 KB )
  41. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/config/trace.php ( 0.34 KB )
  42. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/config/view.php ( 0.82 KB )
  43. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/app/event.php ( 0.25 KB )
  44. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Event.php ( 7.67 KB )
  45. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/app/service.php ( 0.13 KB )
  46. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/app/AppService.php ( 0.26 KB )
  47. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Service.php ( 1.64 KB )
  48. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Lang.php ( 7.35 KB )
  49. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/lang/zh-cn.php ( 13.70 KB )
  50. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/initializer/Error.php ( 3.31 KB )
  51. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/initializer/RegisterService.php ( 1.33 KB )
  52. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/services.php ( 0.14 KB )
  53. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/service/PaginatorService.php ( 1.52 KB )
  54. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/service/ValidateService.php ( 0.99 KB )
  55. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/service/ModelService.php ( 2.04 KB )
  56. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-trace/src/Service.php ( 0.77 KB )
  57. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Middleware.php ( 6.72 KB )
  58. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/initializer/BootService.php ( 0.77 KB )
  59. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/Paginator.php ( 11.86 KB )
  60. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-validate/src/Validate.php ( 63.20 KB )
  61. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/Model.php ( 23.55 KB )
  62. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/model/concern/Attribute.php ( 21.05 KB )
  63. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/model/concern/AutoWriteData.php ( 4.21 KB )
  64. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/model/concern/Conversion.php ( 6.44 KB )
  65. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/model/concern/DbConnect.php ( 5.16 KB )
  66. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/model/concern/ModelEvent.php ( 2.33 KB )
  67. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/model/concern/RelationShip.php ( 28.29 KB )
  68. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-helper/src/contract/Arrayable.php ( 0.09 KB )
  69. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-helper/src/contract/Jsonable.php ( 0.13 KB )
  70. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/model/contract/Modelable.php ( 0.09 KB )
  71. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Db.php ( 2.88 KB )
  72. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/DbManager.php ( 8.52 KB )
  73. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Log.php ( 6.28 KB )
  74. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Manager.php ( 3.92 KB )
  75. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/psr/log/src/LoggerTrait.php ( 2.69 KB )
  76. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/psr/log/src/LoggerInterface.php ( 2.71 KB )
  77. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Cache.php ( 4.92 KB )
  78. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/psr/simple-cache/src/CacheInterface.php ( 4.71 KB )
  79. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-helper/src/helper/Arr.php ( 16.63 KB )
  80. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/cache/driver/File.php ( 7.84 KB )
  81. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/cache/Driver.php ( 9.03 KB )
  82. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/contract/CacheHandlerInterface.php ( 1.99 KB )
  83. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/app/Request.php ( 0.09 KB )
  84. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Request.php ( 55.78 KB )
  85. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/app/middleware.php ( 0.25 KB )
  86. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Pipeline.php ( 2.61 KB )
  87. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-trace/src/TraceDebug.php ( 3.40 KB )
  88. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/middleware/SessionInit.php ( 1.94 KB )
  89. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Session.php ( 1.80 KB )
  90. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/session/driver/File.php ( 6.27 KB )
  91. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/contract/SessionHandlerInterface.php ( 0.87 KB )
  92. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/session/Store.php ( 7.12 KB )
  93. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Route.php ( 23.73 KB )
  94. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/route/RuleName.php ( 5.75 KB )
  95. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/route/Domain.php ( 2.53 KB )
  96. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/route/RuleGroup.php ( 22.43 KB )
  97. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/route/Rule.php ( 26.95 KB )
  98. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/route/RuleItem.php ( 9.78 KB )
  99. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/route/app.php ( 1.72 KB )
  100. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/facade/Route.php ( 4.70 KB )
  101. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/route/dispatch/Controller.php ( 4.74 KB )
  102. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/route/Dispatch.php ( 10.44 KB )
  103. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/app/controller/Index.php ( 4.81 KB )
  104. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/app/BaseController.php ( 2.05 KB )
  105. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/facade/Db.php ( 0.93 KB )
  106. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/connector/Mysql.php ( 5.44 KB )
  107. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/PDOConnection.php ( 52.47 KB )
  108. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/Connection.php ( 8.39 KB )
  109. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/ConnectionInterface.php ( 4.57 KB )
  110. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/builder/Mysql.php ( 16.58 KB )
  111. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/Builder.php ( 24.06 KB )
  112. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/BaseBuilder.php ( 27.50 KB )
  113. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/Query.php ( 15.71 KB )
  114. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/BaseQuery.php ( 45.13 KB )
  115. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/concern/TimeFieldQuery.php ( 7.43 KB )
  116. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/concern/AggregateQuery.php ( 3.26 KB )
  117. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/concern/ModelRelationQuery.php ( 20.07 KB )
  118. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/concern/ParamsBind.php ( 3.66 KB )
  119. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/concern/ResultOperation.php ( 7.01 KB )
  120. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/concern/WhereQuery.php ( 19.37 KB )
  121. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/concern/JoinAndViewQuery.php ( 7.11 KB )
  122. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/concern/TableFieldInfo.php ( 2.63 KB )
  123. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/concern/Transaction.php ( 2.77 KB )
  124. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/log/driver/File.php ( 5.96 KB )
  125. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/contract/LogHandlerInterface.php ( 0.86 KB )
  126. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/log/Channel.php ( 3.89 KB )
  127. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/event/LogRecord.php ( 1.02 KB )
  128. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-helper/src/Collection.php ( 16.47 KB )
  129. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/facade/View.php ( 1.70 KB )
  130. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/View.php ( 4.39 KB )
  131. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Response.php ( 8.81 KB )
  132. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/response/View.php ( 3.29 KB )
  133. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Cookie.php ( 6.06 KB )
  134. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-view/src/Think.php ( 8.38 KB )
  135. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/contract/TemplateHandlerInterface.php ( 1.60 KB )
  136. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-template/src/Template.php ( 46.61 KB )
  137. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-template/src/template/driver/File.php ( 2.41 KB )
  138. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-template/src/template/contract/DriverInterface.php ( 0.86 KB )
  139. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/runtime/temp/067d451b9a0c665040f3f1bdd3293d68.php ( 11.98 KB )
  140. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-trace/src/Html.php ( 4.42 KB )
  1. CONNECT:[ UseTime:0.001079s ] mysql:host=127.0.0.1;port=3306;dbname=f_mffb;charset=utf8mb4
  2. SHOW FULL COLUMNS FROM `fenlei` [ RunTime:0.001645s ]
  3. SELECT * FROM `fenlei` WHERE `fid` = 0 [ RunTime:0.000771s ]
  4. SELECT * FROM `fenlei` WHERE `fid` = 63 [ RunTime:0.000673s ]
  5. SHOW FULL COLUMNS FROM `set` [ RunTime:0.001395s ]
  6. SELECT * FROM `set` [ RunTime:0.000602s ]
  7. SHOW FULL COLUMNS FROM `article` [ RunTime:0.001585s ]
  8. SELECT * FROM `article` WHERE `id` = 468270 LIMIT 1 [ RunTime:0.014771s ]
  9. UPDATE `article` SET `lasttime` = 1770506229 WHERE `id` = 468270 [ RunTime:0.021192s ]
  10. SELECT * FROM `fenlei` WHERE `id` = 65 LIMIT 1 [ RunTime:0.000701s ]
  11. SELECT * FROM `article` WHERE `id` < 468270 ORDER BY `id` DESC LIMIT 1 [ RunTime:0.001060s ]
  12. SELECT * FROM `article` WHERE `id` > 468270 ORDER BY `id` ASC LIMIT 1 [ RunTime:0.001152s ]
  13. SELECT * FROM `article` WHERE `id` < 468270 ORDER BY `id` DESC LIMIT 10 [ RunTime:0.001686s ]
  14. SELECT * FROM `article` WHERE `id` < 468270 ORDER BY `id` DESC LIMIT 10,10 [ RunTime:0.002476s ]
  15. SELECT * FROM `article` WHERE `id` < 468270 ORDER BY `id` DESC LIMIT 20,10 [ RunTime:0.002290s ]
0.151126s