关于 chkit
chkit 是面向 ClickHouse 的 TypeScript schema 与迁移工具包,供团队通过命令行管理数据库变更。它涵盖 SQL 生成、实时差异检测和 CI 策略检查。
主要功能
- 01用 TypeScript 定义 schema
- 02生成并预览迁移 SQL
- 03应用并跟踪迁移
- 04检测实时 schema 差异
- 05执行 CI 策略检查
适用场景
- 01管理 ClickHouse schema 变更
- 02应用前审查迁移 SQL
- 03在 CI 中检查 schema 差异
- 04恢复失败的迁移
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ obsessiondb/ chkit --skill chkit安装后在 Claude Code 中输入/chkit确认已加载。
详细介绍
chkit:ClickHouse 架构与迁移工具包
chkit 让你能够使用 TypeScript 定义 ClickHouse 架构、生成迁移 SQL、检测偏差,并通过单个 CLI 运行 CI 检查。
文档:https://chkit.obsessiondb.com
连接与授权
使用用户已提供的选项、目标详情和应用授权;仅询问缺失的信息。除非用户要求使用 Sync 或 Python,否则请专注于 TypeScript 架构管理工作流。
对于直接连接的 ClickHouse 目标,请获取 CLICKHOUSE_URL(HTTP/S 端点和端口)、CLICKHOUSE_USER、CLICKHOUSE_PASSWORD(明确为空也是有效值)和 CLICKHOUSE_DB。切勿将 localhost、默认用户名、空密码或数据库 default 推断为用户已确认的目标。除非用户提出要求,否则不要配置托管实例。
没有凭据时,请安装依赖并离线准备定义。只有在明确选择数据库命名空间后,才能生成迁移 SQL。不要对猜测的端点运行已连接的 pull、query、status、drift、check 或 migrate。如果数据库名称未知,请询问并暂缓生成;不要编造命名空间。如果先前尝试针对占位符生成了 SQL,请在连接前调整定义,并重新生成仅限从未应用过的初始产物。记录已准备的内容以及仍未验证的部分。
将真实凭据保存在进程环境或被忽略的 .env 中,切勿放入架构、迁移或交接文件。Bun 会加载根目录中的 .env;npm/Node 命令需要显式加载,例如在根目录运行 node --env-file=.env ./node_modules/chkit/dist/bin/chkit.js check --strict --json。嵌套配置需要使用加载器,以明确解析根目录中的 .env。
应用前,请验证实际数据库身份并检查生成的 SQL。对于已明确授权的开发数据库操作,不要重复征求批准;对于任务未授权的重大写入操作,请先取得授权。缺少凭据属于必要输入,但不是停止离线工作的理由。
配置
在项目根目录使用 clickhouse.config.ts,或通过 --config 指定自定义路径。架构文件应使用所选的数据库名称;每个受管理对象的 database 都必须指向预期数据库。
import { defineConfig } from '@chkit/core'
function required(name: string): string {
const value = process.env[name]
if (value === undefined || (name !== 'CLICKHOUSE_PASSWORD' && !value.trim())) {
throw new Error(`Missing ${name}; configure the connection first.`)
}
return value
}
export default defineConfig(({ command }) => ({
schema: './src/db/schema/**/*.ts',
outDir: './chkit',
migrationsDir: './chkit/migrations',
metaDir: './chkit/meta', // snapshot.json; applied history is in ClickHouse
plugins: [],
// Preserve target metadata when supplied, including during generation.
clickhouse: !process.env.CLICKHOUSE_URL && (command === 'generate' || command === 'snapshot') ? undefined : {
url: required('CLICKHOUSE_URL'),
username: required('CLICKHOUSE_USER'),
password: required('CLICKHOUSE_PASSWORD'),
database: required('CLICKHOUSE_DB'),
},
check: { failOnPending: true, failOnChecksumMismatch: true, failOnDrift: true },
safety: { allowDestructive: false },
}))
generate 会离线将定义与快照进行差异比较。在线的 _chkit_migrations 表保存已应用的历史记录;本地没有 journal.json。已连接的迁移预览可能会初始化该日志,但不会执行待处理的迁移语句。
架构 DSL
架构文件是 TypeScript 文件,使用 @chkit/core 中的函数导出定义。
表
import { schema, table } from '@chkit/core'
const events = table({
database: 'default',
name: 'events',
columns: [
{ name: 'id', type: 'UInt64' },
{ name: 'org_id', type: 'String' },
{ name: 'source', type: 'LowCardinality(String)' },
{ name: 'payload', type: 'String', nullable: true },
{ name: 'received_at', type: 'DateTime64(3)', default: { expression: 'now64(3)' } },
{ name: 'status', type: 'String', default: 'pending', comment: 'Processing status' },
],
engine: 'MergeTree()',
primaryKey: ['id'],
orderBy: ['org_id', 'received_at', 'id'],
partitionBy: 'toYYYYMM(received_at)',
ttl: 'received_at + INTERVAL 90 DAY',
settings: { index_granularity: 8192 },
indexes: [
{ name: 'idx_source', expression: 'source', type: 'set', maxRows: 0, granularity: 1 },
],
})
export default schema(events)
必填表字段:database、name、columns、engine、primaryKey、orderBy。
可选字段:partitionBy、uniqueKey、ttl、settings、indexes、projections、comment、renamedFrom。
列默认值
- 字符串是字面值,使用单引号:
default: 'pending'→DEFAULT 'pending' - 数字和布尔值是字面值:
default: 0→DEFAULT 0 - SQL 表达式使用
{ expression }:default: { expression: 'now64(3)' }→DEFAULT now64(3)。表达式中的注释会在生成的 SQL 中被删除 - 切勿将函数调用写成普通字符串:
default: 'now64(3)'是字面文本now64(3),ClickHouse 会拒绝将其用于 DateTime64 列(用于 Nullable 列时则会存储为 NULL)。chkit 高于 0.2.0-beta.8 时,如果非字符串列的DEFAULT或EPHEMERAL默认值是这种写法,会在generate阶段拒绝它(column_default_looks_like_expression);对于MATERIALIZED或ALIAS列,则会拒绝所有普通字符串(column_expression_requires_fn) default: 'fn:now64(3)'是旧版等效写法,也是 chkit 0.2.0-beta.8 及更早版本唯一支持的写法:它们会将{ expression }渲染为DEFAULT [object Object]。如果生成的迁移中出现这种情况,请使用fn:defaultKind用于选择子句(DEFAULT、MATERIALIZED、ALIAS、EPHEMERAL);所有类型的default写法都相同:{ name: 'day', type: 'Date', defaultKind: 'MATERIALIZED', default: { expression: 'toDate(ts)' } }→`day` Date MATERIALIZED toDate(ts)。MATERIALIZED和ALIAS必须有default,且写作{ expression }(常量字符串写作{ expression: "'text'" });EPHEMERAL可以省略它
视图
import { view } from '@chkit/core'
const activeUsers = view({
database: 'app',
name: 'active_users',
as: 'SELECT id, email FROM app.users WHERE active = 1',
})
在视图 SQL 中使用完全限定的 db.name 引用(app.users,而非 users):ClickHouse 会根据会话的当前数据库解析未限定的名称。chkit 高于 0.2.0-beta.8 时,还会读取这些引用,以便在视图所使用的视图、物化视图和字典创建后创建该视图。
使用高于 0.2.0-beta.8 的 chkit 时,as 可以跨行并包含 SQL 注释,其他 SQL 字段也可以(物化视图的 as、partitionBy、ttl、索引和投影 SQL、字典的 source/layout/lifetime):chkit 会在将查询写成一行之前删除注释。全文(type: 'text')索引表达式是例外:它们只会删除 -- 和非嵌套的 /* */ 注释。检查生成的迁移:如果单行语句(CREATE VIEW,或 CREATE TABLE 的 TTL 或 PARTITION BY)中仍有字符串字面值之外的 --、// 或 # 注释,说明已安装的 chkit 不会清除注释,而该注释会吞掉语句的其余部分。请升级 chkit,或使用 /* */ 注释。
物化视图
import { materializedView } from '@chkit/core'
const eventCounts = materializedView({
database: 'analytics',
name: 'event_counts_mv',
to: { database: 'analytics', name: 'event_counts' },
as: 'SELECT org_id, count() AS total FROM analytics.events GROUP BY org_id',
})
导出
使用 schema() 对定义进行分组,或逐个导出(任何具有有效 kind 的导出都会被发现):
export default schema(users, events, eventCounts)
// or
export const users = table({ ... })
export const events = table({ ... })
CLI 命令
所有命令都支持 --json,用于输出机器可读内容;也支持 --config <path>,用于指定自定义配置文件。
init:搭建项目脚手架
chkit init
创建 clickhouse.config.ts 和 src/db/schema/example.ts。
generate:创建迁移
chkit generate --name add-users-table
chkit generate --dryrun # Preview without writing
chkit generate --table analytics.events # Scope to specific table
chkit generate --rename-table old.users=new.accounts
chkit generate --rename-column db.table.old=new
将架构定义与上一个快照进行比较。每项操作都会获得一个风险级别:
- safe:
CREATE TABLE、ADD COLUMN - caution:设置更改
- danger:
DROP TABLE、DROP COLUMN
migrate:应用迁移
chkit migrate --json # Preview pending without applying
chkit migrate --apply # Apply all pending
chkit migrate --apply --allow-destructive # Allow danger operations
chkit migrate --apply --table analytics.events
chkit migrate --apply --retry <file> # Resume a failed migration after editing its file
chkit migrate --abandon <file> --apply # Reset a failed migration so the next apply starts it over
应用前会验证校验和。在 CI 中,破坏性操作需要显式指定 --allow-destructive。
迁移若中途失败,会保持进行中状态;使用 --apply 可恢复迁移,并跳过已完成的语句。使用高于 0.2.0-beta.8 的 chkit 时,编辑迁移文件后,如果没有语句被记录为已完成,--apply 会从第 1 条语句重新运行;否则请使用 --retry 或 --abandon,并先通过 --json 预览,但不要指定 --apply。切勿手动编辑 _chkit_migrations 日志表。这些版本还会拒绝应用不含可执行语句的待处理文件,例如尚未填充内容的 generate --empty 占位文件。
status:迁移状态
chkit status
# Output: Migrations: 5 total, 3 applied, 2 pending
需要连接 ClickHouse 才能读取迁移日志。已连接的配置可以在日志不存在时创建日志,但不会应用待处理的迁移语句。
drift:比较实时状态与预期状态
chkit drift
chkit drift --table analytics.events
将快照与实时 ClickHouse 进行比较。报告缺失或多余的对象以及列级差异。
check:CI 门禁
chkit check # Run all policy checks
chkit check --strict # Force all policies on
chkit check --json # Machine-readable output
检查待处理的迁移、校验和不匹配、架构漂移和插件检查。失败时退出代码为 1。
snapshot:在并行分支后修复 snapshot.json
需要使用高于 0.2.0-beta.8 的 chkit 版本。
chkit snapshot rebuild --dryrun # Report the entries that differ from the current snapshot.json
chkit snapshot rebuild # Rewrite chkit/meta/snapshot.json from the schema definitions
当合并或变基两个都运行过 generate 的分支后,snapshot.json 中出现合并冲突标记时,请使用此命令。先解决架构文件冲突。切勿选择冲突的一方,也不要手动编辑文件:这会丢弃另一分支的条目。冲突文件无法进行比较,因此在暂存前请检查重建的文件:运行 git diff HEAD -- chkit/meta/snapshot.json,合并时再与 MERGE_HEAD 对比,变基时则与 REBASE_HEAD 对比。重建会将每个定义记录为已迁移,因此只有在每项架构变更都有迁移文件时才进行重建(每个分支上 chkit generate --dryrun 都报告 0 个操作);升级 chkit 后,请先运行 chkit generate。如果文件已损坏但已提交,且当前没有正在进行的合并或变基,请从 git 恢复快照,而不要重建(git checkout HEAD -- chkit/meta/snapshot.json);放弃失败的迁移后,也请恢复快照,再重新生成。此命令不支持 --table。如果两个分支都更改了同一张表或视图,请添加一项迁移以再次应用该更改:https://chkit.obsessiondb.com/cli/snapshot/
query:对配置的目标运行 SQL
chkit query "SELECT count() FROM users"
chkit query "SELECT count() FROM users" --json
chkit query "SELECT count() FROM users" --service customer-b # ObsessionDB plugin
使用当前启用的执行器:默认使用直接配置的 clickhouse,如果已加载 ObsessionDB 插件服务,则使用该服务。
重命名工作流
如需重命名表或列而不执行删除并重建:
表重命名:在表上设置 renamedFrom:
const accounts = table({
database: 'app',
name: 'accounts', // new name
renamedFrom: { name: 'users' }, // old name
// ... columns, engine, etc.
})
列重命名:在列上设置 renamedFrom:
columns: [
{ name: 'user_email', type: 'String', renamedFrom: 'email' },
]
CLI 标志会覆盖架构元数据:--rename-table old=new、--rename-column db.table.old=new。
插件
在 clickhouse.config.ts 中注册插件:
import { codegen } from '@chkit/plugin-codegen'
export default defineConfig({
plugins: [
codegen({ outFile: './src/generated/chkit-types.ts', emitZod: true }),
],
})
可用插件
| 插件 | 安装 | 命令 | 用途 |
|---|---|---|---|
@chkit/plugin-codegen | bun add -d @chkit/plugin-codegen | chkit codegen | 生成 TypeScript 类型和 Zod 架构 |
@chkit/plugin-pull | bun add -d @chkit/plugin-pull | chkit pull | 将实时 ClickHouse 数据库结构导入架构文件 |
@chkit/plugin-backfill | bun add -d @chkit/plugin-backfill | chkit plugin backfill | 使用检查点重放分区和排序键 |
@chkit/plugin-obsessiondb | bun add -d @chkit/plugin-obsessiondb | chkit query --service <name> | 通过 ObsessionDB 服务路由命令 |
常见工作流
新建项目
bun add -d chkit
bunx chkit init
# Edit src/db/schema/example.ts with your tables
bunx chkit generate --name init
bunx chkit migrate --apply
添加表
- 在
src/db/schema/中创建新的架构文件 - 使用
table()定义表,并通过schema()导出 - 运行
chkit generate --name add-my-table - 运行
chkit migrate --apply
CI 流水线
chkit check --strict --json
# Fails if: pending migrations, checksum mismatches, schema drift, or plugin errors
接入已有数据的数据库
请参阅完整的接入指南。Pull 导入定义,而非已应用的历史记录。请将生成的基线与实时定义进行核对;单靠 CREATE ... IF NOT EXISTS 并不能验证已有对象。使用现有的受限目标时,只从尚未应用的基线中移除多余的数据库配置,然后按常规方式应用;在下一次 ALTER 之前,请验证数据和视图是否保留、日志校验和以及实时漂移。切勿编辑日志或重建快照来伪装接入已完成。
结构性属性与可更改属性
属性发生变化时,chkit 会判断需要使用 ALTER 还是 DROP+CREATE:
- 结构性(需要删除并重新创建):
engine、primaryKey、orderBy、partitionBy、uniqueKey - 可更改(原地 ALTER):列、索引、投影、设置、TTL、注释
视图和物化视图始终使用删除并重新创建的方式。
对于已填充数据的结构性变更,请勿将生成的 DROP/CREATE 用作数据保留方案。请使用分阶段复制/验证/切换方案,并明确协调写入方、制定恢复方案并跟踪 SQL 迁移。
回填插件目前按物理分区和排序键分块;--from/--to 使用元数据选择分区,而不是根据任意时间戳列筛选行。--time-column 和 --chunk-hours 不是当前的规划标志。新计划会获得新的标识;若要重试,请恢复已保存的计划,而不是重新规划。MV 重放适用于历史聚合目标;对同一张表进行复制可能会导致普通 MergeTree 行重复。对于任意源表到影子表的转换,请使用受跟踪的 SQL,并在执行前检查生成的插件 SQL。
文档
请访问 https://chkit.obsessiondb.com 阅读文档。请求页面时使用 Accept: text/markdown,即可获取 Markdown 输出。有关详细信息,请查阅相关指南。
curl -s -H "Accept: text/markdown" <url>
关键页面
| 页面 | URL | 适用场景 |
|---|---|---|
| Schema DSL 参考 | https://chkit.obsessiondb.com/schema/dsl-reference/ | 完整的字段规范、列类型和验证规则 |
| 配置 | https://chkit.obsessiondb.com/configuration/overview/ | 所有配置选项及默认值 |
| Codegen 插件 | https://chkit.obsessiondb.com/plugins/codegen/ | TypeScript 类型、Zod 模式、类型化插入助手 |
| Pull 插件 | https://chkit.obsessiondb.com/plugins/pull/ | 将运行中的 ClickHouse 内省并转换为 schema 文件 |
| Backfill 插件 | https://chkit.obsessiondb.com/plugins/backfill/ | 按分区/排序键重放并设置检查点 |
| CI/CD 集成 | https://chkit.obsessiondb.com/guides/ci-cd/ | 流水线设置、检查命令、部署 |
查找所有页面
获取索引以查找 CLI 命令页面和其他文档:
curl -s -H "Accept: text/markdown" https://chkit.obsessiondb.com/