FEATURED · 精选文章

PostHog ClickHouse 物化分析实战:从慢查询 JSONExtract 到建列/删列的完整方法论

发布时间 / 2026/9/9 12:28:17
来源 / 创域科博编辑部
栏目 / 资讯中心
PostHog ClickHouse 物化分析实战:从慢查询 JSONExtract 到建列/删列的完整方法论 PostHog ClickHouse 物化分析实战从慢查询 JSONExtract 到建列/删列的完整方法论【免费下载链接】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 仓库中.agents/skills/generating-clickhouse-query-performance-reports技能集的references/materialization-analysis.md编写是该技能「慢查询性能报告」方法论的核心延伸。文章面向负责 PostHog 生产 ClickHouse 集群US / EU容量与查询性能的工程师讲解如何系统性地找出「值得物化的 JSON 属性」消除昂贵的JSONExtract全扫描与「值得删除的物化列」回收磁盘并给出完整可运行的 SQL、Dagster 作业配置建议与最终报告格式。读完本文你将掌握一条从候选识别、交叉核对、风险评估到人工执行落地的闭环工作流。适用范围与前置约定物化materialization分析是 PostHog 慢查询治理中收益最直接的手段之一用户的属性存放在事件 JSON blobproperties、person_properties、group_properties里若每次都靠JSONExtract在查询期解析ClickHouse 会全量扫描整列 JSON导致 read_bytes 巨大、查询缓慢甚至 OOM。把高频属性拆成独立的物化列前缀通常形如mat_*后查询即可走普通列扫描与索引。本分析有几条硬性前置约定务必遵守US 与 EU 各跑一遍。两地是独立集群物化列集合与工作负载不同候选列表必然不同不能以一方代替另一方。所有查询经由query-clickhouse-via-metabase技能执行底层为hogli metabase:query --region us|eu --database-id id数据库 ID 用hogli metabase:databases发现并非固定值。结果写入私有仓库PostHog/query-performance-analysis的analysis/date-materialization-candidates.md不写入本公开仓库。若该兄弟目录不存在则写入临时目录并告知用户。宽列列表查询要分批一次只检查约 5–6 个列避免超出 Metabase 响应截断上限。数据源统一使用posthog.query_log_archive归档表保留约三周system.query_log只保留数小时无法支撑多日分析且始终过滤is_initial_query以免分布式子查询被重复计数。分析的整体位置见技能主文档 SKILL.md报告中各步骤可直接运行的 SQL 模式见 query-patterns.md其中 §7 提供了 JSONExtract 属性拆解查询。Step 1确定「值得物化」的候选属性候选列表的来源是query-patterns.md§7 的JSON 提取属性拆解查询统计慢查询中从 JSON blob 中抽取的属性名按column事件properties、person_properties、group_properties切分并附上驱动这些查询的团队。物化分析的窗口要放宽到约 30 天——一个属性若被持续命中才值得物化单日的偶发热点不足以作为依据。对候选做两层排序按slow_queries命中该属性的慢查询次数排序按teams涉及的团队数排序。被多个团队的大量慢查询同时命中的属性是最强候选。column字段决定了属性位于哪张表的哪个 JSON 列上这正是后续 Dagster 作业需要的三元组输入(table, table_column, property)。反查候选属性所属的表与列当需要针对某个具体候选属性恢复其表别名与 JSON 列时使用下述归档查询注意query_log_archive中保存的是执行过的 SQL 文本用extractAll抓取模式SELECT arrayDistinct(extractAll(query, (\\w)\\.\\w*properties,\\s*PROPERTY)) AS table_aliases, arrayDistinct(extractAll(query, \\w\\.(\\w*properties),\\s*PROPERTY)) AS columns FROM posthog.query_log_archive WHERE event_time now() - INTERVAL 7 DAY AND query LIKE %PROPERTY% LIMIT 100把PROPERTY替换为候选属性名即可。查询结果给出诸如e.properties中的别名与properties/person_properties/group_properties的实际列名。补充仓库内自动化分析器的工作方式虽然技能文档强调手工、谨慎的分析流程但仓库中其实也存在一个可自动化的分析器理解它有助你核对候选列表的合理性analyze.py 中的_analyze()直接扫描clusterAllReplicas({cluster}, system, query_log)用extractAll(query, JSONExtract[a-zA-Z0-9]*?\\((?:...)?(.*?), .*?\\))抽取被JSONExtract引用的列再配合exception_code IN (159,160)、read_bytes 20e9、read_rows 5000000、query_duration_ms等过滤条件最后HAVING要求「超时/过慢命中数 0 或慢查询数 9」才视为候选并LIMIT 100防止单轮加列过多。它会刻意排除personal_api_key调用与 celery 后台任务理由见代码注释API 请求失败比界面查询失败代价小。materialize_properties_task()随后与已有物化列集合做差集并回填。这可以视为本文所述人工方法的自动化同源实现。Step 2核对「已物化」清单区分建列与绕过对每个候选属性先确认它是否已经有物化列SHOW CREATE TABLE sharded_events将输出与 Step 1 的候选列表交叉核对即可把候选分成两类确实需要物化尚无对应物化列在绕过已有物化列物化列存在但查询仍然走JSONExtract。绕过bypass的机理绕过通常意味着属性以用户手写 HogQL的方式访问例如JSONExtractString(properties, $foo)。从源码结构看这种写法在 HogQL 中会生成一个ast.Call节点从而跳过visit_property_type()的属性改写逻辑而写成properties.$foo的字段访问形式才会被改写为物化列引用。相关逻辑见 property_types.py 中的build_property_swapper/PropertySwapper其_JSON_EXTRACT_SCALAR_CASTS表还展示了JSONExtractString→String、JSONExtractInt→Int64等标量类型映射。文档列举的已知绕过来源包括HogQL / DataVisualization 节点任意用户 SQL 与可视化查询部分 survey SQL。识别出绕过案例后结论不应是「加物化列」而应是「把这些查询改成properties.$foo写法」或推动查询发起方修正。补充物化列注册表的实现事实columns.py 维护了物化列的运行时注册表get_materialized_columns(table)从system.columns读取并缓存缓存 key 为materialized_columns:v3:tableMaterializedColumn数据类记录列名、类型、可空性及minmax_/bloom_filter_/ngram_bf_lower_等跳数索引标记其表达式生成方法返回JSONExtract(table_column, property, type)或JSONExtractRaw(...)——即物化列的取值正是把JSONExtract下沉到写入期顶层同时维护SHORT_TABLE_COLUMN_NAME映射properties→p、group_properties→gp、person_properties→pp、group0~4_properties→gp0~gp4。物化列名即以此为前缀形如mat_*这就是为何本文各查询都围绕mat_%与mat_col模式编写。Step 3找出「可以安全删除」的物化列删除物化列的唯一安全判据是30 天内零查询引用 且 零数据。用法检查必须覆盖全部查询含非慢查询而不仅是慢查询子集。3.1 用法检查按列做定向 countIf避免超时-- usage: does any query reference the column? (targeted countIf per column to avoid timeouts) SELECT countIf(query LIKE %mat_col%) AS uses FROM posthog.query_log_archive WHERE event_time now() - INTERVAL 30 DAY AND is_initial_query把col逐个替换为候选列名执行不做全列正则扫描而做定向LIKE是为了避免对归档大表做高代价的逐行正则匹配导致超时。3.2 磁盘占用对 system 表使用 clusterAllReplicas-- disk size SELECT name, formatReadableSize(sum(data_compressed_bytes)) AS compressed FROM clusterAllReplicas(posthog, system, columns) WHERE table sharded_events AND name LIKE mat_% GROUP BY name ORDER BY sum(data_compressed_bytes) DESC该查询只用于非 Distributed 的 system 表此处是system.columns因此可以用clusterAllReplicas汇总所有副本上的元数据。3.3 数据存在性检查必须读 Distributed 表且分批-- data presence before dropping (batch the column list) SELECT countIf(mat_col ! ) AS nonempty_col, uniqExactIf(team_id, mat_col ! ) AS teams_col FROM events WHERE timestamp now() - INTERVAL 7 DAY⚠️ 数据存在性必须从 Distributed 的events表读取而不是对本地sharded_events用clusterAllReplicas(...)Distributed 表会向每个分片的一个副本扇出因此每行只被计数一次clusterAllReplicas会命中每个副本使计数被乘以副本因子而虚高。3.4 风险分级结合 3.1–3.3 的结果用法30 天数据7 天窗口内结论零查询零数据安全删除加入 drop 候选主表零查询有活跃数据有风险的可选项。删除本身不会丢数据原始值仍在 JSON blob 中但日后若想再物化需要昂贵的回填backfill。应记录为「可选删除」附带大小与团队信息供决策关于「有数据但零查询」要提醒一句7 天窗口内非空只能说明该列仍被写入新事件真正判断风险要在报告中写明其压缩大小回填代价的量级与来源团队把决策权交给集群负责人。Step 4Dagster 作业——交给人工执行这两类作业必须由具备 Dagster 权限的人工操作员运行分析者自身不能代为执行。运行前操作员应与#team-clickhouse确认集群不处于异常状态——建列回填与删列都会增加负载绝不能落在集群本就吃紧的时间点。给操作员的配置应作为建议而非代为执行。仓库中两个作业的实现可作为配置参数的事实依据建列作业 create_materialized_column.pyMaterializeColumnConfig含table默认events可选person、table_column默认properties可选group_properties/person_properties、properties: list[str]、backfill_period_days默认 90、dry_run默认False与is_nullable默认True。作业内部调用ee.clickhouse.materialized_columns.analyze.materialize_properties_task先dry_run校验再逐属性materialize(...)并按backfill_period_days回填。删列作业 drop_materialized_column.pyDropMaterializedColumnConfig含table、column_names: list[str]且dry_run默认为True影响最小。内部调用ee.clickhouse.materialized_columns.columns.drop_column。给操作员的建议要点Create使用create_materialized_columnteam-clickhouse location按 region 分别运行。由于回填会给集群增加负载安排在周末执行。为每个新物化列准备一行(table, table_column, property)三元组配置。Drop使用drop_materialized_column保持其dry_run: true默认值先打印将删除的列清单供确认再在复核后关闭 dry_run 真正执行。可选删除项的处理「零查询但有活跃数据」的可选删除项不要直接执行而是写进同一份 Dagster 配置里作为注释掉的条目并附带大小与团队信息——这样既不丢数据也把信息留在将来可执行的位置。报告格式最终产物写入query-performance-analysis的analysis/date-materialization-candidates.md的结构顺序为四张推荐表先表后细节新建物化建议表US-new、EU-new 分开删除候选表US-drop、EU-drop 分开。Dagster 配置建议。调查过程细节。推荐表字段如下。新建物化建议TableColumnPropertySlow queries (30d)TeamsTimeoutsAvg readMax readEst. column size删除候选ColumnCompressedNon-empty eventsTeamsSafe to drop?要点回顾Column告诉 Dagster 作业属性落在哪个 blob对应table_column选列时以Slow queries与Teams为优先级核心Timeouts/Avg read/Max read用于量化收益Est. column size用于量化存储成本可选删除无查询但有数据进入 dagster 配置的注释条目附Compressed与Teams信息。结合源码的纵深建议若要追查候选属性背后的查询链路可继续在仓库中阅读慢查询报告与 SQL 模式母版query-patterns.md§7 提供 JSONExtract 属性拆解的完整 SQL 与按属性钻取每个使用团队的下钻查询HogQL 任意用户/AI SQL 的专项分析方法hogql-deep-dive.md物化列注册表与类型推断columns.py含列名短前缀映射、索引类型minmax_/bloom_filter_/ngram_bf_lower_、缓存 key自动化分析器analyze.py_analyzematerialize_properties_task含回填参数语义两个 Dagster 作业create_materialized_column.py 与 drop_materialized_column.pyHogQL 属性改写与物化列替换逻辑property_types.py理解properties.$foo与JSONExtractString(properties, $foo)走向不同分支的根因。小结一次物化分析的执行清单US、EU 分别执行query-patterns.md§7 的 JSON 属性拆解查询窗口放宽至 30 天得到(column, property, slow_queries, teams)候选用SHOW CREATE TABLE sharded_events交叉核对已物化列区分「待物化」与「绕过」两类对每个「待物化」候选反查table/table_column/property三元组对存量mat_%列执行三层检查30 天用法定向countIf、压缩大小system.columnsclusterAllReplicas、数据存在性Distributedevents表分批按「零查询 零数据 → 安全删除」「零查询 有数据 → 注释化的可选删除」分级向操作员提交四张推荐表 Dagster 配置建议Create 安排在周末、Drop 保持dry_run: true复核后执行结果写入analysis/date-materialization-candidates.md。【免费下载链接】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),仅供参考
RELATED — 相关阅读

相关资讯

LATEST — 最新资讯

最新发布

TODAY — 本日精选

新闻

WEEKLY — 本周精选

新闻

MONTHLY — 本月精选

新闻