mcpField
G
generating-clickhouse-query-performance-reports

generating-clickhouse-query-performance-reports

github.com/posthog数据与数据库40.5k134 次安装今天更新
代码仓库

关于 generating-clickhouse-query-performance-reports

面向分析 ClickHouse 慢查询的 PostHog 技能,提供从查询日志归档数据到撰写性能报告的调查方法。它侧重数据归因、根因验证及与历史报告对比,并配合另一技能执行查询。

主要功能

  1. 01分析查询日志归档数据
  2. 02按日期与产品分类慢查询
  3. 03归因团队及资源消耗
  4. 04通过 EXPLAIN 验证根因
  5. 05撰写报告并比较历史结果

适用场景

  1. 01调查生产环境的慢查询
  2. 02识别超时与内存超限问题
  3. 03定位高资源消耗的团队或工具
  4. 04跟踪性能问题的后续变化

安装

复制下面的命令,在终端中运行。

$npx skills add https://github.com/posthog/posthog --skill generating-clickhouse-query-performance-reports

安装后在 Claude Code 中输入/generating-clickhouse-query-performance-reports确认已加载。

包含的文件

除 SKILL.md 外,安装时一起下载的参考资料和脚本。

references/
hogql-deep-dive.md
materialization-analysis.md
query-patterns.md

详细介绍

生成 ClickHouse 查询性能报告

本技能是调查缓慢的 ClickHouse 查询并撰写性能报告的_方法论_。它与 querying-production-databases-via-metabase 配合使用,后者是_机制_(受 SSO 管控的认证和 hogli metabase:query)。本技能中的每个查询都要通过它来运行。

报告本身并不公开。当它存在时,私有仓库 PostHog/query-performance-analysis 保存着历史报告和示例查询 ID;本仓库只保存工具和方法论。该仓库通常检出为 posthog 检出目录的同级文件夹(例如相对于仓库根目录的 ../query-performance-analysis,或者位于同一父目录下与它并列)。查找名为 query-performance-analysis 的同级目录,其中包含存放按日期命名报告的 analysis/ 文件夹。如果你找到了它,就把新报告作为新的 markdown 文件添加到那里,放在 analysis/ 下,命名为 <YYYY-MM-DD>-<topic>.md(与现有命名保持一致,例如 2026-05-27-slow-queries-14d.md)。

同级仓库可能不存在,这没关系。 如果你找不到它,不要写入公开的 posthog 仓库,也不要因此阻塞:改为把报告写入临时文件夹(例如 /tmp/<YYYY-MM-DD>-<topic>.md),告诉用户你把它放在哪里,并跳过第 9 步中的历史报告对比(没有历史可供对比)。

数据源:posthog.query_log_archive(而非 system.query_log)

生产集群上的 system.query_log 仅保留几小时,因此无法回答跨天的问题。请改用分布式归档表:

FROM posthog.query_log_archive

它大约保留三周,并将 log_comment 暴露为带类型的列,因此无需 JSONExtract。直接查询它即可(它已经会在集群中分发)。始终过滤 is_initial_query,以免分布式子查询被重复计数。在信任某个时间窗口之前,先用按天 count() 确认当前保留时长(参见 references/query-patterns.md)。

关键列(完整列表可通过 system.columns WHERE table='query_log_archive' 获取):

列含义
team_id(Int64)租户。0 / 空表示内部或未归属。
lc_kind查询的发起方式:request(同步 API/Web)、celery(异步刷新)、temporal、cohort_calculation、dagster。
lc_productproduct_analytics、warehouse、experiments、messaging、web_analytics、replay、llm_analytics、cohorts……
lc_access_methodpersonal_api_key、oauth、sharing_token,或空(已登录 Web)。
lc_query__kind产品查询类型:TrendsQuery、FunnelsQuery、RetentionQuery、HogQLQuery……
lc_workloadWorkload.OFFLINE / ONLINE。
lc_feature、lc_temporal__workflow_type、lc_route_id、lc_api_key_label用于归因的来源详情。
lc_dashboard_id、lc_insight_id、lc_experiment_id、lc_cohort_id将查询关联回触发它的对象。
query、query_duration_ms、read_bytes、read_rows、memory_usage、exception_code查询及其开销。

两个区域都有该归档。US 和 EU 是独立集群,工作负载和物化列不同;跨区域比较时需对两者都运行。使用 hogli metabase:databases 发现每个区域当前的 ClickHouse 数据库 id(id 并不稳定)。请注意,某个区域的 ONLINE 和 OFFLINE Metabase 连接会分发到同一个逻辑集群,因此它们返回相同的 query_log_archive 数据。

