关于思源同步为啥这么慢的探究

本贴最后更新于 300 天前,其中的信息可能已经时异事殊

太长不看:

  1. 扫盘建立快照不够高效,占比 10%
  2. 网络条件拉跨,占比 10-100%(假如你家网速正常就 10%,不正常直接断了,影响算 100% 是没问题吧trollface
  3. 同步有锁,必须交替进行,占比 30%
  4. web 上传对小文件极不友好,占比 50%

关于一些前置的讨论可以看看 Issue #12246 · siyuan-note/siyuan ,我在这里研究了下 obsidian 的 livesync 插件与思源同步机制的差异,试图搞清楚,为啥都能使用 S3 作为同步中介,livesync 可以做到实时同步,而思源则有明显的卡顿感。

一个反直觉的观点是,思源的快照机制并不慢,至少不是同步慢的最主要问题。以下是测试出来的快照建立时间于同步时间之间的差异:

  • 日记编辑 0.75s,未同步
  • 主题更新(22 个文件,2m 数据量)0.86s,同步时间 3s
  • 安装插件(650 个文件,20m 数据量)3.36s,同步时间 94s
  • 删除插件(删除 650 个文件,20mb 数据)0.86s,同步时间 14s

以上都是先建立快照,再进行同步的操作,换而言之,同步的时间已经是去除快照建立的时间了,但是还是可以看出相对于快照操作是很慢的。

因此同步慢的的最大根源就是同步加锁……而已吗?

直到今天之前,我都是这个观点,但我忽然想到,同步加锁只是会影响到另一端同步并下载云端内容的速度,理论上不会影响到我这一端上传的速度,或许还要严格评估一下 web io 对于同步速度的影响。因此我直接在本地搭建了一个 docker minio,新开了一个库作为测试。我同时把我主笔记库的数据复制过来了,总共是 800m 的大小,6500 个文件。

我阅读了 dejavu 的代码,发现它里面有对同时上传文件数的限制。我合理的怀疑这就是问题的根源。于是,我拉到本地,进行修改,将同时上传、下载文件数的限制,也是 NewPoolWithFunc 的 poolSize 全拉高了 10 倍,重新打包思源的 kernel,进行测试。以上所有操作都是基于思源是个开源软件,所有,哎,就是能改着玩!

在我进行修改之前,使用本地的 minio 进行全库上传的耗时是 440s,那么,你觉得我修改后的耗时是多少呢?

还是 436s!

这少掉的 4s,完全可以被认为就是一个误差而已,因此可以说,dejavu 里对同时上传文件大小的限制,根本就不是同步慢的原因。

我尝试绕过思源,直接通过 minio 的 webui 上传,时间相差无几。

接着,我做了另一种尝试,使用 restic 进行备份,然后直接通过 webui 上传。备份同样的内容,思源的同步机制总共生成了 13,062 个文件,259 个文件夹,而 restic 则是 48 个文件,261 个文件夹。

最后,restic 的上传时间为 10s。

在查看日志中发现,minio 的同时上传速度哪怕在本地也只能做到每秒 40 左右的文件。

作为测试,我尝试使用官方的上传机制,这次就更慢了,每秒只能上传 16 个文件。而且甚至因为同时上传等待时间过长,还给我返回了 time out 报错。

因此,问题的根源似乎很清楚了:思源扫盘建立快照当然可以进步,但进步空间不大。改善云端同步协作机制、实现无锁同步,以及改变思源的快照分片机制,才是根本解决方案。

那么,古尔丹,代价是什么呢?

代价就是,从此思源的增量下载能力可能大大减弱,可能你下载一个快照,就得把整个库下载回来,因为现在一个数据分块对应了多个实际的文件,你只能一起下载回来再拆分。

这里,我就不得不感慨,省空间换时间,还是省时间换空间,依然、并且永远,是一个值得考虑的问题啊。

  • 思源笔记

    思源笔记是一款隐私优先的个人知识管理系统,支持完全离线使用,同时也支持端到端加密同步。

    融合块、大纲和双向链接,重构你的思维。

    25901 引用 • 107336 回帖

相关帖子

优质回帖
  • tianzhongs 1 赞同

    目前的优化的话,我能想到的就是类似于 Duplicati(某备份工具),他也是增量备份,他的逻辑是

    1. 第一个文件:把元数据的一些映射关系做了信息映射;
    2. 别的文件就是加密后的元数据,同样也是进行了分片,但是他多了一个步骤,就是

    压缩,我觉得这个压缩很有搞头

    1. 每次增量备份,只多备份新的修改的元数据对照和压缩的元数据

    他这个备份工具也支持加密,也是一样的,我个人体验同步速度算快的,毕竟他压缩的大小能指定,比如说 650MB(CD 大小),元数据足够大的话,就会有很多的 650MB 的压缩包,上传起来比思源现在的上传小文件肯定会快很多;

    然后就是映射关系,他本地也有一个数据库,保存的是映射关系和日志,相当于是缓存,可以删因为云端也有,这种同步的话,除了第一次同步这个日志数据库慢点,其余时间都比思源快很多。

    加密上传后的的目录:能看到精简了很多

    image.png

欢迎来到这里!

我们正在构建一个小众社区,大家在这里相互信任,以平等 • 自由 • 奔放的价值观进行分享交流。最终,希望大家能够找到与自己志同道合的伙伴,共同成长。

注册 关于
请输入回帖内容 ...
  • zxhd86 2 评论

    刚刚用腾讯云测试了下,以下是测试结果:

    I 2024/08/17 00:06:11 s3.go:94: uploaded object [repo/objects/95/4cc61e297a67c163cb1e3ae0f66353c8b2178f]
    I 2024/08/17 00:06:11 sync.go:1265: uploaded chunk [objects/95/4cc61e297a67c163cb1e3ae0f66353c8b2178f, 82/6604]

    I 2024/08/17 00:06:32 s3.go:94: uploaded object [repo/objects/28/551765ccb476243d2d9deae94d52c16fb301c1]
    I 2024/08/17 00:06:32 sync.go:1265: uploaded chunk [objects/28/551765ccb476243d2d9deae94d52c16fb301c1, 521/6604]

    每秒 20 个,还行吧,比七牛稍微好点,但也不多。目前尝试的 s3 对于批量小文件的支持都挺一般的。

    2 回复
    我用 powershell 生成了 1W 个 1Kb 小文件,用 cloudflare 试了下,每秒 100 个左右,没你那么慢..
    sunk926
    @sunk926 想问一下具体的代码可以吗?我测试下这里的速度,但我对 powershell 不熟悉
    zxhd86
  • 其他回帖
  • 我想向你描述的很清楚了,包括你现在又说伺服,我一开头表达的很清楚

    同步目前最简单粗暴的增本增效的方法就是,直接在服务器上给每个订阅用户运行自己的思源内核实例,在使用官方同步的情况下,客户端后端直接与云端内核通信,离线状态下客户端自己记录距离上一次同步的离线时间戳,每当与云端内核连接,就开始对比数据新鲜度,而冲突解调在云端内核进行

    代表我不是不知道伺服可以最快解决,但是你不能作为一个官方订阅,让用户自己伺服吧,所以我直接说了云内核,增本增效。

    但是说实话,就做一个最简单的同步,单纯从打开窗口或者后端 api 请求判断哪个 sy 文件发送了改变,上传了哪些文件,删除了哪些文件,禁止 fs,真正的按需同步,也比现在快,大家当然想要如果修改了 3 个字,就只做同步 3 个字的操作,但现在恐怕连修改了 3 个字就只同步这个文档的操作都没做到

    这个是想表达,储存器性能根本不是原因,是自己没做足够复杂的中间件处理,最为全世界广泛应用的技术,对象存储已经是最好的云端存储方案了,就好像是,能不能让博尔特再快 5s,这样我看他跑步时间就更短了,有没有可能是自己阈值太高?

    然后就是,我也没有把 live 看成是什么标杆,它也是早轮子而已

    说白了,你觉得很简单的东西,像 live 做的监听对象,思源加了没有?最后我说这么多,我觉得最核心的还是这个

    从去年讨论到现在,关于同步已经讨论了无数多个先进方案了。我的评价是,别自嗨,主要看 D 怎么说

    1 回复
  • 然后就是关于你提供的代码定位,我当然可以跟着你看代码,可以看到,光是监听就写了

    plugin.registerEvent(plugin.app.vault.on("modify", this.watchVaultChange)); plugin.registerEvent(plugin.app.vault.on("delete", this.watchVaultDelete)); plugin.registerEvent(plugin.app.vault.on("rename", this.watchVaultRename)); plugin.registerEvent(plugin.app.vault.on("create", this.watchVaultCreate)); //@ts-ignore : Internal API plugin.registerEvent(plugin.app.vault.on("raw", this.watchVaultRawEvents));

    五个,每一个事件不只是一个触发器来触发进行扫盘索引,而是拥有具体的返回对象,例如

    watchVaultChange(file: TAbstractFile, ctx?: any) { this.appendQueue([{ type: "CHANGED", file }], ctx); }

    通过将文件的修改事件封装成一个对象,这个方法能够为后续的处理提供结构化的数据,这至少是一种基本的处理,至少是和操作挂钩了,返回校验也不需要重新牵一发而动全身

    其他的我就不细看了,要休息

    1 回复
  • zxhd86

    我再说的通俗一点,如果你家里宽带有 10000 兆的带宽,那么你下载腾讯 s3 的速度就是一万兆,你个人就算是硬盘提供得了这么大的速度吗?详情见这里,https://doc.fincloud.tencent.cn/tcloud/Storage/COS/845813/uploaddownloadfaq
    对象存储的性能和带宽不上限,只按量,远超你自己局域网运行一个 docker 的性能

    对象储存对于少量大文件的带宽确实能跑满,但是大量小文件就不太行了。这个我已经在腾讯云上尝试过了,参照上面的记录,golang 并发上传数设为 640,但是每秒上传文件数他就是有上限的,腾讯云也只能达到每秒 20 个,目前看来,对于小文件,还是 minio 在本地是比较快。

    如果你测试出更好的数据,可以跟我说一下怎么写,我也可以改进下 siyuan 这方面的代码。

    我说了没有那么好的事情,即自由又稳定,况且,又不是没有解决方案,只需要把附件这种不会频繁变动的内容单独走另一个单独的上传信息通道就好了,而不是放到高频索引里面再扫一遍

    可是实际上外部变动主要就是改附件啊,单纯思源本身的笔记文件反而不太会被改,因为其他软件读不懂……

    至少 ob 可以直接知道该同步哪一个 md 文件,思源可以在操作后马上知道该同步哪一个 sy 文件吗?倘若能,为什么要去扫盘?难道是因为我改了一个 sy 文件,每一次都要扫库看看我有没有改其他文件吗?那为什么不一直扫,万一我改了其他的文件但是没该文档,岂不是没有办法了,只能等我改文档的时候再帮我检测一下?这个思路倒是可以理解了。我是真的觉得,和用户的操作一块进行,监听哪些文件发生更改,然后没有中间服务商直接同步哪些文件,都比现在快。

    我倒是没有否认 ob livesync 建立快照速度更快,更有优越性,只不过 golang 上没有太好可用的包。

    我知道你不停的强调建立一个快照或者索引有多快,但是,在我用的过程中,建立快照并不是卡我的原因,原因是这个快照索引本身就是没有时效性的,不可靠的,所以它不是建立完就没事了,他需要下载下来,然后解析,然后一个个核对,我的同步大部分时间就是花在这个核对索引上,一直在校验索引,少则校验 5-6s,多则半分钟,3.10 版本有时候校验索引都会卡死,扫盘建立的索引,终究还是要扫盘去核对

    此索引非彼索引啊,这里有两个概念的索引,siyuan sqlite 里的索引和快照 dejavu 使用的文档索引。思源懒得判断文档变化情况选择全部校验一遍来重建 sqlite 确实是懒政情况了,但是这跟同步的索引和快照完全是两回事啊。解决问题终究还是要一个个来的。

    局域网内客户端是可以互相沟通同步的,每个客户端都具有与储存器之间的完整的中间件

    我觉得对等点本身就是一个服务端了吧……硬要这么说,思源也可以内部塞一个 webdav 或 s3 实现,然后直接通过局域网发现协议互相找到端口,进行同步。但是这种情况下思源自己就成为服务端了,这跟思源作为伺服的区别有多大呢?

    1 回复
  • 查看全部回帖

推荐标签 标签

  • ZooKeeper

    ZooKeeper 是一个分布式的,开放源码的分布式应用程序协调服务,是 Google 的 Chubby 一个开源的实现,是 Hadoop 和 HBase 的重要组件。它是一个为分布式应用提供一致性服务的软件,提供的功能包括:配置维护、域名服务、分布式同步、组服务等。

    59 引用 • 29 回帖 • 6 关注
  • CloudFoundry

    Cloud Foundry 是 VMware 推出的业界第一个开源 PaaS 云平台,它支持多种框架、语言、运行时环境、云平台及应用服务,使开发人员能够在几秒钟内进行应用程序的部署和扩展,无需担心任何基础架构的问题。

    5 引用 • 18 回帖 • 188 关注
  • Elasticsearch

    Elasticsearch 是一个基于 Lucene 的搜索服务器。它提供了一个分布式多用户能力的全文搜索引擎,基于 RESTful 接口。Elasticsearch 是用 Java 开发的,并作为 Apache 许可条款下的开放源码发布,是当前流行的企业级搜索引擎。设计用于云计算中,能够达到实时搜索,稳定,可靠,快速,安装使用方便。

    117 引用 • 99 回帖 • 199 关注
  • 工具

    子曰:“工欲善其事,必先利其器。”

    298 引用 • 763 回帖
  • 分享

    有什么新发现就分享给大家吧!

    248 引用 • 1794 回帖 • 3 关注
  • Solo

    Solo 是一款小而美的开源博客系统,专为程序员设计。Solo 有着非常活跃的社区,可将文章作为帖子推送到社区,来自社区的回帖将作为博客评论进行联动(具体细节请浏览 B3log 构思 - 分布式社区网络)。

    这是一种全新的网络社区体验,让热爱记录和分享的你不再感到孤单!

    1443 引用 • 10082 回帖 • 496 关注
  • OpenCV
    15 引用 • 36 回帖 • 7 关注
  • RYMCU

    RYMCU 致力于打造一个即严谨又活泼、专业又不失有趣,为数百万人服务的开源嵌入式知识学习交流平台。

    4 引用 • 6 回帖 • 59 关注
  • OpenStack

    OpenStack 是一个云操作系统,通过数据中心可控制大型的计算、存储、网络等资源池。所有的管理通过前端界面管理员就可以完成,同样也可以通过 Web 接口让最终用户部署资源。

    10 引用 • 2 关注
  • Windows

    Microsoft Windows 是美国微软公司研发的一套操作系统,它问世于 1985 年,起初仅仅是 Microsoft-DOS 模拟环境,后续的系统版本由于微软不断的更新升级,不但易用,也慢慢的成为家家户户人们最喜爱的操作系统。

    228 引用 • 476 回帖
  • 智能合约

    智能合约(Smart contract)是一种旨在以信息化方式传播、验证或执行合同的计算机协议。智能合约允许在没有第三方的情况下进行可信交易,这些交易可追踪且不可逆转。智能合约概念于 1994 年由 Nick Szabo 首次提出。

    1 引用 • 11 回帖 • 3 关注
  • Kafka

    Kafka 是一种高吞吐量的分布式发布订阅消息系统,它可以处理消费者规模的网站中的所有动作流数据。 这种动作(网页浏览,搜索和其他用户的行动)是现代系统中许多功能的基础。 这些数据通常是由于吞吐量的要求而通过处理日志和日志聚合来解决。

    36 引用 • 35 回帖
  • 开源中国

    开源中国是目前中国最大的开源技术社区。传播开源的理念,推广开源项目,为 IT 开发者提供了一个发现、使用、并交流开源技术的平台。目前开源中国社区已收录超过两万款开源软件。

    7 引用 • 86 回帖
  • Ant-Design

    Ant Design 是服务于企业级产品的设计体系,基于确定和自然的设计价值观上的模块化解决方案,让设计者和开发者专注于更好的用户体验。

    17 引用 • 23 回帖
  • 创业

    你比 99% 的人都优秀么?

    82 引用 • 1395 回帖 • 1 关注
  • flomo

    flomo 是新一代 「卡片笔记」 ,专注在碎片化时代,促进你的记录,帮你积累更多知识资产。

    6 引用 • 143 回帖 • 2 关注
  • Hibernate

    Hibernate 是一个开放源代码的对象关系映射框架,它对 JDBC 进行了非常轻量级的对象封装,使得 Java 程序员可以随心所欲的使用对象编程思维来操纵数据库。

    39 引用 • 103 回帖 • 727 关注
  • SMTP

    SMTP(Simple Mail Transfer Protocol)即简单邮件传输协议,它是一组用于由源地址到目的地址传送邮件的规则,由它来控制信件的中转方式。SMTP 协议属于 TCP/IP 协议簇,它帮助每台计算机在发送或中转信件时找到下一个目的地。

    4 引用 • 18 回帖 • 632 关注
  • Sphinx

    Sphinx 是一个基于 SQL 的全文检索引擎,可以结合 MySQL、PostgreSQL 做全文搜索,它可以提供比数据库本身更专业的搜索功能,使得应用程序更容易实现专业化的全文检索。

    1 引用 • 222 关注
  • Kotlin

    Kotlin 是一种在 Java 虚拟机上运行的静态类型编程语言,由 JetBrains 设计开发并开源。Kotlin 可以编译成 Java 字节码,也可以编译成 JavaScript,方便在没有 JVM 的设备上运行。在 Google I/O 2017 中,Google 宣布 Kotlin 成为 Android 官方开发语言。

    19 引用 • 33 回帖 • 78 关注
  • sts
    2 引用 • 2 回帖 • 239 关注
  • 小说

    小说是以刻画人物形象为中心,通过完整的故事情节和环境描写来反映社会生活的文学体裁。

    32 引用 • 108 回帖
  • JSON

    JSON (JavaScript Object Notation)是一种轻量级的数据交换格式。易于人类阅读和编写。同时也易于机器解析和生成。

    52 引用 • 190 回帖 • 1 关注
  • Facebook

    Facebook 是一个联系朋友的社交工具。大家可以通过它和朋友、同事、同学以及周围的人保持互动交流,分享无限上传的图片,发布链接和视频,更可以增进对朋友的了解。

    4 引用 • 15 回帖 • 451 关注
  • NetBeans

    NetBeans 是一个始于 1997 年的 Xelfi 计划,本身是捷克布拉格查理大学的数学及物理学院的学生计划。此计划延伸而成立了一家公司进而发展这个商用版本的 NetBeans IDE,直到 1999 年 Sun 买下此公司。Sun 于次年(2000 年)六月将 NetBeans IDE 开源,直到现在 NetBeans 的社群依然持续增长。

    78 引用 • 102 回帖 • 707 关注
  • Dubbo

    Dubbo 是一个分布式服务框架,致力于提供高性能和透明化的 RPC 远程服务调用方案,是 [阿里巴巴] SOA 服务化治理方案的核心框架,每天为 2,000+ 个服务提供 3,000,000,000+ 次访问量支持,并被广泛应用于阿里巴巴集团的各成员站点。

    60 引用 • 82 回帖 • 616 关注
  • TensorFlow

    TensorFlow 是一个采用数据流图(data flow graphs),用于数值计算的开源软件库。节点(Nodes)在图中表示数学操作,图中的线(edges)则表示在节点间相互联系的多维数据数组,即张量(tensor)。

    20 引用 • 19 回帖