mcpField
D
doris-profile-reader

doris-profile-reader

github.com/apache数据与数据库47105 次安装4 周前更新
代码仓库

关于 doris-profile-reader

doris-profile-reader 是用于分析 Apache Doris 查询运行时 Profile 的技能,帮助数据库工程师区分实际计算与等待,并检查连接和运行时过滤器行为,避免把 Profile 证据误当作深层根因。

主要功能

  1. 01区分计算、等待与背压
  2. 02解读算子和计数器信号
  3. 03诊断连接顺序与运行时过滤器
  4. 04检查倾斜、扫描、内存和溢写
  5. 05区分 Profile 证据与根因判断

适用场景

  1. 01查找 Doris 慢查询瓶颈
  2. 02评估连接顺序和运行时过滤器方向
  3. 03排查倾斜、溢写或内存压力

安装

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

$npx skills add https://github.com/apache/doris-skills --skill doris-profile-reader

安装后在 Claude Code 中输入/doris-profile-reader确认已加载。

包含的文件

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

agents/
openai.yaml
references/
counter-semantics.md
failure-patterns.md
join-order-diagnosis.md
known-issues-solutions.md
operator-guide.md
reading-workflow.md
runtime-filters.md
solution-playbook.md
source-profile-inventory.md
scripts/
extract_source_profile_inventory.py

详细介绍

Doris Profile Reader

目的

使用此技能识别 Apache Doris 查询运行时 Profile 中的实际瓶颈。核心原则是,在将某个算子判定为开销高之前,先区分实际工作与依赖等待、排队等待和背压等待。如果计划包含联接,还要区分直接的运行时瓶颈与计划形态成因,尤其是糟糕的联接顺序和运行时过滤器方向。

必须遵循的阅读顺序

  1. 阅读 references/reading-workflow.md,了解分析工作流和输出约定。
  2. 阅读 references/counter-semantics.md,了解计数器含义和优先级,尤其是等待计数器。
  3. 阅读 references/operator-guide.md,了解相关算子系列。
  4. 当 Profile 显示数据倾斜、不合理的联接/分布选择、剪枝不足、排序/溢出/资源压力、外部扫描/目录、生命周期等待,或用户希望仅凭 Profile 证据定位问题级根因时,阅读 references/failure-patterns.md。
  5. 当 Profile 或计划包含多个联接、大型哈希/嵌套循环构建、可能被联接剪枝的大型扫描、成对的快速/慢速计划、提示/重排后的 SQL,或用户询问联接形态/重排时,阅读 references/join-order-diagnosis.md。
  6. 当 Profile 或计划包含 RuntimeFilterInfo、RF... <-、RF... ->、JRFs、WaitForRuntimeFilter 或 AcquireRuntimeFilter 时,阅读 references/runtime-filters.md。
  7. 使用 references/source-profile-inventory.md 作为有源代码依据的算子/计数器清单。如果叙述性文档中没有提到某个计数器或算子,不要忽略它;请在此清单中查找,并按照 counter-semantics.md 中的规则分类。
  8. 当用户要求修复、变通方案、缓解措施、调优、SQL 重写,或所请求的输出结构包含解决方案字段时,阅读 references/solution-playbook.md。
  9. 确立根因层级并收到解决方案请求后,阅读 references/known-issues-solutions.md。将其作为历史证据,而不是 Profile 诊断的替代品。

