Yjs 智能合并 + 四种冲突策略——Obsidian 多设备同步协作的终局方案
2026/9/14 19:50:08 网站建设 项目流程

多设备协作有一个绕不开的技术难题:冲突。

你在手机上改了一段笔记的开头,同时在电脑上改了同一段笔记的结尾。两边各自保存、各自同步。当两个版本在云端相遇时,系统面临一个选择——保留谁的版本?如果保留手机版,电脑上改的结尾丢了。如果保留电脑版,手机上改的开头丢了。如果两个都保留,你得到一个"冲突副本"文件,需要手动把两边的修改合到一起。

这个问题在代码协作领域已经被 Git 解决得很好——git merge能自动合并不同行的修改,同一行的冲突才需要人工介入。但在笔记同步领域,大多数工具的处理方式还停留在"检测到冲突→二选一或手动处理"。甚至很多工具根本不检测冲突——后到的版本直接覆盖先到的版本,先到者的修改静默丢失。

Nutstore Sync 1.4.0版在这个问题上做了两件事:一是引入了基于 Yjs CRDT 算法的无冲突合并引擎,二是提供了从完全自动化到完全手动控制的四种冲突策略。这篇文章会用通俗的方式解释 Yjs 的原理(不需要任何技术背景也能看懂),然后逐一拆解四种策略的适用场景,最后说明为什么这套方案是目前 Obsidian 第三方同步插件里处理多设备协作最完善的选择。

先说明:下面讨论的所有能力都建立在用Nutstore Sync插件给Obsidian同步之上。

插件是通过坚果云账号登录的,想要试用这个插件的,记得先去注册一个坚果云账号:坚果云官网

传统冲突处理的三个局限

在展开 Yjs 的原理之前,先明确传统方案(包括 Remotely Save 等基于文件级比较的同步工具)在处理冲突时的三个结构性局限。

局限一:文件级比较,不是内容级比较。传统方案比较的是文件的修改时间戳或哈希值。如果两台设备都修改了同一个文件——不管改的是同一段落还是不同段落——系统只知道"这个文件有两个版本",但不知道两个版本的差异在哪里。因此它要么让你二选一,要么生成一个冲突副本让你手动合并。

局限二:无法理解文档结构。Markdown 文件本质上是一个纯文本文件。传统方案看到的是两段文本,而不是"第一段被设备 A 修改了,第三段被设备 B 新增了"。它无法理解文档的段落结构、标题层级、列表项边界——因此也就无法做精准的段落级合并。

局限三:冲突处理的粒度过粗。即使某个工具支持"自动选择最新版本",它也是基于文件级别——整个文件用最新版本覆盖。如果你在手机上改了一个文件的第 1 行,在电脑上改了同一个文件的第 100 行,电脑版覆盖手机版的代价是丢失第 1 行的修改。这显然是不合理的。

这三个局限的根源在于:传统方案没有在同步层建立对"文档编辑操作"的理解。它们只看到了两个文件,没看到两个文件分别经历了哪些编辑操作。而 Yjs 的 CRDT 方案,解决的就是这个问题。

Yjs CRDT 通俗解释

CRDT 的全称是 Conflict-free Replicated Data Types,翻译过来是"无冲突复制数据类型"。名字听起来很学术,但核心思想其实很直观。

一个类比:Google Docs 的多人同时编辑

你打开一个 Google Docs 文档,另一个同事也打开同一个文档。你在文档开头写了一段话,同事在文档结尾加了另一段话。你不需要等同事写完再写,同事也不需要等你。你们可以同时编辑,Google Docs 会自动把两个人的修改合并到一起,而且不会产生冲突。

Google Docs 能做到这一点,是因为它记录的不是"文件的最终文本",而是"每个人做了什么编辑操作"——“张三在位置 0 插入了字符 A”、“李四在位置 500 插入了字符 B”。这些操作可以被独立应用到任何副本上,而且最终结果总是一致的,不管操作的到达顺序如何。

Yjs 就是一个实现了类似机制的 JavaScript 库。它在底层把每个编辑操作都表示为一个带唯一标识的数据单元,这些单元可以以任意顺序到达任意设备,最终合并结果是确定的。

Yjs 在 Nutstore Sync 中的工作方式

当你用 Obsidian 编辑一篇笔记时,Nutstore Sync 通过 Yjs 在后台记录你对文档的每一次插入和删除操作,而不是在保存时比较整个文件的差异。这些操作被打包同步到云端,其他设备收到后重放这些操作。

关键点来了:如果两台设备分别改了同一篇笔记的不同位置(比如你改第一段、我改第三段),Yjs 能识别出这些修改是"不相交"的,自动把它们合并到一起,不需要人工介入。

只有当两台设备改了同一段落的同一个位置(比如都修改了同一句话),Yjs 才会标记为冲突——因为它无法判断哪个版本更正确。这时候就需要用到 Nutstore Sync 提供的冲突处理策略了。

Diff3 合并标记

当 Yjs 无法自动合并时(两台设备修改了同一位置),Nutstore Sync 会使用 Diff3 算法生成合并标记。如果你用过 Git 手动解决冲突,对下面这个格式应该不陌生——它会同时展示本地版本的修改和远程版本的修改,并用分隔符标记边界,让你在编辑器中手动选择和编辑最终内容。

Git 用户对这个体验是熟悉的:打开冲突文件,看到<<<<<<<=======>>>>>>>标记,手动编辑后保存,标记为已解决。Nutstore Sync 把同样的体验带到了 Obsidian 笔记冲突中。

AI 辅助冲突判决

除了 Diff3 标记的手动合并,Nutstore Sync 还提供了一个更有意思的能力——AI 辅助判决。

