乐于分享
好东西不私藏

OpenCode 源码-Database 与存储层——SQLite + Drizzle 持久化架构

OpenCode 源码-Database 与存储层——SQLite + Drizzle 持久化架构

OpenCode 源码解析

第 14 讲:Database 与存储层——SQLite + Drizzle 持久化架构

基于 dev 分支源码 · 2026-07-21

一、存储层:OpenCode 的持久化引擎

前十三讲覆盖了 OpenCode 从 CLI 到 TUI 的核心能力。这一讲进入Database 与存储层——OpenCode 基于 SQLite + Drizzle ORM 构建的持久化架构。数据库模块横跨 packages/core/src/database/13 个 SQL Schema 文件,支撑 Session、Event、Account、Permission、Share 等所有核心数据。

📦 本讲核心文件

packages/core/src/database/database.ts(57 行)— 数据库服务

packages/core/src/database/sqlite.ts(8 行)— SQLite 抽象层

packages/core/src/session/sql.ts(176 行)— Session Schema

packages/core/src/account/sql.ts— Account Schema

packages/core/src/permission/sql.ts— Permission Schema

二、Database Service 架构

database.ts(57 行)实现了 OpenCode 的数据库服务,基于 Effect + Drizzle ORM + SQLite 构建。

1. 数据库服务接口

📄 database/database.ts (第 16-37 行)

export interface Interface {
  db: DatabaseShape
}

export class Service extends Context.Service<
  Service, Interface
>("@opencode/v2/storage/Database") {}

const layer = Layer.effect(
  Service,
  Effect.gen(function* () {
    const db = yield* makeDatabase

    // SQLite 优化配置
    yield* db.run("PRAGMA journal_mode = WAL")
    yield* db.run("PRAGMA synchronous = NORMAL")
    yield* db.run("PRAGMA busy_timeout = 5000")
    yield* db.run("PRAGMA cache_size = -64000")
    yield* db.run("PRAGMA foreign_keys = ON")
    yield* db.run("PRAGMA wal_checkpoint(PASSIVE)")
    
    // 自动应用数据库迁移
    yield* DatabaseMigration.apply(db)

    return { db }
  }).pipe(Effect.orDie),
)

设计亮点:数据库服务在初始化时配置了 6 个 SQLite PRAGMA 优化参数,包括 WAL 模式、同步级别、忙超时、缓存大小、外键约束和被动检查点。迁移自动应用,确保 Schema 一致性。

2. SQLite 优化配置

PRAGMA 作用
journal_mode WAL 预写日志模式,读写不阻塞
synchronous NORMAL 平衡安全与性能
busy_timeout 5000ms 忙等待超时 5 秒
cache_size -64000 64MB 缓存
foreign_keys ON 启用外键约束
wal_checkpoint PASSIVE 被动检查点

三、Drizzle ORM Schema 设计

OpenCode 使用 Drizzle ORM 定义所有数据库 Schema,遵循 snake_case 命名规范。

1. Session Schema

📄 session/sql.ts (第 1-60 行)

import { sqliteTable, text, integer, real }
  from "drizzle-orm/sqlite-core"

export const SessionTable = sqliteTable(
  "session", {
    id: text().primaryKey(),
    directory: text().notNull(),
    title: text().notNull(),
    model: text().notNull(),
    provider: text().notNull(),
    time_created: integer().notNull(),
    time_updated: integer().notNull(),
    time_used: real().notNull(),
    // ... 更多字段
  }
)

export const SessionInputTable = sqliteTable(
  "session_input", {
    id: text().primaryKey(),
    session_id: text().notNull(),
    content: text().notNull(),
    time_created: integer().notNull(),
  }
)

export const SessionMessageTable = sqliteTable(
  "session_message", {
    id: text().primaryKey(),
    session_id: text().notNull(),
    role: text().notNull(),
    content: text().notNull(),
    time_created: integer().notNull(),
  }
)

Schema 设计:Session 表存储会话元数据,SessionInput 表存储用户输入,SessionMessage 表存储消息。所有表使用 snake_case 命名,时间字段使用整数存储。

四、数据库迁移系统

OpenCode 使用自动迁移系统,确保数据库 Schema 与代码版本一致。

1. 迁移流程

📄 database/database.ts (第 33 行)

// 自动应用数据库迁移
yield* DatabaseMigration.apply(db)

迁移流程:数据库服务初始化时自动调用 DatabaseMigration.apply(db),检查并应用所有待执行的迁移。迁移文件存储在 database/migration/ 目录。

五、总结

🔹 SQLite + Drizzle ORM + Effect 三层架构

🔹 6 个 PRAGMA 优化参数:WAL、NORMAL、5000ms、64MB、外键、被动检查点

🔹 Session/Input/Message 三表设计

🔹 自动迁移系统确保 Schema 一致性

🔹 snake_case 命名规范

🔹 13 个 SQL Schema 文件覆盖 Session、Event、Account、Permission、Share 等

← 系列导航 →

← 第 13 讲:TUI 终端界面 | 第 15 讲:Env 与运行时标志 →

关注公众号获取更多 OpenCode 源码解析干货

源码:https://github.com/opencode-ai/opencode