ClickHouse system.query_log 字段详解与排障实践

**摘要:**本文系统梳理 ClickHouse 查询审计日志表 system.query_log 的字段含义与排障实践。文章先介绍表引擎定义与按月分区、TTL 等关键特性,再按事件类型、性能指标、查询内容、涉及对象、异常信息、分布式链路、客户端信息、配置画像等维度逐组详解字段,并给出排障场景速查与常用巡检 SQL,最后说明副本去重、统计口径、落盘延迟与分区裁剪等使用注意,帮助读者快速定位慢查询、负载热点与来源。

一、概述

system.query_log 是 ClickHouse 的查询审计日志表:每条查询在开始、结束时由后台线程异步写入(需 log_queries = 1,默认开启)。它是慢查询分析、负载分析、来源定位的核心数据源。

复制代码
用户查询
   │
   ▼
┌─────────────────────────────────────────────────┐
│ query_log 写入时机(一条查询可产生多条记录)      │
│  ① QueryStart            开始时写入              │
│  ② QueryFinish           成功结束时写入 ← 统计用这条 │
│  ③ ExceptionBeforeStart  解析/规划期失败          │
│  ④ ExceptionWhileProcessing 执行中失败/被 kill   │
└─────────────────────────────────────────────────┘

⚠️ 统计耗时/扫描量必须加 type = 'QueryFinish',否则同一条查询会被重复计算。

二、表引擎定义

复制代码
ENGINE = MergeTree
PARTITION BY toYYYYMM(event_date)
ORDER BY (event_date, event_time)
TTL event_date + toIntervalDay(30)
  • 按月分区:查询必须带 event_date 条件做分区裁剪(与 event_time 双条件配合:event_date 管分区裁剪,event_time 管精确窗口)

  • 主键排序(event_date, event_time):日期 + 时间范围查询局部性好

  • TTL 30 天:日志只保留一个月,长期趋势需提前导出

三、字段分组详解

3.1 事件类型与时间(何时)

字段 含义
type 事件类型,见上方示意图
event_date 记录写入日期,分区键
event_time / event_time_microseconds 事件发生时刻(微秒版精度更高),巡检常用
query_start_time / ..._microseconds 查询开始执行时刻,query_start_time + query_duration_ms ≈ event_time
initial_query_start_time / ..._microseconds 分布式链路最初始查询的开始时刻

3.2 性能指标(多贵)------ 排障核心

字段 含义
query_duration_ms 查询耗时(毫秒)
read_rows / read_bytes 扫描的行数/字节数,判断"是否扫多了"的核心指标
result_rows / result_bytes 返回结果行数/字节数,read/result 比值即读放大
written_rows / written_bytes 写入行数/字节数(INSERT/CREATE 等)
memory_usage 峰值内存占用(字节),配合 max_memory_usage 排查 OOM

3.3 查询内容(是什么)

字段 含义
query SQL 原文
normalized_query_hash 归一化哈希(字面值替换为占位符后计算),WHERE id=1 与 WHERE id=2 hash 相同,用于按查询模式聚合
query_kind 查询类型:Select/Insert/Create/Alter...
current_database 执行时默认数据库
log_comment 通过设置项写入的备注,可携带 trace_id 做链路追踪
query_cache_usage 查询缓存:Unknown/None/Write(写入)/Read(命中)

3.4 涉及对象(动了哪些表)

databases / tables / columns / partitions / projections / views:查询涉及的库、表、列、分区、投影、视图数组。

3.5 异常信息(怎么死的)

字段 含义
exception_code 错误码(如 241 = MEMORY_LIMIT_EXCEEDED)
exception 错误消息文本
stack_trace 异常堆栈

3.6 分布式链路(谁发起、谁执行)

字段 含义
is_initial_query 1 = 用户直接发起的初始查询;0 = 分布式查询拆到各节点执行的分片子查询
query_id / initial_query_id 本条记录 ID / 链路根查询 ID。子查询的 initial_query_id 都指向根查询,按它聚合/去重可归并多节点记录
user / initial_user 本节点执行用户 / 初始发起用户
address / port 发起本查询的客户端(上级节点)地址
initial_address / initial_port 最初始客户端地址,定位"谁在打集群"
distributed_depth 分布式转发深度:初始为 0,每转发一层 +1

3.7 客户端信息(从哪来)

字段 含义
interface 1=TCP,2=HTTP
is_secure 是否 TLS
os_user 客户端机器操作系统用户名
client_hostname / client_name / client_revision / client_version_* 客户端主机名、驱动名及版本
http_method 0=非HTTP,1=POST,2=GET
http_user_agent / http_referer / forwarded_for HTTP 的 UA/Referer/转发链,经网关转发的查询靠它定位真实来源

3.8 配置与画像(怎么跑的)

字段 含义
Settings 本查询生效设置项 Map(含 session 覆盖)
ProfileEvents 细粒度计数器 Map(SelectedBytes、FileOpen、DiskReadElapsedMicroseconds 等),深挖 IO/缓存用
quota_key 配额归组键
thread_ids 参与执行的线程 ID
transaction_id 事务 ID(非事务查询为 0)

3.9 used_* 系列(查询画像)

used_aggregate_functions / used_aggregate_function_combinators / used_database_engines / used_data_type_families / used_dictionaries / used_formats / used_functions / used_storages / used_table_functions / used_row_policies:查询用到的聚合函数、函数、存储引擎、表函数、字典等清单,主要用于审计与生态分析。

四、排障场景速查

