news 2026/10/16 7:02:06

redb 嵌入式数据库实战:清风输入法 WindInput 的用户词、词频与 Shadow 规则持久化设计

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
redb 嵌入式数据库实战:清风输入法 WindInput 的用户词、词频与 Shadow 规则持久化设计

【免费下载链接】WindInput

清风输入法 | 轻量、快速、可定制的开源中文输入法,专为五笔和码表输入方案设计,支持全拼/双拼(Windows / macOS / Linux)

项目地址:https://gitcode.com/gh_mirrors/wi/WindInput
点击查看免费下载

WindInput(清风输入法)是一款轻量、快速、可定制的开源中文输入法,专为五笔和码表输入方案设计,支持全拼/双拼,覆盖 Windows / macOS / Linux。它的"越用越懂你"背后,是一套基于嵌入式 KV 数据库 redb 的持久化层:用户词、词频统计、Shadow 候选调序规则,全部落在一个.redb单文件里。本文带你完整拆解这套设计的核心思路,无需阅读全部源码也能看懂它是怎么做的。

为什么选 redb?

输入法的学习数据有几个典型特点:

  • 数据量小但写频繁:每次选词都要记录一次"用了哪个词";
  • 需要崩溃安全:写一半断电,词库不能坏;
  • 需要按前缀范围扫描:"打了sh开头的码,要能列出所有sh…的用户词";
  • 单文件可携带:备份、导入导出时直接拷走即可。

用 SQLite 当然也行,但 redb 是 Rust 原生的嵌入式 KV 引擎,零外部依赖、自带事务、key 有序,天然支持 range 扫描,和"词库 = 一组有序 KV"的模型严丝合缝。清风输入法的存储层 wind-store 就是围绕它构建的,是整个系统中最底层、无内部依赖的 crate。

设计演进过程记录在 docs/redesign/store.md,里面详细对比了旧版 Go 实现(bbolt)的问题,例如时间戳单位不一致、记录用 JSON 序列化偏臃肿等,Rust 重写时一并修正。

扁平表 + 复合 key:redb 里没有"嵌套目录"

redb 不支持数据库嵌套(不像 bbolt 有 bucket 树),所以用扁平 table + 复合 key来表达层次。核心设计是:

key 用\0做字段分隔符,把"方案 id"编进 key 前缀,靠 key 的字典序天然实现按方案隔离 + 前缀范围查询。

主要表一览(定义见 store.rs):

表名key 编码存什么
user_words方案\0输入码\0词语用户词(自己造的词)
temp_words同上临时学习词(自动造词)
freq同上词频:{count, last_used}定长 12 字节
shadow方案\0输入码Shadow 调序/删除规则
phrases输入码\0文本全局短语(不分方案)
draft_words方案\0输入码\0词语自动造词草稿层(待验证)
stats_daily/meta日期 / 固定键打字统计 / 版本号、设备 ID

这个编码是范围查询的基础:查某方案下某输入码的所有词,就是一次range前缀扫描;短语刻意不带方案前缀,因为"手机号、住址"这类短语与用五笔还是拼音无关。

用户词:定长二进制比 JSON 香

每条用户词是一条记录,字段包括weight(权重)、count(使用次数)、created_at(创建时间)、boundary(音节边界)等。设计上有几个值得学习的点(user_words.rs):

  • code 和 text 存在 key 里,value 只放定长二进制字段。词库动辄十万条,JSON 序列化的体积和解析开销都是浪费;
  • 字段演进走"惰性升级":记录从 16 字节长到 28 字节,旧记录读出来缺的字段按默认值处理,不需要写迁移脚本;
  • 流式遍历不物化:遍历设置页词库列表时,回调直接借用 redb 页里的字节(UserWordView),只对真正要保留的少数几条做拷贝,避免"翻一页先创建 38 万个 String"。

词的学习规则也很直观:重复添加同码同词取最大权重;每次选中该词count + 1,达到阈值就提升权重。

词频系统:与权重彻底解耦

这是整个设计中含金量最高的决策,权威文档是 docs/redesign/frequency.md:

旧版把"使用频率"算成一个 boost加到词的 weight 上,但词库权重量级巨大(拼音可达千万级),有上限的 boost 加上去几乎不起作用——"用了几十次还是排不上去"。新版彻底推翻:

概念含义作用
权重 weight词库自带默认排序维度
词频 frequency只记真实使用数据{count, last_used}独立的排序维度

词频只在 freq.rs 里存 12 字节(count u32 + last_used i64),排序时才作为独立维度参与,绝不修改 weight。不同方案用不同策略:

  • 码表方案:用过的词置顶(used-first),可选"一次到顶"(MRU)或"逐次提升"(count 累积)两种策略;
  • 拼音方案:按半衰期衰减分参与排序——(base_scale × log2(count+1)) × exp(-ln2 × 时长 / 半衰期),最近用过的词上浮,久未用自然回落,且整句候选恒定豁免不被词频挤下。

一句话总结:码表的"用过"是永久的,拼音的"用过"是会褪色的。

Shadow 规则:候选置顶与删除的"影子层"

