MyBase .nyf单文件知识库:长期存储、全文索引与冷备份迁移
简介:MyBase Desktop 6.3 正式版绿色包面向需要本地知识库、资料归档和个人笔记管理的用户,提供解压即可运行的桌面端资料库工具,适合做项目资料整理、文档归类与私密信息保存。压缩包共 259 个文件,约 3.13MB,包含 exe、dll 等程序运行组件,bmp、gif 界面资源,js、html 脚本与帮助页面,以及 ini 配置、nyf 示例库和少量说明文档,目录便于按用途查找。已有 703 人学习下载。版本亮点包括将资料库导出为 CHM 项目、延后加载带来的即时开启体验,以及 SSG-5 单文件存储对大容量数据库的支持,并内置 AES-256 加密与命令行恢复工具,对关注容量、保密性和可恢复性的使用者有参考价值。
1. 一个 .nyf 单文件,能不能长期承载你未来三年的资料量
资料一多,人就会开始找地方存放:云笔记担心断网、担心导出残缺;纯文件夹又搜不动内容,翻旧文档全靠记忆。MyBase Desktop 6.x 在这类需求里被反复提起,它把整棵节点树、富文本正文、附件和全文索引都装进单个 .nyf 文件里,拷走就带走了全部内容。它适合的人是:资料量在几百 MB 到几 GB、以文字和附件为主、要求离线可查、并且愿意自己动手做备份;不适合的人是:指望多人协作、移动端同步,或者想把几十万条数据塞进去还要秒开。授权和安装包走官方渠道,这是把它当长期资产的前提,来源不明的整合包省下的那点事,后面往往要用数据去还。
2. 拆开 .nyf:笔记、附件、索引到底被塞进了哪里
决定要不要用一款本地知识库,第一步不是试用界面,而是搞清楚它的存储模型。MyBase 的核心是一个单文件数据库,所有节点、正文、附件二进制、全文索引都在里面。理解这一点,后面所有的备份、迁移、排错动作才有依据;不理解这一点,你做的每一次“整理”都可能是在给未来的自己挖坑。
2.1 单文件数据库的取舍:为什么不是 SQLite 加文件夹
单文件方案最大的好处是原子性。你把 .nyf 拷到 U 盘、拷到另一台机器、拷进归档盘,节点之间的父子关系、附件与正文的引用关系全部保持原样,不会出现“正文在、附件丢了”的经典事故。相比之下,“SQLite + 附件文件夹”的组合虽然可读性更好,但只要有人手动改过文件夹名、同步工具漏传了一个子目录,库就是残的。
代价也很明确。第一,局部损坏无法局部修复,文件头坏了就是整库打不开,没有“只恢复某几个节点”这种操作。第二,写入期间文件被独占,任何在它正在写的时候做的复制,拿到的都可能是半个文件。第三,杀毒软件和云盘的实时扫描会反复读取这个大文件,库越大,卡顿越明显。第四,云同步的冲突合并逻辑对它基本无效,两端同时改,最后往往只剩一边。
我一般会这样权衡:如果这份资料是“我自己看、我自己写、不要求实时共享”,单文件是加分项;如果这份资料要在两台以上设备之间来回改,那就得靠“主库只放一台机器 + 定期单向同步”的纪律来兜底,而不是指望软件自己解决冲突。
2.2 目录布局与文件头:三分钟判断一个库是不是“活的”
拿到一个库,先别急着双击。用几条命令把它的状态看清楚,比打开界面后才发现打不开要省事得多。便携版常见的布局是程序目录、数据目录、配置目录三者分离,数据目录里那个 .nyf 就是全部。
# 便携版典型目录:程序、数据库、配置分开摆,方便整包搬走 tree -L 2 -a ~/apps/mybase-portable | head -40 # 只看数据库本体:大小、修改时间、inode,判断是不是刚被写过 stat -c '%n size=%s mtime=%y inode=%i' ~/apps/mybase-portable/data/*.nyf # 确认文件类型,以及文件头是不是被截断(大小异常小基本就是坏的) file ~/apps/mybase-portable/data/*.nyf xxd -l 64 ~/apps/mybase-portable/data/*.nyf # 残留的临时文件、锁文件、备份文件,是“双击没反应”的高频原因 find ~/apps/mybase-portable/data -maxdepth 1 \ \( -name '*.tmp' -o -name '*.bak' -o -name '~$*' -o -name '*.lock' \) -lsstat输出的 mtime 是你判断“最后一次正常退出是什么时候”的唯一依据,比软件里的修改时间可信。file和xxd用来排除“文件被网盘同步截断”这类问题——一个几百 MB 的库如果只剩几 KB,那不用再试了,直接去备份里捞。find那一条最实用:异常退出后残留的临时文件会让程序认为库仍被占用,表现就是双击没反应、进程一闪而过;确认主程序已退出后把这些残留清掉,多数情况下就能正常打开。
注意:清残留之前,先把整个数据目录复制一份到别处。清理动作本身不可逆。
2.3 全量索引与增量索引:搜索快慢的真实来源
全文检索是这类工具的核心价值,但它也是最容易被误判的部分。索引存在 .nyf 内部,跟随库一起走,所以换机器之后不需要重建;但索引的更新是增量的,批量导入几千个节点之后,索引可能还停留在导入前的状态,你搜不到新内容,就会误以为是软件不行。
判断方法很直接:导入完成后,手动触发一次索引重建(菜单里通常叫“重建索引”或“压缩/修复数据库”),然后对比重建前后同一个关键词的命中数量。如果重建后命中数明显变多,说明之前一直是增量索引没跟上。这个动作建议在每次大批量导入之后固定做一次,成本是几分钟,收益是后面几个月搜索结果的准确性。
另一个影响速度的因素是索引粒度。节点标题、正文、附件文件名通常都会进索引,但附件正文(比如 PDF 里的文字)一般不进——这是设计上的取舍,不是 bug。如果你的资料以扫描件为主,别指望全文检索能救命,得靠节点标题和标签把入口做厚。
3. 从空节点树到能用:树形大纲、附件归档、全文检索三步落地
建库最容易犯的错是“边写边想结构”。写到第三百个节点时回头看,发现自己有三套并行的分类逻辑,删也不是、留也不是。稳妥的做法是先把骨架定死,再往里填内容,最后才调检索。下面这三步是我每次新建库都会走一遍的顺序,顺序本身比具体参数重要。
3.1 先定树,再写字:三层节点结构的硬约束
三层不是拍脑袋的数字。一层是领域(工作、学习、生活这类互斥的大类),二层是项目或主题,三层是具体条目。超过三层,导航成本指数上升;少于三层,条目会全部堆在二层,一样找不到。硬约束的意思是:任何一个新节点,必须先能回答“我属于哪一层”,回答不了就说明它该进收件箱,而不是直接进主树。
先把目录骨架用脚本建出来,比在界面里一个个点要快,也方便以后重建:
# 建库前的三件套:主库、附件仓、导出仓,物理上分开 mkdir -p ~/kb/{db,assets,export,inbox} # 骨架只建两层,第三层留给实际内容自然生长 for d in 01-工作 02-学习 03-生活 09-归档; do mkdir -p ~/kb/inbox/"$d" done ls -1 ~/kb/inboxdb放 .nyf,assets放待挂载的原始附件,export放导出的 HTML/Markdown,inbox是缓冲带。这个分离的意义在于:备份的时候你只需要盯住db,附件仓和导出仓坏了都能重建,主库坏了才是真事故。01-这类数字前缀不是强迫症,是为了让排序稳定——中文按拼音排序在不同系统上结果不一致,加了前缀就锁死了顺序。
提示:收件箱机制比分类技巧更重要。任何来不及归类的资料先进
inbox,每周固定清一次。没有这个缓冲带,人会因为“不知道放哪”而干脆不记。
3.2 附件不要拖进来就完事:路径策略与命名规则
附件是这类库体积膨胀的主因,也是迁移时最容易断链的部分。两条规则能解决八成问题:文件名只保留 ASCII 字母、数字、中文、点、下划线和短横线;文件名前面加内容哈希前缀,避免同名覆盖。
下面这个脚本在附件入库前生成改名计划,先看计划再执行,比直接批量改名安全:
# normalize_assets.py —— 附件入库前的改名计划生成器 # 用法: python normalize_assets.py ~/kb/inbox > plan.tsv import hashlib import re import sys from pathlib import Path # 只放行中文、字母、数字和少量安全符号,其余一律折叠成短横线 ALLOWED = re.compile(r'[^0-9A-Za-z\u4e00-\u9fa5._-]+') def slug(name: str) -> str: stem, dot, ext = name.rpartition('.') if not dot: # 没有扩展名的文件,整体当 stem stem, ext = name, '' stem = ALLOWED.sub('-', stem).strip('-') or 'unnamed' return f"{stem}.{ext.lower()}" if ext else stem def digest(path: Path, n: int = 8) -> str: h = hashlib.sha1() with path.open('rb') as f: for chunk in iter(lambda: f.read(1 << 20), b''): h.update(chunk) return h.hexdigest()[:n] root = Path(sys.argv[1]) for p in sorted(root.rglob('*')): if not p.is_file(): continue target = f"{digest(p)}_{slug(p.name)}" print(f"{p}\t{target}") # 左右对照,人工再过一遍ALLOWED里的白名单决定了哪些字符会被折叠。空格、括号、全角标点、#、%这些在导出成 HTML 时会被转义成%20、%23,链接一长串还容易断,所以在入库阶段就干掉。digest取内容哈希前 8 位而不是完整哈希,是为了在“唯一性”和“文件名可读性”之间取平衡——8 位十六进制在几万个文件的量级上碰撞概率可以忽略。输出成 TSV 而不是直接改名,是因为总有一些文件名带着业务含义,需要人工看一眼再决定。
附件挂进节点之后,正文里尽量用节点的相对位置描述它,而不是写死绝对路径。绝对路径在换机器、换用户名之后全部失效,而节点引用关系是跟着 .nyf 走的。
3.3 全文检索的可用性调优:命中范围、索引重建、搜索词写法
检索调优的空间其实不大,能动的就三处。第一处是索引范围:确认标题、正文、标签都进了索引,附件文件名是否进索引取决于版本设置,用几个已知存在的附件名去搜一遍就能验证。第二处是重建时机:大批量导入、批量改名、批量移动节点之后,都手动重建一次。第三处是搜索词写法:多数本地库的检索是按词切分的,长句直接贴进去命中率很低,把关键词拆成两三个短词分别搜,再用标签收窄,比一次搜一整句有效得多。
一个实用习惯是给每个二层节点配一个固定的标签前缀,比如项目类统一打p:,资料类统一打r:。这样在搜索结果里可以用标签快速过滤,而不用依赖文件夹层级——层级只有一个维度,标签可以叠很多个。这个习惯在库超过一千个节点之后回本最快。
4. 备份与迁移:别让 .nyf 成为无法恢复的封闭文件
单文件方案最怕的不是软件不好用,是某天文件打不开而你没有第二份。备份这件事在这类工具上有个额外约束:必须冷备,也就是主程序完全退出之后再复制。热备拿到的文件可能处于写入中间态,恢复时表现为“能打开但节点缺一半”,比直接打不开更麻烦。
4.1 冷备份脚本:为什么必须在进程退出后复制
先做个进程检查,再复制到临时目录,最后改名。这三步的顺序不是形式主义:直接复制到目标位置,中途断电或磁盘满会留下半截文件,而半截文件和完整文件在文件名上没区别,下次恢复时你分不清哪个能用。
#!/usr/bin/env bash # kb_backup.sh —— 冷备份:必须在 MyBase 完全退出后执行 set -euo pipefail SRC="$HOME/kb/db" ROOT="$HOME/kb/backup" DST="$ROOT/$(date +%Y%m%d-%H%M)" KEEP=14 # 保留最近 14 份 # 1) 进程检查:库还在被写的时候复制,十有八九拿到半个文件 if pgrep -fi 'mybase' >/dev/null 2>&1; then echo "MyBase 仍在运行,先退出再备份" >&2 exit 1 fi mkdir -p "$DST" # 2) 先落到同盘临时目录,成功后再改名,避免半截文件冒充完整备份 rsync -a --partial-dir=.rsync-partial "$SRC/" "$DST/.staging/" mv "$DST/.staging" "$DST/data" # 3) 哈希清单:回滚时用来证明这份备份是完整的 ( cd "$DST/data" && find . -type f -print0 | sort -z | xargs -0 sha256sum ) \ > "$DST/manifest.sha256" # 4) 只留最近 N 份,目录名带时间戳,排序天然有序 ls -1d "$ROOT"/*/ 2>/dev/null | sort | head -n -"$KEEP" | xargs -r rm -rf echo "ok -> $DST"set -euo pipefail让任何一步失败都立刻中止,而不是带着错误继续往下跑——备份脚本最危险的失败模式是“看起来跑完了,其实什么都没备”。pgrep -fi里的-f匹配完整命令行,因为有些启动方式下进程名并不是mybase。rsync的--partial-dir把断点数据放进独立子目录,不会污染正式备份。KEEP=14按天算就是两周,如果你改库频率高,可以调到 30;磁盘紧张就配合异地归档,别把保留份数压到 3 份以下,因为一旦某次改坏了库,你往往是在几天后才发现,那时候连续几份备份里可能都已经是坏的。
注意:备份目录不要放在同一个正在被云盘实时同步的文件夹里。同步工具会在文件写入过程中抢读,配合大文件很容易产生冲突副本。
4.2 导出策略:HTML、Markdown、RTF 三条路的适用边界
备份解决“文件还在”的问题,导出解决“内容还能被别人读”的问题。这两件事不能互相替代。三格式的取舍大致是这样:
| 格式 | 保留结构 | 保留样式 | 可被外部检索 | 适合场景 |
|---|---|---|---|---|
| HTML | 树形层级可保留 | 较好 | 是 | 长期归档、给别人看 |
| Markdown | 需自行约定 | 一般 | 是 | 进 Git、二次加工 |
| RTF | 弱 | 好 | 一般 | 打印、贴进办公文档 |
我一般以 HTML 为主归档,因为层级关系和附件链接保留得最完整;需要进版本管理或者做全文二次检索时,再从 HTML 转一层 Markdown。RTF 用得最少,只在需要把某几个节点交付给不装这个软件的人时导出。
导出的目录结构建议按“日期 + 用途”命名,比如export/2024-06-archive/。每次导出都是全量快照,不要做增量覆盖——增量覆盖的目录树,你很难判断某个文件到底是哪次导出的,出问题也无从回滚。
4.3 回滚演练:拿一份备份真的打开一次
备份最讽刺的地方在于,很多人是在需要它的时候才发现它没用。所以每隔一两个月,我会挑一份最旧的备份,解到一个临时目录,双击打开,随机点开五个节点,确认正文和附件都在。这个过程五分钟,但能暴露三类问题:备份其实是热备的残file、附件没跟着走、以及某些节点引用了库外路径。
回滚演练时顺便核对manifest.sha256:
# 在解开的备份目录里校验完整性,任何一行 FAILED 都要当回事 cd ~/kb/backup/20240601-0930/data && sha256sum -c ../manifest.sha256 | grep -v ': OK$'如果输出为空,说明这份备份和生成清单时完全一致。如果有 FAILED,先别急着删,很可能是复制过程中磁盘出过错,这份备份本身就不可信,换下一份。
5. 避坑与排查:五个把库搞坏的常见操作
下面这几条都是从“库打不开了”“节点少了一半”“导出的链接全是死的”这类具体故障反推出来的。每条按现象、原因、解决三段写,遇到对应症状直接照着查。
5.1 打开与读写相关的三个坑
第一条,双击没反应,进程一闪而过。现象是任务管理器里进程出现又消失,界面始终不弹。原因九成是上次异常退出留下的锁文件或临时文件,程序认为库仍被占用,于是直接退出。解决方式是先确认没有残留进程,再把数据目录整体复制一份做保险,然后删掉*.tmp、*.lock、~$*这类文件,重新启动。不要在原库上反复试,每试一次都可能让残留状态更复杂。
第二条,能打开,但最近写的内容不见了。现象是打开后停在两三天前的状态。原因通常是这个库被放在了云盘的实时同步目录里,同步工具在文件写入过程中做了一次“冲突副本”处理,主文件被旧版本覆盖。解决办法是把主库移出同步目录,同步改成“备份目录单向上传”,也就是只同步backup/而不是db/。已经发生的覆盖,去同步工具的历史版本里找时间点最近的完整文件。
第三条,库越用越慢,搜索要等好几秒。现象是打开节点、切换标签都有明显延迟。原因一般是索引膨胀加上长期没有压缩,单文件数据库在频繁增删之后会产生内部碎片。解决方式是先做完整冷备份,再执行软件自带的“压缩/修复数据库”,最后重建索引。压缩这一步在大库上可能跑十几分钟,中途别强制结束进程,否则就是自己制造一次损坏。
5.2 导出与迁移相关的两个坑
第四条,导出的 HTML 里附件链接全部失效。现象是页面能打开,点附件提示找不到文件。原因是导出时选了“仅导出节点”而没勾选附件,或者附件原本是以库外绝对路径挂进来的。解决办法是重新导出时勾选包含附件,并且在日常使用中就避免挂库外路径——需要引用的外部文件,先复制进assets/再挂进节点,让所有引用都落在库内。
第五条,换机器之后中文文件名变成乱码。现象是节点标题正常,附件名一片问号。原因是打包环节用了不支持 UTF-8 文件名的压缩工具,或者中途经过了某个会改编码的中转。解决办法是打包时用tar而不是某些图形压缩工具:
# 打包用 tar,保留 UTF-8 文件名和权限位,别用会改编码的图形工具 tar --format=posix -czf kb-$(date +%Y%m%d).tar.gz -C "$HOME" kb/db kb/assets # 解包前先看一眼清单,确认中文名在里面是完整的 tar -tzf kb-20240601.tar.gz | head -20--format=posix会保留更完整的长文件名和编码信息,跨平台解包时最稳。-tzf先列清单再解包,这个习惯能省掉大量“解完了才发现文件名全乱”的时间。
6. 进阶:把导出目录做成可 diff 的归档仓
真正让一份知识库长期可用的,不是它有多好用,而是它退化成的普通文件能不能被 diff、被搜索、被版本管理。我的做法是把 HTML 导出再转一层 Markdown,扔进一个本地 Git 仓库,每次导出提交一次。这样“哪次改动之后某个结论变了”是可查的,比在软件内部翻修改记录可靠。
# html2md_lite.py —— 只处理导出 HTML 里最常见的那几种标签 import re import sys from pathlib import Path def convert(html: str) -> str: html = re.sub(r'(?is)<(script|style).*?</\1>', '', html) # 去掉脚本和样式 html = re.sub(r'(?i)<br\s*/?>', '\n', html) html = re.sub(r'(?i)<h([1-6])[^>]*>(.*?)</h\1>', lambda m: '#' * int(m.group(1)) + ' ' + m.group(2).strip() + '\n', html) html = re.sub(r'(?is)<li[^>]*>(.*?)</li>', lambda m: '- ' + m.group(1).strip() + '\n', html) html = re.sub(r'(?is)<a[^>]+href="([^"]+)"[^>]*>(.*?)</a>', r'[\2](\1)', html) html = re.sub(r'(?i)</p>', '\n\n', html) html = re.sub(r'(?s)<[^>]+>', '', html) # 剥掉剩余标签 for a, b in ((' ', ' '), ('&', '&'), ('<', '<'), ('>', '>')): html = html.replace(a, b) return re.sub(r'\n{3,}', '\n\n', html).strip() src, dst = Path(sys.argv[1]), Path(sys.argv[2]) for f in src.rglob('*.html'): out = dst / f.relative_to(src).with_suffix('.md') out.parent.mkdir(parents=True, exist_ok=True) out.write_text(convert(f.read_text(encoding='utf-8', errors='ignore')), encoding='utf-8')这个脚本只保证“结构不丢、链接不丢”,样式一律不要。所有正则都是非贪婪匹配,避免一条.*把整页吞掉;errors='ignore'是为了让个别编码有问题的旧文件不至于中断整批转换。转完之后git diff --stat,如果某次导出显示几万个文件全变了,多半是导出工具的模板变了,先看一两个文件的 diff 再决定要不要提交。
定期体检我只看三个指标:节点总数、孤儿附件数(在assets/里但没有任何节点引用)、导出链接断裂数。节点总数突然掉一大截,说明上次操作有节点被误删;孤儿附件多了,说明有节点删了但附件没跟着清;链接断裂数超过 1%,说明导出时附件没带全。三个数字都记在同一张表里,趋势比绝对值有用。
提示:这三个指标也可以写成脚本放进备份流程,跑完备份顺手出一份报表,比靠感觉判断可靠。
从那以后,我每次导入大批量资料之前,都会强制走一遍“冷备份 → 改名计划 → 导入 → 重建索引 → 回滚抽检”这五步,哪怕资料只有几十条。多花的那十分钟,是唯一能让我在误删之后不用重新整理三个月的后悔药。希望帮到你。
本文还有配套的精品资源,点击获取