如何在 PostHog ClickHouse 中创建物化列加速 JSON 属性查询?
【免费下载链接】posthog:hedgehog: PostHog is the leading platform for building self-driving products. Our developer tools – AI observability, analytics, session replay, flags, experiments, error tracking, logs, and more – capture all the context agents need to diagnose problems, uncover opportunities, and ship fixes. Steer it all from Slack, web, desktop, or the MCP.项目地址: https://gitcode.com/GitHub_Trending/po/posthog
PostHog 把事件的 JSON 数据存在 ClickHouse 的字符串列中,查询时再读取并解析,这类“宽列”(fat column)的读取开销很大,慢查询常出现在对$browser_language这类 JSON 属性做过滤的场景。物化列(materialized columns)把 JSON 中指定的属性预先物化成磁盘上的独立列,官方手册给出的收益是:读取这些列最快可比普通 JSON 属性快 25 倍(up to 25x)。
本文的任务很明确:为高频使用的属性创建物化列,并用回填(backfill)让历史数据也走新列。适用前提是你的实例运行着 PostHog 的 ClickHouse 集群,并且你需要区分两条路径——PostHog 生产环境走 Dagster 作业,自建/本地环境走仓库提供的 Django 管理命令。
先确认:为什么必须回填才生效
物化列创建后只对新写入的数据立即生效。要让已有数据也受益于新列,必须执行回填,而回填会显著增加集群负载,官方手册明确建议在周末执行。手册原文指出:“materialized columns also require backfilling the materialized columns to be effective - an operation best done on a weekend due to extra load it adds to the cluster.”
所以任何创建物化列的计划都要包含两个决定:选哪些属性和回填多少天的历史数据。
路径一:依赖自动物化(云端默认行为)
PostHog 有一个 cron 任务,会分析上周运行的慢查询,找出其中用到的属性并自动物化其中一部分,实现代码在 analyze.py。相关开关通过环境变量与实例设置控制(手册指向 PostHog 的环境变量文档与 instance settings)。
需要注意:这个 cron 经常因为集群问题或正在进行的数据迁移而被临时禁用,因此它不能作为你“保证某列被物化”的依据。如果你的目标属性不在自动物化范围内,或者你希望控制回填窗口,就走下面两条手动路径。
路径二(手动):用 Django 管理命令创建
仓库提供了materialize_columns管理命令(源码),在 PostHog 后端环境中执行。它有两种模式:
- 指定
--property:直接物化你列出的属性,跳过慢查询分析; - 不指定
--property:按--analyze-period指定的时间窗口分析慢查询,自动挑选属性,受--max-columns限制单次物化数量。
1. 先 dry-run 预览计划
--dry-run只打印计划,不修改任何表,日志会输出Dry run: No changes to the tables will be made!:
python manage.py materialize_columns \ --property '$browser_language_prefix' '$app_namespace' \ --property-table events \ --table-column properties \ --backfill-period 90 \ --dry-run--property支持在一个参数组内传多个属性(命令帮助中的示例形式是--property abc '$.abc.def'),$开头的属性名建议加引号。
2. 确认后实际执行
去掉--dry-run重复执行同一命令即可。执行时日志会输出实际操作的表和属性:
Materializing column. table=events, property_name=['$browser_language_prefix', '$app_namespace']3. 参数说明
| 参数 | 取值 / 默认 | 用途 |
|---|---|---|
--property | 属性名列表 | 要物化的属性;提供后跳过自动分析 |
--property-table | events(默认)或person | --property所在的表 |
--table-column | properties(默认)、group_properties、person_properties | 物化来源的 JSON 列 |
--backfill-period | 天数为默认值(对应MATERIALIZE_COLUMNS_BACKFILL_PERIOD_DAYS环境变量),0表示不回填 | 回填多少天历史数据 |
--dry-run | 布尔开关 | 只打印计划,不执行变更 |
--min-query-time/--analyze-period/--analyze-team-id/--max-columns | 对应同名环境变量或默认值 | 仅在未指定--property的自动分析模式下生效 |
列名由系统生成:events表的物化列以mat_为前缀,person表以pmat_为前缀,属性名中的特殊字符会被替换(见 columns.py 的_materialized_column_name)。同时会在数据表上为新列创建minmax数据跳过索引,用于加速过滤。
路径三(PostHog 生产环境):通过 Dagster 作业
PostHog 在生产环境中用 Dagster 作业创建物化列:job 名为create_materialized_column,位于team-clickhouselocation,EU 与 US 两个区域各有对应的 playground 入口(手册中给出了两个区域各自的链接,见 手册原文)。
在 playground 中配置create_materialized_columns_op,手册给出的示例配置(作业源码):
ops: create_materialized_columns_op: config: backfill_period_days: 90 dry_run: false properties: - $browser_language_prefix - $app_namespace table: events table_column: properties配置项含义(手册原文):
table:要物化的 ClickHouse 表,例如events、person;table_column:存放属性的 JSON 列,例如properties、person_properties、group_properties;properties:要物化成列的属性名列表;backfill_period_days:回填多少天历史数据(手册示例为90);dry_run:设为true可预览将物化什么而不做任何变更。
该 op 还支持is_nullable(默认true),控制生成的列是否为Nullable类型。
验证结果
1. 检查列是否已创建
物化列的 comment 带有column_materializer::标记,项目实现就是靠这个标记识别物化列的(columns.py)。你可以在 ClickHouse 中查询:
SELECT name, type, comment FROM system.columns WHERE database = currentDatabase() AND table = 'events' AND comment LIKE '%column_materializer::%';能查到刚创建的对象即说明列已建好,type通常为Nullable(String)(--nullable默认开启时)。
2. 检查索引
默认会创建minmax_<列名>索引,可用同一实现中使用的查询方式核对:
SELECT name, table FROM system.data_skipping_indices WHERE database = currentDatabase() AND table = 'events' AND name LIKE 'minmax_%';3. 回填状态判断
回填走的是 ClickHouse mutation(ALTER TABLE ... UPDATE col = col WHERE timestamp > <截止日期>),提交后立即返回、在后台按分区更新。因此“命令成功返回”不等于历史数据已全部回填完成,文档没有给出统一的回填完成判定 SQL,建议结合集群的 mutation 状态自行跟踪。
限制与后续操作
- 回填负载:无论哪条路径,回填都会额外读写大量数据,官方建议安排在周末。
- 自动物化可能缺席:cron 会因集群问题或数据迁移被禁用,手动路径不受影响。
- 启用/禁用/删除:物化列不是永久的。仓库提供
update_materialized_column命令(源码):
# 启用 / 禁用某个物化列 python manage.py update_materialized_column enable events mat_xxx python manage.py update_materialized_column disable events mat_xxxdisable只把该列标记为禁用(通过列 comment 标记),不动数据;drop会真正执行DROP COLUMN及对应索引(DROP INDEX),是不可逆的删除操作,执行前确认该属性确实不再需要,例如:
python manage.py update_materialized_column drop events mat_xxx命令执行成功时日志输出Success!。如果你只是想让查询暂时忽略某个物化列,用disable而不是drop。
【免费下载链接】posthog:hedgehog: PostHog is the leading platform for building self-driving products. Our developer tools – AI observability, analytics, session replay, flags, experiments, error tracking, logs, and more – capture all the context agents need to diagnose problems, uncover opportunities, and ship fixes. Steer it all from Slack, web, desktop, or the MCP.项目地址: https://gitcode.com/GitHub_Trending/po/posthog
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考