什么算作慢查询

query_duration_ms > 30000 OR exception_code IN (159, 160, 241)
Code含义
159TIMEOUT_EXCEEDED
160TOO_SLOW
241MEMORY_LIMIT_EXCEEDED

不要添加 type = 'QueryFinish':OOM 和超时的行是 type = 'ExceptionWhileProcessing', 因此该过滤条件会静默丢弃所有失败记录。duration/exception 谓词已经排除了 QueryStart 行(duration 为 0)。通过 normalized_query_hash 排除集群健康检查查询 (模式见 references/query-patterns.md)。

生成报告

标准工作流,从粗略到具体逐步构建。每一步的 SQL 都在 references/query-patterns.md 中。

在第 9 步之前不要阅读之前的报告。 第 1-8 步应以全新的视角针对原始数据运行, 这样分析才能覆盖最大的范围,而不是重走上一份报告的发现。过早阅读之前的报告会让你 锚定在它的分类上,从而容易漏掉它从未提及的新问题。只有在完成独立分析之后,才与历史记录进行对比。

  1. 确认窗口。 在目标范围内按天执行 count(),以验证归档确实覆盖了该范围(保留期可能比你预期的更短)。
  2. 总体摘要。 慢查询总数、集群查询总小时数、读取字节数、涉及的团队,以及成功但慢 / 超时 / OOM / 其他的分布。同时还要采集所有查询的集群级总量(不只是慢查询集合):查询总秒数、CPU 总秒数(类型化的 ProfileEvents_OSCPUVirtualTimeMicroseconds 列,而不是 Map 查找)、读取总字节数,以及 OOM 总数(references/query-patterns.md §1b)。慢查询集合的合计是有偏的子集;所有查询的总量才是诚实的“本期是不是更繁忙 / 读取更多?”分母,也是未来报告对比的基线。一旦窗口老化超出保留期,这些数据就无法回填,因此每次运行都要记录它们。
  3. 日期分布。 按天统计慢查询数、超时数和 OOM 数。异常事件正是在这里自我显现:在平稳基线上出现持续多天的 OOM 或超时激增。
  4. 分类。 按 lc_kind × lc_product × lc_access_method 分组。这样可以把后台工作(数据建模、dagster 预聚合、批量导出)与同步的用户查询区分开。
  5. 归因。 按 team_id 深入分析最严重的类别。分别按集群总小时数(sum(query_duration_ms))和OOM 数排序。在把任何问题称为系统性问题之前,先检查是否是某个团队或某个 API key 在某个指标上占主导:一个通过 personal_api_key 查询的集成就可能占集群 OOM 的绝大多数,而所谓的“异常事件”其实只是单个租户。先按 team_id + lc_api_key_label 归因。然后再加上一个针对所有查询(不只是慢查询集合)的头部消费者视图:按字节数、CPU 秒数和墙钟时间排序的头部团队、头部 API key(lc_api_key_label)和头部工具(lc_product)(references/query-patterns.md §4c)。重但快的消费者正是在这里显现:某个租户或集成可以通过数百万次廉价查询在集群 CPU 或字节数上占主导,却从不越过慢查询阈值,因此对慢查询集合排名而言是不可见的。每行的 CPU:墙钟时间比可以区分受计算约束的负载和受等待/IO 约束的负载。
  6. 刻画面向用户的慢查询。 对于 lc_kind='request' AND lc_product='product_analytics' 且 lc_access_method 为空的情况(已登录的 Web),按 lc_query__kind 细分,并标记对 person_properties 的 breakdown_value 用法和 JSONExtract。这是产品上可行动的桶。始终要包含 JSON 提取属性细分(references/query-patterns.md §7):从慢查询集合中 JSON blob 里提取出的头部事件属性和 person 属性名称,以及每个属性分别由哪些团队使用。这些是物化候选,也是必需的报告输出。HogQLQuery(用户和 AI 编写的任意 SQL)值得单独深入分析,包括其中有多少是 AI 编写的、以及它为什么慢;参见 references/hogql-deep-dive.md。
  7. 对最严重的对象做根因分析。 对于最主要的发现,不要止步于“团队 X 很慢”:拉取完整查询,对其_原因_形成假设,然后用 EXPLAIN 验证它。对单个查询做根因分析是 optimizing-clickhouse-and-hogql-queries 技能的职责;其 references/investigation-playbook.md 就是操作手册(拉取完整查询、字节数 vs CPU vs 时长、运行时原因、来源追踪、EXPLAIN)。有用的发现要包含原因(“因为时间过滤被函数包裹而无法裁剪 granule,所以扫描了全部历史”),即使只是作为假设表述。
  8. 示例 + 报告撰写。 对每项发现中最严重的对象记录 query_id + event_date,然后撰写报告(结构见下)。由于 system.query_log 的保留期很短,示例要从 query_log_archive 中解析(WHERE query_id = '…' AND event_date = '…'),而不是旧的 Metabase 查询卡。为每个示例链接一个可分享、自包含的 Metabase URL(references/query-patterns.md 中的 query_link 配方),以便读者直接点击跳到该查询。当你起草建议时,把其中可研究的建议落到代码上:通过派生后台研究 Agent(见下文“把建议落到代码上”)让建议指向实际的文件和改动,而不是说“审查 X”。
  9. 与上一份报告做对比(如果存在,放在最后做)。 如果同级的 query-performance-analysis 仓库不存在,就完全跳过这一步。否则,只有在完成上述独立分析之后,现在才去读取其 analysis/ 文件夹中最近的一份带日期的报告(按文件名日期排序)。在新报告中增加一个简短的 delta 部分,涵盖:自上次以来发生了什么变化(新的异常事件、增长或已解决的发现、总体数字上升或下降),以及对上一份报告标记为需要采取行动的任何事项做一个跟进检查(建议做的物化、需要关注的团队、需要改为增量的管道)。对每项此前的跟进事项,说明它是已解决、仍未解决还是出现回退,并附上当前数字作为证据。把它放在最后做是有意为之:这样可以保持新分析不带偏见,同时又能对历史闭环。在引用 delta 之前先让窗口具有可比性: 确认上一份报告使用的是相同的窗口长度(这里两份报告都使用 now() - INTERVAL N DAY 的滚动窗口,因此长度相同,但边缘日期有重叠和部分包含)。两个滚动窗口之间的总体数字通常由落在其中一个窗口而没落在另一个窗口的一次性异常事件主导,所以大幅下降很少是结构性改进。始终还要比较一个排除异常事件的基线(例如去掉激增天后每天的 OOM 数),以免误读 delta,并在某个总量变动是因为异常事件进入或离开窗口时明确说明。记住,这些汇总指标(读取字节数、集群小时数)只覆盖慢查询集合,而不是集群总 I/O,所以当某个重后台任务的运行跨过或不再跨过 30 秒阈值时,它们也会变动;要把字节数/小时数的大幅波动归因到具体类别(通常是一两条后台管道),而不是把它报告为集群范围内的变化。

