ARTICLE DETAIL

建站实战干货

来自一线的建站与推广经验沉淀,每一条都经过真实交付验证。

Karakeep 数据库迁移实战:基于 Drizzle ORM 的 Schema 演进、迁移生成与 Drizzle Studio 操作指南

2026/9/10 11:09:51 拓冰建站 浏览量
Karakeep 数据库迁移实战:基于 Drizzle ORM 的 Schema 演进、迁移生成与 Drizzle Studio 操作指南 Karakeep 数据库迁移实战基于 Drizzle ORM 的 Schema 演进、迁移生成与 Drizzle Studio 操作指南【免费下载链接】hoarderA self-hostable bookmark-everything app (links, notes and images) with AI-based automatic tagging and full text search项目地址: https://gitcode.com/GitHub_Trending/ho/hoarder本篇技术指南聚焦当前仓库hoarder数据库包名为karakeep/db的数据库迁移体系从 Schema 的唯一事实来源packages/db/schema.ts出发完整讲解如何使用pnpm run db:generate生成迁移、用pnpm run db:migrate应用迁移以及通过 Drizzle Studio 可视化浏览数据库。读完本文你将掌握这套基于 Drizzle ORM SQLite 的数据库版本管理完整工作流能够安全地修改数据模型并在本地开发、Docker Compose 与生产环境中正确推进 Schema 演进。一、迁移体系总览Schema 先行迁移随行Karakeep 的数据库层建立在Drizzle ORM之上采用SQLite通过better-sqlite3驱动作为底层存储。整个数据库迁移体系遵循一条清晰的原则Schema 是唯一事实来源迁移由 Schema 变化自动推导生成。数据库 Schema 定义位于 packages/db/schema.ts所有表结构、索引、外键、枚举约束都以 TypeScript 代码形式声明在此文件中每次修改 Schema 后必须生成一个对应的迁移migration文件迁移文件被组织在 packages/db/drizzle 目录中与 Drizzle Kit 的 journal 元数据配合构成可回放、可追踪的版本历史。从源码结构看见 packages/db/package.jsonkarakeep/db包直接依赖drizzle-kit^0.31.10、drizzle-orm^0.45.2与better-sqlite3^13.0.3这四者分别承担迁移工具链、运行时 ORM 与 SQLite 原生驱动三个角色。二、Schema 定义迁移的起点所有数据库表的定义都集中在 packages/db/schema.ts该文件约 1337 行。它使用 Drizzle 的sqliteTable声明表结构并大量使用relations、index、foreignKey、unique等辅助 API。以首个迁移中的核心表为例Schema 中典型的表定义包含主键如id: text(id).notNull().primaryKey().$defaultFn(() createId())主键使用paralleldrive/cuid2生成随机 ID而非自增整数时间戳通过createdAtField()、modifiedAtField()等辅助函数统一生成createdAt/modifiedAt字段并借助$defaultFn与$onUpdate自动填充枚举约束如role: text(role, { enum: [admin, user] })、tagStyle的七种取值枚举由数据库层约束非法值布尔与 JSON布尔列以integer(..., { mode: boolean })表示部分配置字段如curatedTagIds以{ mode: json }存储。从源码结构看Schema 覆盖了用户、账号OAuth 适配、API Key、书签bookmarks及bookmarkLinks/bookmarkTexts/bookmarkAssets等子表、标签、列表、高亮、备份、Webhook、RSS Feed、导入会话等 Karakeep 的全部业务域。因此任何功能新增或字段变更都应首先修改这里再进入迁移流程。三、生成迁移db:generate修改完 Schema 后在仓库根目录执行pnpm run db:generate --name description_of_schema_change其中--name后应填写对该次 Schema 变更的简短英文描述它会被用作迁移文件的命名后缀。这个命令实际经过了 pnpm workspace 的转发链见根目录 package.jsonpnpm run db:generate └─ pnpm --filter karakeep/db run generate └─ drizzle-kit generate也就是说它最终调用的是karakeep/db包内定义的generate脚本drizzle-kit generate见 packages/db/package.json。底层配置drizzle.config.tsdrizzle-kit generate的行为由 packages/db/drizzle.config.ts 决定export default { dialect: sqlite, schema: ./schema.ts, out: ./drizzle, dbCredentials: { url: databaseURL, }, } satisfies Config;dialect: sqlite声明 SQLite 方言生成 SQL 时按 SQLite 语法输出schema: ./schema.tsSchema 来源文件out: ./drizzle迁移文件输出目录dbCredentials.url指向实际数据库文件其路径由serverConfig.dataDir推导——当设置了DATA_DIR环境变量时数据库文件位于${DATA_DIR}/db.db否则回退到仓库根目录的./db.db。生成产物执行生成后drizzle-kit会对比 Schema 与上一个迁移快照的差异并在 packages/db/drizzle 目录下产生两类文件迁移 SQL 文件形如00XX_snake_case_name.sql记录实际的 DDL 语句例如0000_luxuriant_johnny_blaze.sql中CREATE TABLE语句、0025_aspiring_skaar.sql中的ALTER TABLE bookmarks ADD type与数据回填语句快照文件drizzle/meta/00XX_snapshot.json记录该迁移应用后的完整 Schema 结构供下次生成时做差异对比。截止当前仓库状态packages/db/drizzle 目录下已积累94 个迁移 SQL 文件从0000_luxuriant_johnny_blaze.sql到0093_reader_view_assessment.sql最新迁移示例为0093_reader_view_assessment.sql。迁移的时序与顺序由 packages/db/drizzle/meta/_journal.json 记录该 journal 以entries数组按idx递增登记每个迁移的tag文件名与生成时间是 Drizzle 判断下一个待应用迁移的依据。四、应用迁移db:migrate生成迁移后需要在目标数据库上应用它。在仓库根目录执行pnpm run db:migrate同样经过转发链pnpm run db:migrate └─ pnpm --filter karakeep/db run migrate └─ tsx migrate.ts与generate不同migrate脚本直接通过tsx运行 packages/db/migrate.tsif (serverConfig.degradedMode) { console.log(Skipping database migrations in degraded mode); } else { migrate(db, { migrationsFolder: ./drizzle }); }这里有两个值得注意的实现细节degraded mode 会跳过迁移当环境变量DEGRADED_MODEtrue时配置定义见 packages/shared/config.ts迁移被显式跳过数据库以只读方式打开适合演示环境或故障排查场景迁移目录固定为./drizzledrizzle-orm/better-sqlite3/migrator会按_journal.json中的顺序逐条执行尚未应用的迁移文件。migrate使用的数据库连接由 packages/db/drizzle.ts 建立const sqlite openSqliteDatabase(dbConfig.dbCredentials.url, { readOnly: serverConfig.degradedMode, walMode: serverConfig.database.walMode, });其中serverConfig.database.walMode由环境变量DB_WAL_MODE默认false控制。数据库连接的底层行为在 packages/db/sqlite.ts 的openSqliteDatabase中连接建立时会设置一组 pragmaPRAGMA取值作用journal_modeWAL当DB_WAL_MODEtrue或DELETE默认控制 SQLite 日志模式WAL 模式更适合并发读写synchronousNORMAL仅 WAL 模式降低 WAL 模式下的同步开销cache_size-65536将页面缓存上限设为约 64 MBforeign_keysON强制启用外键约束temp_storeMEMORY临时表与排序存于内存query_onlyON仅只读模式只读模式下禁止一切写操作本地开发中的首次迁移在开发环境首次初始化时需要先准备环境变量至少设置DATA_DIR见 docs/docs/08-development/01-setup.md再执行pnpm run db:migrate创建数据库文件与全部表结构。由于数据库文件路径由DATA_DIR推导${DATA_DIR}/db.db确保 web 应用、workers 与 db 包共享同一个DATA_DIR即可让所有进程访问同一份数据。五、Drizzle Studio可视化浏览与调试数据库在仓库根目录执行pnpm run db:studio该命令经pnpm --filter karakeep/db studio转发到drizzle-kit studio见 packages/db/package.json。Drizzle Studio 会启动一个本地 Web 界面允许你浏览所有表的结构与行数据直接查看drizzle.config.ts中配置的out目录对应的迁移与快照在开发与调试阶段快速核对 Schema 变更是否符合预期。Studio 同样读取 packages/db/drizzle.config.ts因此它会连接到DATA_DIR指向的真实数据库文件与 web 应用使用的是同一份数据所见即所得。六、完整迁移工作流与常见问题标准操作流程修改 packages/db/schema.ts新增或调整表、字段、索引、约束在仓库根目录运行pnpm run db:generate --name 变更描述检查生成的 SQL 与快照是否符合预期运行pnpm run db:migrate将新迁移应用到本地数据库可选运行pnpm run db:studio可视化核对数据提交 Schema、迁移 SQL 文件与drizzle/meta/下的快照、journal 变更。与 Docker Compose 开发环境的集成在 Docker Compose 开发模式下见 docker/docker-compose.dev.yml迁移被内置进prep服务该服务会先创建DATA_DIR、执行pnpm install --frozen-lockfile然后运行pnpm run db:migrate确保 web 与 workers 服务启动前数据库已是最新结构。这意味着开发容器每次重启都会自动应用尚未执行的迁移与本地手动执行db:migrate的效果一致。常见问题为什么改了 Schema 后应用报错因为 Schema 类型与数据库实际结构不一致。请先db:generate再db:migrate两者缺一不可——前者产出迁移文件后者将变更落到数据库DEGRADED_MODEtrue时迁移被跳过此时数据库以只读模式打开fileMustExist: true无法写入任何新表属于刻意设计的只读降级行为迁移文件冲突怎么办迁移顺序由_journal.json决定多人协作时应避免手动修改已提交的迁移文件新增变更应生成新的迁移如何查看已应用的迁移可通过 Drizzle Studio 或直接查看drizzle/meta/_journal.json的entries列表也可在 SQLite 中查询 Drizzle 自动维护的迁移记录表。七、总结Karakeephoarder的数据库迁移体系以Schema 即真相为核心Schema 定义在 packages/db/schema.tspnpm run db:generate负责将 Schema 变更转化为带编号的迁移 SQL 与快照pnpm run db:migrate按 packages/db/drizzle/meta/_journal.json 记录的时序应用到${DATA_DIR}/db.db而pnpm run db:studio则提供可视化的检查手段。理解这套改 Schema → 生成 → 应用 → 校验的闭环是在这个 monorepo 中安全演进数据模型的必备技能。【免费下载链接】hoarderA self-hostable bookmark-everything app (links, notes and images) with AI-based automatic tagging and full text search项目地址: https://gitcode.com/GitHub_Trending/ho/hoarder创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考