资讯动态

Cherry Studio 数据库迁移机制解析:基于 Drizzle ORM 的 SQLite 只追加演进实践

发布时间:2026/9/12 3:00:16 来源:尧图企业网站定制
Cherry Studio 数据库迁移机制解析基于 Drizzle ORM 的 SQLite 只追加演进实践【免费下载链接】cherry-studioAI productivity studio with smart chat, autonomous agents, and 300 assistants. Unified access to frontier LLMs项目地址: https://gitcode.com/GitHub_Trending/ch/cherry-studio本文聚焦 Cherry Studio 的数据库迁移体系它如何用better-sqlite3 Drizzle ORM 管理本地 SQLite 数据为何坚持只追加append-only的迁移策略以及贡献者应如何在表结构变更时安全地生成、应用与审查迁移。读完本文你将掌握migrations/目录的完整运作原理、db:migrations:generate等命令的正确用法以及运行时迁移链路的底层实现细节。一、技术栈与数据架构概览Cherry Studio 是一个本地优先的 AI 生产力应用其用户数据对话、助手、Agent、知识库、模型配置等持久化在本地 SQLite 数据库中核心数据库文件位于用户数据目录下的Data/cherrystudio.sqlite参见 restore 相关测试 中对app.database.file的引用。根据 migrations/README.md 的明确说明这套持久化方案的技术选型为驱动层better-sqlite3作为 SQLite 3 驱动同步 API、性能好适合 Electron 主进程场景ORM 与迁移工具drizzle既负责运行时查询drizzle-orm也负责迁移生成与执行drizzle-kit migrator。表结构Schema不是散落各处的 SQL而是集中定义在 src/main/data/db/schemas 目录下。该目录中每个文件对应一类业务实体例如agent.ts、agentChannel.ts、agentSession.ts、message.ts、topic.ts、userModel.ts、userProvider.ts、file.ts、knowledge.ts、preference.ts等共 30 余个 schema 文件并配有_columnHelpers.ts等共享列定义辅助工具。Schema 是唯一的事实来源——一切迁移都从这些 TypeScript 定义生成因此贡献者修改表结构时应先改这里而不是手写 SQL。二、迁移目录结构一张完整的演进地图迁移产物全部落在 migrations/sqlite-drizzle 目录其结构如下migrations/ ├── README.md # 迁移纪律与使用说明本文主题文档 ├── sqlite-drizzle.config.ts # drizzle-kit 配置 └── sqlite-drizzle/ # 自动生成的迁移数据禁止手改 ├── 0000_orange_jasper_sitwell.sql # 初始合并迁移 ├── 0001_tan_cerise.sql ├── ... ├── 0020_wooden_fat_cobra.sql # 当前最新迁移 └── meta/ ├── _journal.json # 迁移链日志 └── 0000_snapshot.json ... 0020_snapshot.json # 每步 schema 快照各组成部分的职责*.sql迁移文件drizzle-kit 根据 schema 差异自动生成的增量 SQL按NNNN_随机名.sql命名并严格编号meta/_journal.json迁移链的账本按idx顺序记录每个迁移的 tag、version 与生成时间戳whendrizzle 的运行时迁移器依赖它判断当前数据库已应用到哪一步meta/NNNN_snapshot.json生成对应迁移时点的完整 schema 快照drizzle-kit 靠它对比出下一步差异也用于审计某次迁移前后表结构的确切形态。配置入口 migrations/sqlite-drizzle.config.ts 内容如下import { defineConfig } from drizzle-kit export default defineConfig({ out: ./migrations/sqlite-drizzle, // 迁移产物输出目录 // 递归扫描 排除 *.test.ts避免 drizzle-kit 加载到依赖 vitest 的文件 schema: ./src/main/data/db/schemas/**/!(*.test).ts, dialect: sqlite, // SQLite 方言 casing: snake_case // 列名自动转换为蛇形命名 })值得注意的两点schema采用 glob 递归匹配整个 schemas 目录新增 schema 文件无需改动配置同时通过!(*.test).ts排除测试文件防止 drizzle-kit 在加载 schema 时意外拉起 vitest 依赖casing: snake_case则让字段在数据库层统一为蛇形命名风格。三、只追加Append-only原则迁移链的生命周期纪律migrations/README.md 开篇就划定了两条不可逾越的红线THIS DIRECTORY IS NOT FOR RUNTIME USE本目录不用于运行时Migration files are append-only迁移文件只允许追加这两条纪律的背景是迁移链曾被合并为单一初始迁移并随v2.0.0-rc.1版本发布。也就是说从该版本起迁移会真实运行在持有用户数据的数据库上任何对已发布迁移的重写都可能破坏现有用户的数据。由此推导出的三条铁律绝不重新初始化、重写或重编号已发布的迁移——已发布的迁移是历史事实改动它等于篡改历史绝不建议删除Data/cherrystudio.sqlite——这是用户全部本地数据对话、配置、知识库索引的载体删库是数据灾难而非修复手段表结构变更必须以追加新迁移的方式落地——新变更永远生成0021_xxx.sql这样的新文件而不是回头修改0000。这一只追加模式与 git 历史类似迁移链是一条只能前进、不能回退的单向时间线_journal.json就是这条时间线的索引。从 meta/_journal.json 可以看到当前链共含 21 个条目idx020其中0000_orange_jasper_sitwell即合并后的初始迁移00010020均为后续追加的演进步骤每条都记录了version: 6与生成时间戳。四、表结构变更的标准流程从 Schema 到迁移文件当需要修改表结构新增表、加列、改索引等时标准流程是修改 schema 定义编辑 src/main/data/db/schemas 下对应的*.ts文件新增实体则新建文件无需改配置glob 会自动纳入生成迁移在仓库根目录执行迁移生成命令审查产物确认新生成的NNNN_*.sql与meta/NNNN_snapshot.json符合预期提交将迁移文件与 schema 变更一并提交迁移文件是 schema 变更的提交记录。生成命令定义在 package.json 的 scripts 中# 生成迁移当前仓库使用 pnpm 管理依赖亦可使用文档记载的 yarn 写法 pnpm run db:migrations:generate # yarn run db:migrations:generate # 等价写法 # 校验迁移链一致性推荐在生成/提交前运行 pnpm run db:migrations:check其中db:migrations:generate展开为drizzle-kit generate --config ./migrations/sqlite-drizzle.config.ts它会对比当前 schema 与最近一次快照将差异编译为增量 SQL 输出到migrations/sqlite-drizzledb:migrations:check展开为drizzle-kit check --config ./migrations/sqlite-drizzle.config.ts用于校验迁移链与快照的一致性。由于 SQLite 无法原地修改表约束或列类型drizzle-kit 在遇到这类变更时会将其编译为建新表 → 拷贝数据 → 删旧表 → 重命名的表重建流程这一点在 applyMigrations.ts 的注释中有详细说明因此生成的 SQL 往往比直觉上更重审查时需格外关注数据是否被正确迁移。五、运行时迁移执行applyMigrations 的完整链路迁移不仅在开发期生成也会在应用每次启动时被消费。Cherry Studio 将运行时迁移封装在 src/main/data/db/applyMigrations.ts 的applyMigrations(db, migrationsFolder)中这是全仓库唯一被允许调用 drizzlemigrate()的入口文件头有专门的 eslint 限制注释。它同时服务于三条路径DbService.onInit正式数据库的启动迁移测试 harness每次测试用的临时数据库备份恢复管线将work.sqlite前滚迁移。该函数做了三件普通调用migrate()不会做的事1. 在迁移事务之外切换外键约束。SQLite 不能原地修改表约束因此约束/列类型变更会被 drizzle-kit 编译成表重建CREATE __new_x → INSERT SELECT → DROP x → RENAME迁移器本会以自己的PRAGMA foreign_keysOFF守护这个过程——但 drizzle-orm 的 migrator 把每条语句包进一个事务而 SQLite 明确规定PRAGMA foreign_keys在事务内部是 no-op于是这个守护实际从不生效DROP旧表时会连带触发子表的外键级联动作、静默删掉关联行。applyMigrations的解法是在 migrator 事务之外手动执行const enforced isForeignKeysEnforced(db) db.run(sql.raw(PRAGMA foreign_keys OFF)) try { migrate(db, { migrationsFolder }) } finally { db.run(sql.raw(PRAGMA foreign_keys ${enforced ? ON : OFF})) }先记录迁移前的外键开关状态关掉后执行迁移再在finally中恢复原状避免破坏调用方的既有约定。2. 迁移后执行外键完整性检查。若迁移前外键是开启的迁移结束后会执行PRAGMA foreign_key_check一旦发现悬空引用说明迁移本身引入了断链立即通过 logger 记录错误日志。设计上它不会阻塞启动——因为该函数同时是恢复管线和测试路径硬失败对用户是不可恢复的——但绝不放过无声无息。3. 补跑 Drizzle 管不了的自定义 SQL。迁移完成后会顺序执行 customSqls.ts 导出的CUSTOM_SQL_STATEMENTS数组详见下一节。六、Drizzle 管不了的 SQLFTS5 全文索引与触发器Drizzle ORM 的表结构跟踪能力并不覆盖 SQLite 的全部特性。customSqls.ts 明确列出了三类它无法管理的对象虚拟表Virtual tables如 FTS5 全文索引触发器Triggers带表达式的自定义索引Custom indexes with expressions。这些 SQL 被集中定义后通过applyMigrations()在每次启动即每次迁移执行后补跑export const CUSTOM_SQL_STATEMENTS: string[] [ ...MESSAGE_FTS_STATEMENTS, // 来自 schemas/message.ts ...AGENT_SESSION_MESSAGE_FTS_STATEMENTS // 来自 schemas/agentSessionMessage.ts ]这里最关键的设计约束是幂等性idempotent由于每次启动都会执行所有语句必须可以安全地重复执行——虚拟表使用CREATE ... IF NOT EXISTS触发器则采用DROP TRIGGER IF EXISTSCREATE的组合这样即使开发者修改了触发器逻辑也能在存量数据库上生效。FTS5 全文索引用于消息内容的本地全文搜索是 Cherry Studio 全局搜索能力的数据基础相关设计取舍可进一步参考 docs/references/data/database-construction.md。七、迁移文件实例分析从建表到数据回填初始迁移0000_orange_jasper_sitwell.sql820 行。作为合并后的单一初始迁移它一次性定义了整个应用的初始 schema。例如agent表CREATE TABLE agent ( id text PRIMARY KEY NOT NULL, type text NOT NULL, name text NOT NULL, description text DEFAULT NOT NULL, instructions text NOT NULL, model text, plan_model text, small_model text, disabled_tools text DEFAULT [] NOT NULL, configuration text DEFAULT {} NOT NULL, order_key text NOT NULL, created_at integer NOT NULL, updated_at integer NOT NULL, deleted_at integer, FOREIGN KEY (model) REFERENCES user_model(id) ON UPDATE no action ON DELETE set null, FOREIGN KEY (plan_model) REFERENCES user_model(id) ON UPDATE no action ON DELETE set null, FOREIGN KEY (small_model) REFERENCES user_model(id) ON UPDATE no action ON DELETE set null );这段 SQL 体现了 Cherry Studio 数据模型的几个典型约定主键用textUUID/ID 字符串而非自增整数JSON 字段如disabled_tools、configuration以text存储并带默认值[]/{}时间统一用integerUnix 毫秒时间戳软删除采用deleted_at可空列而非物理删除。同文件还定义了agent_channel带typeCHECK 约束与permission_modeCHECK 约束、agent_channel_task复合主键 级联外键等表以及配套索引如agent_name_idx。最新迁移0020_wooden_fat_cobra.sql数据回填范式。这个例子展示了迁移不止能改结构还能顺手完成存量数据的回填ALTER TABLE job ADD cancel_requested_at integer;-- statement-breakpoint -- Backfill pre-column rows: updated_at ≈ request time (the cancel tx was the last write); -- terminal rows cap at finished_at because post-terminal no-op cancels bump updated_at. UPDATE job SET cancel_requested_at CASE WHEN finished_at IS NOT NULL AND updated_at finished_at THEN finished_at ELSE updated_at END WHERE cancel_requested 1 AND cancel_requested_at IS NULL;先ALTER TABLE ADD COLUMN新增可空列再用一条带业务注释的UPDATE根据既有列finished_at、updated_at、cancel_requested推断并回填历史行——-- statement-breakpoint是 drizzle-kit 的语句分隔符标记。这种加列 数据回填的组合是追加迁移中最常见的实战形态写回填 SQL 时务必如本例一样先写清业务前提注释再用WHERE精确限定目标行保证可重复执行且不误伤数据。八、给贡献者的实践清单综合以上机制为 Cherry Studio 贡献表结构变更时应遵守以下清单先改 Schema再生成迁移编辑 src/main/data/db/schemas 下的*.ts文件然后执行pnpm run db:migrations:generate或yarn run db:migrations:generate绝不手改迁移产物migrations/sqlite-drizzle 是自动生成的手改会导致与快照、journal 不一致db:migrations:check会报警绝不触碰已发布迁移只追加新编号0021_起不重写、不重编号、不删除历史.sql绝不建议删库Data/cherrystudio.sqlite是用户数据本体任何 schema 问题都应通过追加迁移解决善用 check 与测试生成后运行pnpm run db:migrations:check校验链路运行时迁移路径由 applyMigrations.ts 统一承载相关数据库测试与备份恢复流程可参考 docs/references/data/database-construction.md 与 restore 目录若新增 FTS5/触发器/表达式索引按 customSqls.ts 的约定在对应 schema 文件中定义语句并导入CUSTOM_SQL_STATEMENTS保证幂等记得添加约束类变更时重点审查SQLite 会把它编译为表重建注意外键与数据保全运行时由applyMigrations的外键开关保护但生成阶段仍需人工确认回填正确。这套体系的核心思想可以概括为一句话数据库演进是单向不可逆的时间线schema 是唯一事实来源迁移文件是只追加的账本运行时代码负责在正确的外键与幂等前提下把账本安全地落到用户的磁盘上。理解这四条你就能安全地参与 Cherry Studio 的数据层开发。关联参考migrations/README.md纪律总纲sqlite-drizzle.config.tsdrizzle-kit 配置applyMigrations.ts运行时迁移入口customSqls.ts自定义 SQLschemas表结构定义meta/_journal.json迁移链账本【免费下载链接】cherry-studioAI productivity studio with smart chat, autonomous agents, and 300 assistants. Unified access to frontier LLMs项目地址: https://gitcode.com/GitHub_Trending/ch/cherry-studio创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价