打错了想置顶某个候选、或者屏蔽一个永远用不上的候选?这些手动干预就是 Shadow 规则,实现见 shadow.rs:

  • 按"方案 + 输入码"分组,记录pinned(置顶到某位置)和deleted(屏蔽)两类规则;
  • 置顶优先于删除,新规则插队首(LIFO),语义简单可预测;
  • cand_id精准去重是个细节亮点:像"今天日期"这种动态候选,文本每天都不一样,只比文本会导致规则"删不掉、恢复默认恒灰显",所以引入候选稳定 id 作为匹配键。

Shadow 的妙处在于它是排序后的应用点而非数据层——词库本身不动,规则单独存,随时可恢复默认。

工程细节:缓存回收、热替换与版本迁移

redb 用得好不好,差距在细节。wind-store 的 AGENTS.md 把几个"血泪教训"写得很直白:

  1. 唯一安全入口with_db:所有读写必须经这个闭包,它持锁并检查暂停态,避免绕过 pause/resume 语义;
  2. 页缓存是"只涨不落"的高水位:redb 的读缓存实测 19 万词的库全表扫描后会常驻 41 MB 不还。解法是双档回收——批量写完成后立即drop_page_cache(关库重开一次,约 6 ms);全表扫描只做标记,等库空闲 3 秒后由后台线程回收,绝不打扰按键链路。相关测试见 tests/redb_cache_high_water.rs;
  3. pause / resume 释放文件锁:Windows 下原子替换.redb文件前必须解锁文件,这对热更新很关键;
  4. 版本迁移框架:meta表存schema_version,全新库直接打版本号,旧库按版本链逐步升级,当前CURRENT_VERSION = 1(store.rs)。

动手验证

wind-store不依赖任何平台特定代码,任意系统上都能直接跑测试(临时.redb文件建在系统临时目录):

cargo test -p wind-store

配套的架构文档 docs/architecture/ 与 docs/redesign/store.md 记录了从 Go 到 Rust 重写的完整决策链,想深入"为什么这么设计"的同学值得一读。

小结

  • redb 用扁平表 + 方案前缀复合 key优雅表达了"按方案隔离 + 前缀扫描"的词库模型;
  • 定长二进制 value + 惰性字段升级,让十万级词库的小而快成为可能;
  • 词频与权重彻底解耦,是修掉"学了不灵"顽疾的关键;
  • Shadow 作为排序后的影子层,让用户的手动干预可存、可查、可恢复;
  • 缓存回收、文件锁热替换、版本迁移三件套,把嵌入式数据库的生产级坑填平了。

这套设计对任何需要"本地学习数据 + 崩溃安全 + 前缀检索"的桌面应用都有很强的参考价值。

【免费下载链接】WindInput

清风输入法 | 轻量、快速、可定制的开源中文输入法,专为五笔和码表输入方案设计,支持全拼/双拼(Windows / macOS / Linux)

项目地址:https://gitcode.com/gh_mirrors/wi/WindInput
点击查看免费下载

相关推荐

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/10/16 7:01:38

链表刷题第三天:虚拟头节点与双指针反转的实战思路

算法训练营第三天,任务列表里写着链表理论基础、203 移除链表元素、707 设计链表、206 反转链表。说实话,第一天学数组、第二天刷双指针,脑子里画的都是连续格子,今天突然要切换到链表的指针世界,确实有种被拽着转弯的…

作者头像 李华
网站建设 2026/10/16 7:01:30

B站视频下载与音频提取:yt-dlp+ffmpeg完整实践

如果你也跟我一样,遇到过好不容易刷到一条高质量教程,没过几天就被UP主动态删除的情况,或者想在通勤路上把视频内容变成音频反复复习,那你就会明白“BiliBili视频下载”和“提取音频”这两件事到底有多刚需。在线解析站和浏览器插…

作者头像 李华
网站建设 2026/10/16 7:01:03

STM32H743+QSPI驱动ST77903并适配LVGL的完整移植攻略

简介:面向穿戴设备显示方案的ST77903 LCD驱动与LVGL集成Demo,硬件平台为STM32H743,软件栈采用RT-Thread与QSPI接口,重点解决该IC无内置RAM时刷屏数据需连续传输、须由专用线程驱动的难点。资源包13.66MB,共2591个文件&…

作者头像 李华
网站建设 2026/10/16 6:59:47

OptiScaler 上采样替换指南:从安装到调参只要 5 步

OptiScaler 上采样替换指南:从安装到调参只要 5 步 【免费下载链接】OptiScaler OptiScaler bridges upscaling/frame gen across GPUs. Supports DLSS2/XeSS/FSR2 inputs, replaces native upscalers, enables FSR-FG/XeFG on non-FG titles. Supports Nukem mod f…

作者头像 李华
网站建设 2026/10/16 6:59:24

可疑压缩包与WPS云源码下载:安全识别与处理全攻略

先别急着双击那个TCSSOFTDEPTCOPY.zip。我知道你在某个群里看到它的消息,或者是在文档分享链接里瞄到这个名字,心里想的是“WPS文档服务的源代码,搞到就是赚到”。但以我处理过大量来历不明压缩包的经验来说,文件名里带着“softde…

作者头像 李华