将建议落实到代码

像 "audit pipeline X" 或 "materialize property Y" 这样的建议,如果指向实际代码,就会有用得多。对于每一条能映射到 PostHog 代码库中具体位置的建议,派生一个后台研究 Agent(Agent 工具,run_in_background: true,subagent_type: general-purpose 或 Explore)来阅读源码并返回:相关代码目前的运作方式、需要修改的具体文件 / 函数、任何约束条件,以及是否已经存在更好的机制。每条可研究的建议派生一个 Agent,全部放在同一条消息中,使它们并行运行,并在建议草拟完成后立即执行。让它们在你处理增量(第 9 步)和最终定稿时运行,然后把每项发现并入对应的建议中:把 "audit X" 替换为 "X 在 <file> 中以 <current behavior> 实现;改动是 <specific>",并引用文件路径,以便人类可以直接跳转查看。Agent 只负责研究和报告;它们不会修改代码。

并非每条建议都能以这种方式研究。只有在源码是事实来源时才派生 Agent;跳过运维 / 基础设施类条目:

建议形态可研究?Agent 阅读的内容
重写缓慢的 insight / 查询形态是posthog/hogql_queries/ 下的查询运行器、它发出的 HogQL
物化属性 X是物化列注册表(ee/clickhouse/materialized_columns/)
让 pipeline Y 增量执行是构建它的 dagster / temporal 作业
限制内存 / 为每个 key 添加查询守卫是应用 ClickHouse SETTINGS 和按 key 限流的位置
添加 breakdown 基数守卫是trends / breakdown 查询运行器
调查某段基础设施事故时间窗口否不适用(部署、节点健康状况、集群状态)
观察 / 确认某租户的预期负载否不适用(需要人类判断)