必须遵守的解读规则

  • 不要将 WaitForDependencyTime、WaitForDependency[...]Time、WaitForData0、WaitForDataN、WaitForRpcBufferQueue、WaitForBroadcastBuffer、PendingFinishDependency 或 pipeline 阻塞/等待计数器直接视为算子计算成本。它们是依赖、数据到达、队列、内存或背压信号。
  • 不要把 DATA_STREAM_SINK_OPERATOR 上较高的 RpcMaxTime/RpcAvgTime 当作普通等待计数器而忽略。如果 BytesSent 和 RpcCount 均非零,这就是交换/RPC 路径的证据;如果该值接近墙钟时间,或高于本地序列化/压缩/发送计时器,就应将其纳入瓶颈结论,并检查通道、接收端或 BE 离群偏斜。
  • 如果某个会话变量、缺失的优化或较大的行数可以解释数据为何到达交换节点,除非本地扫描/排序/窗口/连接活动计时器占主导,否则不要将其作为主要慢因。如果 DATA_STREAM_SINK_OPERATOR 的 RPC 时间占用的时间超过已用时间的一半,应将计划/数据量视为放大因素,并将 RPC/BE 到 BE 传输保留为首要瓶颈候选。
  • 提供多个 profile 时,它们之间的差异优先于单个 profile 的等待现象。如果慢/快 profile 在连接顺序、裁剪、instance_num、交换分布或 bucket/sink 布局方面不同,应先诊断这些计划/数据布局差异,而不是归咎于普通 RPC、扫描器或交换背压。
  • 如果连接/投影热点主要由来自某个表的宽 JSON/VARIANT/字符串列造成,且工作由单个实例处理或存在严重偏斜,不要止步于表达式 CPU。应将可能的根因层表述为连接顺序/分布选择,或成本模型低估了宽列处理开销,并说明表达式 CPU 是运行时表现。
  • 对于 INSERT/SELECT profile,可见的巨量行 SPLIT_BY_STRING/cardinality(split_by_string(...)) 表达式是首要根因候选。除非有独立证据表明 sink/写入活动计时器占主导,否则不要将它们排在普通扫描/Shuffle/table-sink 数据量之后。
  • 对于 INSERT 偏斜,必须将接收端或 UNION/本地交换偏斜与目标 bucket 数、bucket 键、写入 bucket 并行度及热点值联系起来。如果没有准确的 DDL,应得出可能是目标 bucket/键设计不匹配或写入 bucket 数不足的结论,而不是止步于交换偏斜。
  • 在得出全表扫描或 INSERT 慢只是因为行数多之前,应先在 SQL/计划文本中搜索字符串密集型表达式,例如 SPLIT_BY_STRING、regexp、trim/replace 链、INSTR、COALESCE 和 NULLIF。处理海量行时,即使没有可见的 ProjectionTime 计数器,这些表达式仍是根因候选。
  • 对于点查或窄查询,如果输出很少但涉及许多 tablet/扫描器,应明确说明 bucket/tablet 裁剪情况。较高的 ScannerWorkerWaitTime 是扇出导致的现象,除非有工作负载压力证据;如果谓词和索引有效,但打开了许多 tablet,应优先考虑缺少 bucket 裁剪/表布局问题,而不是扫描器池争用。
  • 不要仅凭最大的类等待计数器给算子排序。应先根据 ExecTime/活动计时器、直接自定义计时器、行数/字节数、内存/溢写和偏斜进行排序。
  • 对查询瓶颈排序之前,应先确认 profile 类型。Async-profiler CPU 火焰图、FE load-image/load-auth profile、日志或缓存摘要可以作为其自身生命周期热点的有效证据,但它们不是 BE 查询运行时 profile。如果文件类型与所请求的慢查询问题不匹配,应指出这种不匹配,并要求提供相应的运行时 profile,而不是强行得出无关的 Doris 查询结论。
  • 对于 OOM、溢写后超时或内存限制 profile,应先找到内存占用方,再选择根因:检查会话/全局内存设置、溢写状态、各算子峰值内存、哈希连接构建/探测内存、聚合哈希/状态内存、排序缓冲区、table-sink 缓冲区、交换缓冲区和表达式物化。未确认聚合状态和表达式物化成本是否为原因之前,不要将大型连接定为根因。
  • 会话/配置证据的优先级可能高于对算子形态的推测。应先评估明确的非默认设置,例如禁用并行扫描、parallel_pipeline_task_num 设置过小、内存百分比/限制过低、禁用两阶段读取或禁用本地 Shuffle,然后再得出连接顺序不佳、扫描器争用或网络离群的结论。
  • 先独立分析多个 profile,再进行总结。对于每个 profile,先指出其主要活动/等待/资源信号,然后再进行比较。不要把无关的 profile 归并为一种笼统的扇出、扫描、连接或 RPC 解释。
  • 将 profile 可见的瓶颈与问题层面的根因分开。如果更深层的原因需要当前未提供的 schema、统计信息、版本、配置、日志、注释或快慢对比,应说明 profile 能证明什么,并列出确切的待查项,不要过度断言。
  • 对于解决方案字段,不要把每个合理的验证项都转成修复方案。先确定解决方案的来源:profile 已证实、确切的历史模式、代码支持但尚未证实的后续检查,或没有历史解决方案。如果已知模式证据中包含精确的历史参数名、值、PR 编号、版本号和 SQL 改写形式,应予以保留。
  • 如果诊断不确定或仅限于 profile 层面,优先留空历史解决方案,并提供有针对性的 next_checks,而不是给出具体缓解措施。错误的具体修复方案比不提供修复方案更糟。
  • 如果所请求的 schema 包含 historical_solution,只有对于 profile_proven 或确切的 historical_pattern 操作才填写该字段。如果该操作仍需验证,或是根据相邻模式族推断得出,应将 historical_solution 留空,并将该操作移至 next_checks。
  • 将合并 profile 中的 sum 计时器视为多个并行实例的累计值。计时器可能超过查询已用时间,且在许多扫描器、driver 或 fragment 并行运行时仍属正常。
  • 对于扫描,应优先关注 RowsRead、ScanRows、ScanBytes、ScannerCpuTime、ScannerGetBlockTime、ScannerWorkerWaitTime、I/O/解压计时器、谓词/延迟读取计时器和行过滤计数器。ScannerWorkerWaitTime 很重要,但它表示扫描器调度/线程池等待,而非扫描 CPU。
  • 对于运行时过滤器,应区分来源/构建端与目标/探测端扫描。在计划文本中,RFxxx <- expr 是由构建端生成的;RFxxx -> expr 则应用于目标扫描。在 profile 中,RuntimeFilterInfo 和扫描端等待/过滤计数器决定 RF 是发挥了作用,还是仅仅在等待。
  • 对于连接,在判断顺序之前,始终先识别构建端和探测/目标端。如果选择性连接/RF 来源的调度过晚,未能裁剪该扫描,那么即使扫描是当前直接的活动瓶颈,根因仍可能是连接顺序不佳。
  • 如果连接/分布选择看起来不佳,应检查估算行数与实际行数、统计信息/NULL/热点值证据、连接分布,以及存在时的 RF 方向。如果 profile 暴露了规划器/统计信息方面的问题,不要止步于“大扫描”或“大构建端”。
  • 不要要求必须提供配对的快速 profile 才能指出可能的连接顺序不佳。如果单个慢 profile 表明:在探测端/结果为空之前进行了大量无用的构建/扫描工作;RF 来源端在发出空/极小过滤器之前必须进行大量扫描;或者巨大的中间结果随后被高度选择性或矛盾的连接谓词消除,那么单个慢 profile 就足以作为证据。
  • 不要把“当前 RF 让其他扫描等待后跳过”视为连接顺序良好的证据。如果生成这个空/极小 RF 必须进行唯一一次昂贵扫描,那么 RF 来源/目标选择本身就是连接顺序问题。
  • 如果在空/极小探测端、保留端、半连接键集合或矛盾连接消除结果之前,已经付出了大型构建/来源端的成本,那么应指出构建/探测顺序可能不佳,除非 profile 证明该顺序在语义上不可避免,且不可能更早裁剪或短路。
  • 如果扫描、连接、聚合、交换、排序或 sink 算子的 max 远高于 avg,应进行偏斜检查:比较各实例的行数/时间/字节数、tablet 或 bucket 分配、本地 Shuffle、分布键以及全局/会话级并行度,然后再将原因归结为普通数据量。
  • 如果强烈匹配单 profile 连接顺序模式,不要用“可疑”“很可能不佳”或“尚未证实”来含糊其辞。如果更优的合法顺序仍需验证,应使用“可能不佳”;“尚未证实”仅用于确切的备选形态,而不是用于连接顺序诊断本身。
  • 当连接查询存在计划形态证据时,答案必须明确判断连接顺序/构建端-探测端/RF 方向是良好、可疑还是不佳。不要用含糊的“谓词问题”或“计划形态问题”取代这一判断。
  • 较长的 InitTime、OpenTime、CloseTime 或 profile 总时长可能很重要,但只有在确认它不是许多实例的累计值,且没有被已知的等待/依赖分支主导之后,才能将其作为依据。