当你面对一个冲突文件、两边的内容都是你亲手写的、你不知道该选哪个版本或者怎么合并时,你可以让 AI 来分析两边的差异。AI 会读取本地版本和云端版本的差异,基于上下文(前后段落、文章主题等)给出合并建议。

这比"纯手动合并"省了一个关键步骤:你不用逐行对比两个版本找差异——AI 已经帮你找到了,你只需要审核 AI 的建议是否合理。

这个功能目前是 Nutstore Sync 独有的。Remotely Save 作为通用同步插件,完全不涉及任何 AI 能力——它的职责边界就是"传输文件",不做内容理解。

四种冲突策略全解析

除了 Yjs 引擎本身的无冲突合并能力,Nutstore Sync 共提供了4种显式的冲突处理策略,你可以根据使用场景选择。

策略选择的决策逻辑

总结一下,选择哪种策略取决于三个因素:

  1. 你是一个人用还是多人协作?单人使用且设备使用时序清晰 → 策略二或策略五/六。多人协作 → 策略一或策略三。
  2. 你的修改在文档中的分布是怎样的?如果修改通常分散在不同段落 → 策略一(Yjs 自动处理大部分情况)。如果经常在同一段落的同一句子上做修改 → 策略三或策略四(人工介入不可避免)。
  3. 你当前的时间紧迫程度?有时间手动合并 → 策略一。赶时间 → 策略四。

与 Remotely Save 的冲突处理对比

为了明确差异,这里做一个直接对比。

Remotely Save 作为通用同步插件,它的冲突处理逻辑相对基础:同步时如果发现本地文件和远程文件都有更新,通常会保留本地版本并生成一个冲突副本文件(文件名带 “conflicted” 标记)。它不做内容级别的合并,不理解文档结构,没有 AI 辅助判决。

用表格总结:

对比维度Nutstore SyncRemotely Save
合并引擎Yjs CRDT(操作级合并)文件级时间戳比较
不同段落修改自动合并,无冲突可能标记为冲突
同一位置冲突处理Diff3 标记 + AI 辅助判决生成冲突副本,手动合并
冲突策略数量6 种,覆盖全部场景1 种默认行为
AI 辅助支持冲突分析和合并建议不支持

这个差距的根本原因在于:Nutstore Sync 是深度绑定坚果云后端的垂直方案,可以在同步引擎层面做深度优化;Remotely Save 是通用适配方案,需要兼容 S3、OneDrive、WebDAV 等多种后端,很难在所有后台上实现一致的内容级合并能力。

Canvas 白板同步与大文件处理

这次更新还有两个和协作相关的辅助能力值得提一下。

Canvas 白板同步:Obsidian 的 Canvas 功能(白板/画布)本质上是一个 JSON 文件。Nutstore Sync 现在支持 Canvas 的增量同步,你在白板上新增一个节点、移动一条连线,不需要重传整个 Canvas 文件。

跳过超大文件:你可以在设置中配置一个文件大小阈值。超过这个阈值的文件在同步时会被自动跳过。这个功能避免了大型附件(比如嵌入的 PDF、图片、视频)在移动网络下意外消耗大量流量。你可以在有 Wi-Fi 时手动取消跳过。

Q&A

Q1: Yjs 合并的结果一定正确吗?

Yjs 保证的是"确定性"——相同的一组编辑操作,无论在哪些设备上以什么顺序到达,最终合并结果都是一致的。但它不保证"语义正确"——如果两台设备在同一位置写了互相矛盾的句子(比如一台写了"方案 A 通过",另一台写了"方案 A 否决"),Yjs 会标记冲突而不是自作主张替你选。最终判断权在你手上。

Q2: 我能在同步过程中切换冲突策略吗?

能。你可以在每次手动同步前临时切换策略。自动同步使用你在设置中配置的默认策略。

Q3: AI 辅助冲突判决需要联网吗?需要额外付费吗?

AI 辅助判决目前需要网络连接(AI 模型在云端运行)。当前版本中这个功能包含在 Nutstore Sync 插件内,不需要额外的 API key 或付费。

Q4: 如果我不想要任何自动合并,只想自己手动处理所有冲突怎么办?

选择Diff3合并策略。所有冲突文件都会被标记为冲突状态,你需要在编辑器中手动处理。这个流程和 Git 的冲突解决体验一致。

Q5: Yjs 的合并操作会增加同步传输量吗?

实际上 Yjs 可能减少传输量。因为它同步的是编辑操作而非完整文件——如果你在一个 5000 字的文档中只改了 20 个字,Yjs 只传输这 20 个字的操作数据,而不是整个文件。这也符合坚果云智能增量传输的设计思路:改一个字只传一个字。

Q6: 多人协作时不同人的冲突策略设置会冲突吗?

每个人在自己的设备上选择的策略只影响该设备的同步行为。如果协作方各自选了不同的策略,云端文件的最终状态取决于各策略的执行顺序和 Yjs 的合并结果。建议团队统一使用策略一(无冲突合并)减少意外。

Q7: 历史版本能不能解决冲突问题?能不能替代冲突策略?

历史版本是冲突发生后的"保险"——如果合并结果不理想,你可以回溯到合并前的版本。但它不能替代冲突策略,因为冲突策略在你合并时就已经决定了结果。两者是互补关系:冲突策略帮你"正确合并",历史版本帮你"失败时回退"。

Q8: Canvas 白板的多人同时编辑会不会产生冲突?

Canvas 文件是 JSON 格式,Nutstore Sync 在底层使用 Yjs 处理 JSON 结构的合并。两个人在白板上分别新增不同的节点、移动不同节点、添加不同类型的连线——这些操作通常能被 Yjs 自动合并。但如果两个人同时移动同一个节点、修改同一个节点的同一个属性,就会触发冲突。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询