给每个 Agent 一个聚焦的提示词:该建议、具体问题,以及一项指令——返回文件路径 + 当前行为 + 精确的改动点,并且不做任何修改。Agent 阅读 posthog 仓库(本 skill 所在之处);报告本身则写入单独的 query-performance-analysis 仓库。

解读结果

  • "慢查询" 中其实包含两类群体。 紧超时导致的 API 噪声(查询在约 10s 时因较低的 max_execution_time 而报错,通常是 personal_api_key)会虚增原始计数,却不代表真实的计算量。真正昂贵的工作最好用总集群小时数和 OOM 数量来衡量。务必指出这一区别;不要让超时数量伪装成缓慢。
  • 读取字节数是最真实的开销信号,比耗时更可靠(耗时会随缓存和集群负载而变化)。高字节数对应低行数意味着存在重列,几乎总是对 properties blob 做 JSONExtract。若要定位单个查询的根本原因,请参见 optimizing-clickhouse-and-hogql-queries skill。
  • 后台 pipeline 通常主导原始集群时间(数据建模 DAG、web 分析预聚合)。这是预期之中的;要衡量它们时,应看其扫描量是否必要,并将其与面向用户的延迟分开考量。

报告结构

报告应按以下顺序包含:

  1. 一句话范围:区域、时间窗口,以及所用的慢查询定义 / 排除项。
  2. 标题数字表 + **集群范围总计(所有查询)**表(总查询秒数、 CPU 秒数、读取字节数、OOM 次数)+ 两个总体的说明。
  3. 每日分布表(标出任何事故窗口)。
  4. 发现,按最严重者优先。每个发现都需要至少一个具体的 query_id + event_date, 通过可分享的 query_link URL 链接(见 references/query-patterns.md),以便读者直接点击 跳转到确切的查询,还要附上关于它为什么慢的假设(来自 optimizing-clickhouse-and-hogql-queries 技能中的调查手册)。按发现的类型分组:单个租户的事故、最重的集群时间消耗者、 面向用户的洞察变慢,以及紧凑超时的 API 噪音。
  5. 一个按资源划分的顶级消耗者部分(所有查询,不只是慢查询):按字节 / CPU / 墙钟时间 排名的顶级团队、顶级 API 密钥和顶级工具(lc_product)(references/query-patterns.md §4c), 指出从不触发慢查询阈值的消耗者,以及计算受限与等待受限的划分。
  6. 一个 JSON 提取属性表:从慢查询集中的 JSON blob 里提取的顶级事件属性和人员属性名称, 以及使用每个属性的团队(references/query-patterns.md §7)。这些是物化候选项。
  7. 与每个发现挂钩的具体建议(物化属性 X、限制每个 API 密钥的内存、 让管道 Y 变为增量式,……)。把可研究的建议落在代码上(见"将建议落到代码上"):引用 文件 / 函数和具体改动,而不只是"审计 X"。
  8. 一个与上一份报告的差异(第 9 步):自上次以来发生了什么变化,以及对上一份报告所建议的 每项行动的后续检查(已解决 / 仍待处理 / 出现倒退,并附上数字)。没有上一份报告时省略 这一节。

将完成的报告保存为同级 query-performance-analysis 仓库中的 analysis/<YYYY-MM-DD>-<topic>.md, 绝不要保存在公开的 posthog 仓库中;如果该仓库不存在,则保存到 临时文件夹(例如 /tmp/<YYYY-MM-DD>-<topic>.md)并告诉用户路径。

参考

  • references/query-patterns.md:针对 query_log_archive 的、可用于上述每个步骤的即用型 SQL。
  • references/materialization-analysis.md:查找要物化的属性和要删除的列, 在美国和欧盟两个区域运行。
  • references/hogql-deep-dive.md:专门分析 HogQLQuery(任意用户 / AI SQL), 包括如何识别 AI 编写的 HogQL(lc_product/lc_feature,而不是 ai_query_source)以及 使即席查询和 AI 查询变慢的原因。

相关技能

此技能是舰队级的:它发现并排名所有团队的慢查询并撰写报告。一旦某个发现指向某个你想要解释 或修复的查询,请切换到 optimizing-clickhouse-and-hogql-queries —— 它 负责对单个查询进行根因分析(其 references/investigation-playbook.md)并在 正确的层级(printer、查询运行器或 ClickHouse 迁移)应用修复。

评论

支持 Markdown