本节摘要:本节给一套可落地的慢查询排查流程,从看
query_log定位、到 EXPLAIN 分析、到对症下药,并附常见瓶颈和参数调优清单。
阅读完本节,你应当能够:
遇到慢查询,别急着改 SQL,按这个流程走:
system.query_log 找到这条查询的耗时、扫描行数、内存、异常。
最常见。表现是 ReadRows 巨大。根因通常是分区或主键裁剪没生效。
对策:
event_time 范围)SELECT *,显式列清单表现是 MemoryUsage 接近上限或报 Memory limit exceeded。根因是 GROUP BY 基数大或 JOIN 大表。
对策:
SET max_memory_usage = 10000000000(10GB)SET max_bytes_before_external_group_by = 5000000000SET max_bytes_before_external_join = ...uniq() 替代 count(distinct)表现是单分片快、分布式表慢。根因是分片太多、网络传输、协调节点合并。
对策:
SELECT * 到分布式表表现是查询时延波动大,CPU 高。根因是合并或 mutation 和查询争 CPU/IO。
对策:
background_pool_size、merge_tree.max_merge_select_threads| 参数 | 作用 | 调优建议 |
|---|---|---|
max_threads |
单查询线程数 | 默认核数,高并发场景调低 |
max_memory_usage |
单查询内存上限 | 大聚合调高,配合溢写 |
max_bytes_before_external_group_by |
聚合溢写磁盘阈值 | 设为内存上限的 60-70% |
max_block_size |
批处理块大小 | 默认 65536,一般不动 |
index_granularity |
granule 行数 | 默认 8192,一般不动 |
background_pool_size |
后台任务线程池 | 写入压力大时调大 |
⚠️ 常见坑:调参不是越多越好。很多参数有默认值是有道理的,乱调可能让别的场景变差。调一个参数前先想清楚它影响什么、为什么默认值不合适。能用结构优化(分区、索引、物化视图)解决的,别靠参数硬扛。
把排查变成日常而非救火,靠监控。设一个慢查询阈值(比如 10 秒),超过就记录并告警:
SELECT query_duration_ms, query, ReadRows, MemoryUsage, event_time FROM system.query_log WHERE query_duration_ms > 10000 AND type = 'QueryFinish' ORDER BY query_duration_ms DESC LIMIT 20;
定期看这张表,找出 top 慢查询逐个优化。配合第 6 章的监控体系,慢查询、合并队列、磁盘占用一起盯,集群健康度就有保障。
第 4 章结束。你已经能看懂执行计划、应用优化技术、系统排查慢查询。下一章把单机扩成分布式集群。
把调优经验沉淀成检查清单,比每次都从零排查高效得多。下面是一份按优先级排列的检查清单,配合 SQL 直接执行:
-- 1. 找出一小时内最慢的 20 条查询 SELECT query_duration_ms, read_rows, read_bytes, memory_usage, query FROM system.query_log WHERE event_time > now() - INTERVAL 1 HOUR AND type = 'QueryFinish' ORDER BY query_duration_ms DESC LIMIT 20; -- 2. 按查询特征分类:看慢查询主要卡在扫描还是聚合 SELECT if(read_rows > 10000000, 'scan_heavy', 'other') AS category, count() AS cnt, round(avg(query_duration_ms)) AS avg_ms FROM system.query_log WHERE event_time > now() - INTERVAL 1 HOUR AND type = 'QueryFinish' GROUP BY category;
清单的优先级大致是:结构问题优先于参数问题。第一看查询是否扫了太多数据(分区/主键没命中、SELECT *、函数包裹列),第二看聚合是否太重(高基数、没走物化视图),第三才轮得到调参数。大部分慢查询在前两步就能解决。
针对"扫描量大"的常见修法是把高频过滤条件固定下来。如果查询总是按 event_time 过滤,把 ORDER BY 首列设为 event_time 且按天分区,查询裁剪效果立竿见影。已经上线的表要调整排序键,可以用新增一个按新键组织的物化视图的方式渐进迁移,而不是直接改表结构。
监控和调优是闭环的:调优前记录基线(query_log 里的平均耗时),调优后对比同一指标。没有基线的调优无法判断是变好还是变差,这也是第 6 章要讲监控的原因。