复制代码
谁在打集群?        initial_user, initial_address, http_user_agent, os_user
哪条 SQL 慢?       query, query_duration_ms, read_rows, read_bytes
为什么慢?          read_rows vs result_rows(读放大), ProfileEvents, partitions
哪张表遭殃?        tables, partitions, databases
失败的查询?        type != 'QueryFinish' → exception, exception_code
去重统计次数?      initial_query_id, normalized_query_hash
同类查询频率?      normalized_query_hash GROUP BY count()
某次请求全链路?    log_comment 或 initial_query_id

CPU 尖刺/负载热点归因路径:

复制代码
CPU 尖刺时间点 t
   ├─ 对齐慢查询分钟桶 → t 处 cnt/total_sec 有尖峰?
   │     ├─ 是 → 查该分钟 query_log 明细(按 read_rows 排序)→ 定位 SQL 和发起方
   │     └─ 否 → system.merges / system.mutations → 后台任务嫌疑
   └─ 都否 → 主机层面(其他进程/宿主干扰)

五、常用巡检 SQL

5.1 上一个整点小时的慢查询明细(>10s)

复制代码
SELECT 
    hostName() AS node,
    event_time,
    query_kind,
    query_duration_ms,
    read_rows,
    formatReadableSize(read_bytes) AS io,
    substring(query, 1, 1000) AS sql
FROM clusterAllReplicas('default_cluster', system.query_log)
WHERE event_time >= toStartOfHour(now()) - INTERVAL 1 HOUR
  AND event_time <  toStartOfHour(now())
  AND type = 'QueryFinish'
  AND query_duration_ms > 10000
ORDER BY query_duration_ms DESC
LIMIT 2000;

5.2 最近 24 小时负载最重的 30 个分钟(含全部查询)

复制代码
SELECT toStartOfMinute(event_time) AS minute,
       count()                                   AS queries,
       round(sum(query_duration_ms)/1000, 1)     AS total_query_sec,
       round(max(query_duration_ms)/1000, 1)     AS max_query_sec,
       formatReadableQuantity(sum(read_rows))    AS read_rows
FROM clusterAllReplicas('default_cluster', system.query_log)
WHERE event_date >= today() - 1
  AND event_time >= now() - INTERVAL 24 HOUR
  AND type = 'QueryFinish'
GROUP BY minute
ORDER BY total_query_sec DESC
LIMIT 30;

结果解读:

  • total_query_sec 高 + queries 少 + max 高 → 个别重型查询拖累

  • total_query_sec 高 + queries 多 + max 低 → 小查询风暴

  • top 分钟集中在固定时刻 → 定时任务扎堆,需错峰

5.3 定位热点时刻的元凶明细

复制代码
SELECT hostName() AS node, event_time, user, initial_user,
       query_id, initial_query_id, query_duration_ms, read_rows,
       formatReadableSize(read_bytes) AS io,
       substring(query, 1, 500) AS sql
FROM clusterAllReplicas('default_cluster', system.query_log)
WHERE event_date = toDate('尖刺日期')
  AND event_time >= '尖刺时间-2分钟' AND event_time <= '尖刺时间+1分钟'
  AND type = 'QueryFinish'
ORDER BY read_rows DESC
LIMIT 100;

六、使用注意

  1. 副本重复计数:clusterAllReplicas 会把同一条分布式查询在各节点的执行记录都计入。看明细是特性;做条数/耗时统计需按 initial_query_id 去重或改用 cluster()。

  2. 只统计正常结束:QueryFinish 排除被 kill/报错的查询;要含失败的用 type IN ('QueryFinish','ExceptionWhileProcessing') 并补 exception 列。

  3. 落盘延迟:query_log 异步 flush(默认数秒级),刚结束的查询可能查不到;定时巡检放整点后 1~2 分钟执行更稳。

  4. 分区裁剪:务必带 event_date 条件(如 event_date >= today() - 1),否则全表扫描。

  5. 大表 COUNT(*) 是慢查询常见根因:非精确场景可用 system.parts 元数据计数(毫秒级):

    SELECT table, sum(rows) AS total_rows, formatReadableSize(sum(bytes_on_disk)) AS size
    FROM clusterAllReplicas('default_cluster', system.parts)
    WHERE active AND database = 'xxx' AND table LIKE 'xxx%'
    GROUP BY table;

相关推荐
再吃一根胡萝卜1 小时前
把本地 RAG 问答做"准":多轮指代消解 + 云端语义向量 + TXT 章节切分
后端
怕浪猫2 小时前
2026年9月面18个后端
后端·面试·github
IT_陈寒2 小时前
Vue 这个响应式陷阱,我的头发都掉没了
前端·人工智能·后端
谁在黄金彼岸2 小时前
trae的gitee mcp莫名错误
后端
前端的日常2 小时前
一个人+AI做情侣食谱小程序,30天纯赚
前端·javascript·后端
再吃一根胡萝卜2 小时前
给 RAG 问答加上「多轮指代消解」:让"他 / 这个"不再翻车
后端
Wx-bishekaifayuan3 小时前
springboot生活易购超市管理系统54086-计算机课程设计、毕业设计
spring boot·后端·python·django·课程设计·express·旅游
lizhongxuan3 小时前
VirtIO:虚拟机的磁盘、网络与内存是怎样工作的
后端
计算机毕设定制辅导-无忧学长4 小时前
《基于Spring Boot的减脂轻食购物网站》
java·vue.js·spring boot·后端·减脂轻食购物网站