SiYuan v3.5.5 版本技术解读:mmap 落盘、未引用数据库清理与 I/O 性能优化全解析
【免费下载链接】siyuanAn open-source, privacy-first, self-hosted knowledge workspace where humans and AI agents work together 开源、隐私优先、自托管的知识工作空间,让人与智能体在此协作项目地址: https://gitcode.com/GitHub_Trending/si/siyuan
本文基于 SiYuan 官方 v3.5.5 版本变更记录(v3.5.5.md),逐条解读该版本的 23 项功能增强、3 项缺陷修复与 1 项底层重构,并结合内核源码剖析其中三个最有技术含量的改动——基于 mmap 的文档写入、树数据缓存以及未引用数据库(AV)清理——的实现机制,帮助你在自托管部署与二次开发中理解 SiYuan 本地存储 I/O 链路的优化思路。
版本总览
v3.5.5 是一个以"细节改进"为主题的版本,官方概述将其概括为 "This version improves some details"(此版本改进了一些细节)。完整变更清单如下,后文按技术主题分组展开:
功能增强(23 项)
- 支持清理未被引用的数据库
- 支持移动端浏览器输入工具栏
- 改进移动端侧边栏面板滑出的检测
- 支持移动端的导出相关设置
- 改进批量将图片粘贴到数据库
- 光标在移动端代码块内时工具栏将显示增加/减少缩进按钮
- 修复移动端块图标消失的问题
- 适配 iOS 上苹果的暗色应用图标
- 改进 Android 与鸿蒙上软键盘工具栏的弹出
- 改进 Ctrl+Shift+T 打开最近关闭文档的功能
- 改进在列表项末尾按 Delete 时的编辑结果
- 改进在提示块中编辑公式块的体验
保存查询条件包含搜索方式- 支持通过 HTTPS 提供网络服务
- 改进输入法兼容性
- 在看板视图中默认创建一个用于分组的选择字段
- 改进 Android 与鸿蒙上标题块的复制
- 写入文档数据通过 mmap 以减少磁盘写入操作
- 改进数据索引以减少磁盘读取操作
- 缓存树数据以提升性能并减少磁盘读取操作
- 改进集市(插件集市)列表加载
- 兼容 HarmonyOS 6.1
- 导出包含大量引用的文档时减少内存使用
缺陷修复(3 项):设置键盘快捷键时出现错误、在提示块中编辑列表块后插入位置不正确、改进 AI 继续写入数据库块的行为。
开发重构(1 项):升级至 Electron v39.5.1。
下面按主题深入解读。
本地存储性能优化:mmap 写入、索引改进与树数据缓存
这是本版本技术含金量最高的部分,包含三条相互关联的优化:文档写入走 mmap(对应 issue #16956)、数据索引减少磁盘读(#16958)、树数据缓存(#16961)。三者共同的目标是压缩"编辑—落盘—索引—读取"这条 I/O 主链路上的磁盘操作次数。
文档写入:从"临时文件 + rename"到内存映射原地覆写
SiYuan 的文档(.sy 文件)由内核中的树写出链路负责持久化。v3.5.5 起,写入路径优先采用内存映射(mmap)原地覆写,其核心实现在 mmap.go 的WriteFileByMmap函数中:
// WriteFileByMmap 使用内存映射将 data 原地覆写到 filePath。 // 流程:OpenFile(O_RDWR|O_CREATE) → Truncate 到精确长度 → mmap.Map(RDWR) → // copy 写入 → Flush → Unmap,全程持有 filelock 的进程内互斥锁,避免并发写冲突。 func WriteFileByMmap(filePath string, data []byte) (err error) { f, err := filelock.OpenFile(filePath, os.O_RDWR|os.O_CREATE, 0644) // ... if err = f.Truncate(int64(len(data))); err != nil { /* ... */ } m, err := mmap.Map(f, mmap.RDWR, 0) // ... copy(m, data) if err = m.Flush(); err != nil { /* ... */ } return }从源码注释可以读出该设计的几个关键取舍:
- 写入流程:以
O_RDWR|O_CREATE打开目标文件 →Truncate到精确长度 →mmap.Map(RDWR)建立读写映射 →copy将数据拷入映射区 →Flush刷盘 →Unmap释放映射; - 并发安全:全程持有
filelock的进程内互斥锁,避免多个协程对同一文档并发写冲突; - I/O 收益:相比传统
filelock.WriteFile(临时文件 + rename + fsync),mmap 路径下的copy是纯内存写,不经过 I/O 子系统,只有最终的Flush会产生极少量计入的写操作; - 失败回退:mmap 出错时由调用方回退到常规文件写入,保证功能不受影响。
调用方在 tree.go 的WriteTree函数中,完整落盘链路如下(约第 346–390 行):
prepareWriteTree用 Lute 引擎将树渲染为 JSON,并校验笔记本内相对路径、补全updated属性;- 加密笔记本先经
encryptData得到密文encData(缓存与比对仍用明文); - 缓存比对:先查
cache.GetTreeDataInBox,命中且与待写数据一致则直接跳过落盘;未命中再读盘(加密笔记本需先解密)比对,一致则回填缓存并跳过; - mmap 写入:
util.WriteFileByMmap(filePath, encData),失败则回退writeTreeByWriteFile(即filelock.WriteFile); - 大文件超过阈值时向用户推送警告,最后写入缓存
cache.SetTreeDataInBox并触发afterWriteTree后处理。
值得强调的是,第 3 步的"缓存未命中再读盘比对、无变更重复写入直接跳过"正是变更日志中"缓存树数据以提升性能并减少磁盘读取操作"(#16961)的落地:缓存实现位于 tree.go 的GetTreeDataInBox/SetTreeDataInBox,以"文档根 ID + 笔记本 ID"为键保存明文树数据,使高频的"打开—无实质改动—保存"场景完全不触盘。
mmap 写入并不只服务于文档:属性视图(数据库)的 JSON 元数据同样走该路径,见 av.go 中util.WriteFileByMmap(avJSONPath, writeData)的调用,即数据库块的结构变更也被纳入同一套低开销落盘机制。
数据索引与磁盘读优化
变更日志中的"改进数据索引以减少磁盘读取操作"(#16958)对应内核的索引队列体系,从源码结构看,索引任务的排队与去重逻辑集中在 index_queue.go 与 queue.go 一带。与树缓存配合后,一次文档保存产生的"写文件 → 解析 → 更新 SQL 索引"链路中,能命中内存缓存的环节将不再重复读盘。
未引用数据库清理:从界面能力到 CLI 命令
"支持清理未被引用的数据库"(#11569)解决的是一个长期存在的维护痛点:文档删除后,其关联的属性视图(AV)JSON 文件可能残留在工作区,成为"孤儿数据库"。
该能力在内核侧由model层提供判定与删除函数(UnusedAttributeViews、RemoveUnusedAttributeView、RemoveUnusedAttributeViews),并暴露为 CLI 子命令,实现在 database.go 中:
var databaseCleanCmd = &cobra.Command{ Use: "clean", Short: "Clean unused databases", RunE: func(cmd *cobra.Command, args []string) error { avID, _ := cmd.Flags().GetString("av") if avID != "" { // 指定单个 AV 清理 model.RemoveUnusedAttributeView(avID) return nil } // 批量清理所有未引用数据库 removed := model.RemoveUnusedAttributeViews() fmt.Printf("%d database(s) cleaned\n", len(removed)) return nil }, }配套还有只读的unused子命令(model.UnusedAttributeViews(true)列出未引用数据库,支持json输出格式),且两个命令均支持--dry-run模式先预览再执行。对于运维自托管实例的开发者而言,"先unused列出、再clean清理、全程可 dry-run"的三段式操作是一个可验证、可回退的安全流程。
网络服务:支持 HTTPS 发布
"支持通过 HTTPS 提供网络服务"(PR #16930)让 SiYuan 的网络服务(Web 端访问入口)可以直接以 TLS 终结方式对外提供服务,适用于反向代理之外的直连场景,例如配合自签名证书或内网可信证书在局域网/家庭服务器中直接以https://访问。网络服务的启动与路由入口位于 serve.go 与 router.go;从源码结构看,证书配置属于服务端启动参数的一部分,具体字段以当前仓库kernel/conf中的网络配置定义为准。
数据库与视图交互增强
本版本对属性视图相关体验做了三处改进:
- 批量粘贴图片到数据库(#15128):在表格视图单元格中批量粘贴多张图片时不再丢失或错位,剪贴板侧的图片解析逻辑见 clipboard.go 一带的实现(不同平台有
clipboard_darwin.go/clipboard_windows.go的构建标签分离); - 看板视图默认分组字段(#16940):新建看板视图时会默认创建一个"选择"类型字段用于列分组。看板布局与分组字段逻辑集中在 layout_kanban.go 与 group.go,从源码结构看,默认字段是在视图初始化阶段注入的,保证用户新建看板即可按列组织行;
- 保存查询条件包含搜索方式(#16927):搜索面板的"保存查询条件"功能此前不保存查询所用方式(如 SQL 查询/普通搜索),本版本起一并持久化,前端搜索模块位于 search/ 目录。
移动端与跨平台体验
移动端(浏览器 + Android + HarmonyOS)是本次改进数量最多的平台,共 12 项相关变更:
- 输入与键盘:支持移动端浏览器输入工具栏(#12789);改进 Android 与鸿蒙上软键盘工具栏的弹出时机(#16548);改进整体输入法兼容性(PR #16937),后者主要解决中文输入组合态(composition)期间内容误判问题;
- 工具栏细节:光标位于移动端代码块内部时,浮动工具栏会显示"增加/减少缩进"按钮(#15381);
- 交互检测:改进移动端侧边面板滑出(滑出屏幕后唤出)的手势检测(#13915);
- 显示修复:修复移动端块图标消失(#16074);适配 iOS 上苹果为应用提供的暗色 APP 图标(#16110);
- 导出设置:移动端设置面板新增导出相关选项(#14757),与桌面端能力对齐;
- 剪贴板:改进 Android 与鸿蒙上标题块的复制(#16946);
- 平台兼容:适配 HarmonyOS 6.1(#16974)。移动端前端实现集中在 mobile/ 目录,鸿蒙端内核接入见 harmony/ 目录。
编辑器行为修复
三处编辑器交互与 AI 相关修复值得说明:
- 列表项末尾 Delete 行为(#16895):修复在列表项末尾按 Delete 键时列表结构合并/删除的结果不符合预期的问题,块编辑核心逻辑位于 editor/ 目录;
- 提示块内列表插入位置(#16916,缺陷修复):在提示块(callout)中编辑列表块后,新插入块的位置计算错误,已修正;同一提示块场景下公式块的编辑体验也在本版本改进(#16917);
- AI 续写数据库块(#16944,缺陷修复):改进 AI 在数据库块内继续写入(续写行/单元格)时的定位与插入行为。
- 快捷键(#16877,缺陷修复):修复在设置中配置键盘快捷键时报错的问题;同时 Ctrl+Shift+T 重新打开最近关闭文档的可靠性得到改进(#16879)。
其他改进与依赖升级
- 集市列表加载(PR #16965):插件/主题集市列表的加载流程优化,后端集市拉取逻辑位于 bazaar/ 目录;
- 导出内存占用(#16982):导出包含大量内部引用的文档时减少内存使用,导出主链路见 export.go,通过降低大文档合并导出时的中间对象驻留来缓解峰值内存;
- Electron 升级:桌面端基础框架升级至 Electron v39.5.1(#16966),依赖版本记录在 package.json 中,属于常规的安全性/稳定性基线跟随。
小结
v3.5.5 虽然是"细节改进"版本,但从内核源码看,其存储链路的变化是实打实的地基工程:WriteFileByMmap+ 树数据缓存 + 索引读优化三者组合,把 SiYuan 的文档写入从"每次全量临时文件写入"变成"先比对、再内存映射覆写"的模式,且通过缓存命中直接省掉读盘比对。对自托管用户而言,本版本可直接收益的功能点包括:HTTPS 网络服务、未引用数据库清理(含 CLIdatabase clean/database unused命令与 dry-run 预览)、看板默认分组字段,以及移动端一揽子输入与工具栏修复。
如需核对本文引用的实现细节,可重点阅读 v3.5.5 官方变更记录(同目录另附 简体中文 与繁体中文版本)以及 mmap 工具实现、树写出链路、CLI 数据库命令 这几个文件。
【免费下载链接】siyuanAn open-source, privacy-first, self-hosted knowledge workspace where humans and AI agents work together 开源、隐私优先、自托管的知识工作空间,让人与智能体在此协作项目地址: https://gitcode.com/GitHub_Trending/si/siyuan
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考