标准回答格式

解释 profile 时,按以下顺序回答:

  1. Conclusion:用一到两句话指出可能的瓶颈;如果涉及 join,还要说明计划形态/join 顺序是否可能是原因。
  2. Evidence:列出 profile 计数器、算子名称、数值,并说明每项属于活跃工作量、数据量、等待/背压、内存/溢写,还是运行时过滤器证据。
  3. Reasoning:说明证据如何对应执行计划、哪一侧是构建端/探测端或 RF 源/目标、为何不采信容易误导的计数器,以及 join 顺序是否合理。
  4. Next checks:如果结论仍不确定,列出所需的最少额外 profile/日志/代码检查。
  5. Solution / mitigation(需要时):只提及此 profile 直接支持的解决方案,或 references/known-issues-solutions.md 中与之匹配的历史模式。如果没有适用的历史解决方案或经 profile 证实的解决方案,请如实说明,并将其列为后续检查项,而不是凭空给出建议。

始终保留不确定性。当 profile 信息不足时,明确使用“已证实”“可能”和“尚未证实”。

脚本

  • scripts/extract_source_profile_inventory.py:扫描 Doris 源码,查找由工厂创建的算子以及计数器/信息注册项。

脚本是用于生成证据的辅助工具,不能替代阅读流程。

评论

支持 Markdown