关于 clickhouse-migrations
用于编写 PostHog ClickHouse 迁移的仓库技能,指导开发者选择集群、保持 Schema 一致,并遵循安全规则与测试流程。
主要功能
- 01按集群选择节点角色
- 02ClickHouse 引擎与迁移模式
- 03Schema 变更安全规则
- 04本地 Schema 与 HCL 一致性
- 05迁移测试指引
适用场景
- 01向 ClickHouse 添加表或视图
- 02为新数据对象选择集群
- 03准备仅包含迁移的 Pull Request
MCP 相关
该指南要求使用 ClickHouse MCP 检查 dev、prod-us 和 prod-eu 的实时集群,再确定对象的部署位置。
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ posthog/ posthog --skill clickhouse-migrations安装后在 Claude Code 中输入/clickhouse-migrations确认已加载。
详细介绍
ClickHouse 迁移
阅读 posthog/clickhouse/migrations/AGENTS.md,了解全面的模式、集群设置、示例以及摄入层细节。
快速参考
迁移结构
operations = [
run_sql_with_exceptions(
SQL_FUNCTION(),
node_roles=[...],
sharded=False, # True for sharded tables
is_alter_on_replicated_table=False # True for ALTER on replicated tables
),
]
节点角色(根据拥有该对象的集群来选择)
只有 posthog/clickhouse/client/connection.py 中 NodeRole 的成员是有效的:ALL、DATA、
INGESTION_EVENTS、INGESTION_SMALL、INGESTION_MEDIUM、ENDPOINTS、LOGS、APM、AI_EVENTS、
AUX、BATCH_EXPORTS、OPS、SESSIONS。不在该枚举中的名称会在导入时报错,从而中止
迁移发现,并连带拖垮所有运行迁移的任务。
根据哪个集群拥有该对象来选择角色,而不要仅依据其类型:
[NodeRole.DATA]:属于主集群的对象——分片表、位于分片表或卫星表之前的Distributed读 表、视图、字典。位于DATA上的非分片复制表会在每个主集群节点上保留一份完整副本,因此它不适合 承载产品汇总或预聚合(见下文)。[NodeRole.INGESTION_SMALL]:可写表、Kafka 表、摄入层上的物化视图[NodeRole.APM]:ingestion-apm节点,即指标摄入层。Kafka 表、Null输入 表、物化视图,以及写入LOGS上存储的可写Distributed表。 与其他摄入节点一样是无状态的,因此存储表绝不能放在这里。[NodeRole.OPS]、[NodeRole.LOGS]、[NodeRole.AUX]、[NodeRole.AI_EVENTS]、[NodeRole.SESSIONS]、[NodeRole.BATCH_EXPORTS]:位于卫星集群上的对象。把服务于上述某一角色的表放在DATA上会落到错误的节点,并使原本预期的集群缺少该表,因此在默认选择DATA之前,请先确认 该对象在哪里被读取和写入。Dev 运行与 US/EU 生产相同的卫星集群——绝不要根据CLOUD_DEPLOYMENT分支 来给 dev 一个不同的布局。[NodeRole.ALL]:很少使用
预聚合和汇总存储在卫星集群上,并通过 DATA 上的 Distributed 表读取
(如需在存储卫星上临时读取,也可在那里读取),除非应用已经直接连接到该卫星。
对卫星自身数据的汇总保留在该
卫星上;通用的产品预聚合则放到 AUX。用下面的实时检查确认卫星,
并用 hclexp locate 查找当前示例的 HCL 层。
在选择角色之前检查实时集群
较旧的迁移、HCL 的 local 层以及共享辅助函数可能落后于 dev 和生产中实际运行的内容。在编写迁移之前,请对照实时集群确认放置位置:
- 使用 ClickHouse MCP 检查 dev、prod-us 和 prod-eu 中的每一个:可比较的表位于何处,
主集群通过哪些
Distributed表读取哪些卫星表,以及新表 名称未被占用。有关设置和用法,请参阅 Housekeeper MCP runbook。 - 当先例相互矛盾时,遵循该表族最新的迁移,并确认它与 所有三个环境中运行的内容一致。只有部分环境仍在运行的布局属于迁移 中途状态,不是可以照搬的模式。
- 在 PR 描述中说明每个新对象落到哪个集群,并指出该选择所依据的参考迁移 或实时检查。
表引擎快速参考
MergeTree 引擎:
AggregatingMergeTree(table, replication_scheme=ReplicationScheme.SHARDED)用于分片表ReplacingMergeTree(table, replication_scheme=ReplicationScheme.REPLICATED)用于非分片表- 其他变体:
CollapsingMergeTree、ReplacingMergeTreeDeleted
Distributed 引擎:
- 分片:
Distributed(data_table="sharded_events", sharding_key="sipHash64(person_id)") - 非分片:
Distributed(data_table="my_table", cluster=settings.CLICKHOUSE_SINGLE_SHARD_CLUSTER)
Kafka 引擎 —— kafka_engine() 仅返回 ENGINE 子句,因此每个表都要自行追加
SETTINGS。从这个基线开始:
SETTINGS kafka_skip_broken_messages = 100,
kafka_num_consumers = {kafka_num_consumers(1)},
kafka_thread_per_consumer = 1,
kafka_poll_timeout_ms = 10000,
kafka_max_block_size = 100000
kafka_skip_broken_messages默认为 0,此时一条格式错误的消息就会永久停止消费者。kafka_poll_timeout_ms对于 WarpStream 为 10000,因为 WarpStream 不支持fetch.min.bytes。kafka_max_block_size默认约为一百万行,因此 100000 是缩减;对于宽行应调低, 但不要低于约 1000。kafka_num_consumers是每个节点的,因此该角色的节点数会将其倍增;总数不得 超过主题的分区数。- 列和设置在创建时即固定:删除并连同其 MV 一起重建表,不要用
SYNC。ALTER TABLE <mv> MODIFY QUERY可覆盖仅修改 MV-SELECT 的情况。切勿用于kafka_events_json_ws/events_json_ws_mv—— 参见关键规则。
posthog/clickhouse/migrations/AGENTS.md 中有容量规划规则及其余内容。
关键规则
- 绝不在 SQL 语句中使用
ON CLUSTER子句 - 始终使用
IF EXISTS/IF NOT EXISTS子句 - 在同一迁移中删除并重建复制表时,使用
DROP TABLE IF EXISTS ... SYNC - 如果生成 SQL 的函数有 on_cluster 参数,始终设置
on_cluster=False - 修改分片表时使用
sharded=True - 修改非分片复制表时使用
is_alter_on_replicated_table=True - 绝不在列上写
CODEC(ZSTD(1))—— 服务端已用 ZSTD 压缩每一列,因此这毫无收益。只有在能胜过该默认值的地方才声明 CODEC,并先检查ORDER BY:Delta/DoubleDelta需要该列在存储顺序中接近有序(排序键的前缀),而在仅被键分桶或未包含在键中的列上则会失效。T64/Gorilla与排序无关。只将它放在存储表上 —— 在 Distributed 或 Kafka 表上的 CODEC 是无效元数据,会与其前置的分片表产生偏差。 - 绝不要自行编写
DROP COLUMN迁移 ——DROP COLUMN可能会在 ClickHouse 中卡住并阻塞发布。删除列是一个两步流程:(1) ClickHouse 团队直接在集群上删除该列,然后 (2) 你编写带有匹配DROP COLUMN的迁移,使代码库 schema 保持同步。在 ClickHouse 团队未先完成第 1 步的情况下,绝不要从迁移中发起删除。 - 绝不要删除或重建
kafka_events_json_ws或events_json_ws_mv—— 这些表是禁区。MV 定义在美国生产、欧洲生产和开发环境之间差异很大(数十个特定于环境的mat_*列),而这些差异并未反映在仓库中。根据仓库 SQL 删除并重建会破坏特定于环境的 schema 并中断事件摄取。任何更改都必须经过 ClickHouse 团队。
PR 范围
包含 ClickHouse 迁移的 PR 必须仅包含迁移。不要在同一 PR 中将迁移文件与功能代码、API 变更、模型变更或前端变更混在一起。与迁移相关的文件包括:
- 迁移文件本身(
posthog/clickhouse/migrations/0NNN_*.py) - 迁移所依赖的 SQL 定义文件(例如
posthog/clickhouse/sql/*.py、表引擎辅助文件) - 直接测试该迁移或其所涉及的 SQL 定义的测试
如果你同时需要 schema 变更和使用新 schema 的应用代码,请先以单独的 PR 发布迁移,并在应用代码 PR 之前合并它。
本地设置一致性
不应存在只存在于云端的表。 通过迁移创建的每一张表,也都必须存在于本地开发环境中。
有些迁移受云端保护,会在本地/hobby 开发环境中被跳过。判定条件应基于 posthog.run_mode,而不要直接基于 settings.CLOUD_DEPLOYMENT,因为 clickhouse-migrations-use-run-mode semgrep 规则会阻止原始比较:
from posthog.run_mode import RunMode, run_mode
operations = (
[]
if not run_mode().is_deployed_cloud # US/EU/DEV
else [...]
)
| 判定条件 | 为 true 的情况 |
|---|---|
run_mode().is_deployed_cloud | US、EU、DEV(通常的迁移判定条件) |
run_mode().is_prod_cloud | US、EU(不包括 staging) |
run_mode() is RunMode.CLOUD_US | 单个区域(CLOUD_EU、CLOUD_DEV 同理) |
run_mode().is_cloud 也会把 E2E 计算在内,因此不适用于迁移判定。该项匹配的是 posthog.cloud_utils.is_cloud。
在需要时调用 run_mode(),而不要将其赋值给模块级常量。posthog/clickhouse/test/test_migrations.py 会在打补丁后的 posthog.settings.CLOUD_DEPLOYMENT 下重新导入每个迁移,以检查每个部署分支中是否存在误加的 ON CLUSTER,而缓存的值会静默跳过这一覆盖检查。
如果你在这样的保护分支内创建新表,还必须将其 SQL 函数添加到 posthog/clickhouse/schema.py 中相应的元组,以便该表在本地被创建:
| 表类型 | schema.py 中的元组 |
|---|---|
| MergeTree / 基础表 | CREATE_MERGETREE_TABLE_QUERIES |
| Distributed / 可写入 | CREATE_DISTRIBUTED_TABLE_QUERIES |
| Kafka 消费者 | CREATE_KAFKA_TABLE_QUERIES |
| 物化视图 | CREATE_MV_TABLE_QUERIES |
| 非物化视图 | CREATE_VIEW_QUERIES |
| 字典 | CREATE_DICTIONARY_QUERIES |
唯一的例外是那些定义在不同环境中有意不同、且未在仓库中跟踪的表(例如禁区中的 events_json_ws_mv 表)。
字典凭据: 当字典使用 SOURCE(CLICKHOUSE(...)) 时,应通过 get_clickhouse_creds(ClickHouseUser.DICT_READER) 解析源用户名/密码,并将其插入到 USER/PASSWORD 子句中——不要硬编码 default/CLICKHOUSE_USER,也不要省略凭据。这样可以保持字典认证使用专用的低权限 dict_reader 用户,与 default 解耦;当环境变量未设置时,它会回退到 default 凭据。该模式可参见 posthog/models/exchange_rate/sql.py。
声明式 schema(HCL)必须一致
有些角色也在 posthog/clickhouse/hcl/ 中声明,而多节点迁移冒烟测试会重新内省
已迁移的集群,并在实时 schema 与已提交的 golden 之间存在任何漂移时失败。因此,在受管
角色上由迁移添加的表,需要在同一个 PR 中更新 HCL,否则 CI 会在
check_live_hcl 处失败,并给出一个 DRIFT: 块,列出你的新对象。
manifest.hcl 中带有 local-multi 块的每个角色都是受管的,这覆盖主集群的 data
角色以及大多数卫星和摄取角色。请在该文件中检查你目标角色;在受管角色上新增表也计入。
编写迁移之后:
HCL=posthog/clickhouse/hcl
# add the table to the layer that composes it (hclexp locate a sibling table to find that layer)
bash $HCL/gen-golden.sh && bash $HCL/gen-sql.sh # refresh generated artifacts
bash $HCL/check.sh # must exit 0
在编辑某一层之前,请先阅读 posthog/clickhouse/hcl/README.md——其中介绍了应选择哪一层、抽象
列列表,以及为你编写迁移的代码生成路径。
测试
从 infi_clickhouse_orm_migrations 表中删除对应条目即可重新运行迁移。