我们肯定遇到过这样的糟心情况:系统功能明明没问题,但就是卡得让人抓狂,刷新半天才加载出来,我们查来查去最后揪出元凶“慢SQL”。它就像系统里的“隐形拖油瓶”,不及时处理,轻则影响用户体验,重则直接导致连接池耗尽、系统宕机。今天本来准备把“慢SQL排查”一文讲透,准备针对MySQL、PostgreSQL、SQL Server、Oracle和MongoDB(类SQL查询场景)五个数据库,一步步带实操,保证看完我们就能上手解决问题,但因为时间原因(等找到工作上班了,估计时间更紧,好在前面读商业分析和几段实习时有时间积累资料),只好先以MySQL、PostgreSQL、SQL Server三个数据库为例,简单介绍一下慢SQL排查的基本流程,这样我们可以先上手,以后再慢慢进阶。(所有代码块或表格均可左右滚动)
一、先搞懂慢SQL的“来龙去脉”
1、什么是慢SQL?没有绝对标准,看业务场景
“慢”是相对的,不是说超过1秒就一定慢。比如电商秒杀场景,0.5秒没返回就叫慢;但后台管理系统,3秒返回可能也能接受。通常我们会给数据库设“阈值” (如:2秒),超过这个时间的SQL就归为“慢SQL”,我们需重点监控。
2、慢SQL为啥会拖垮系统?主要是因为“资源抢占”
数据库处理SQL时,会占用连接、CPU、内存、磁盘IO等等资源。一条慢SQL可能会长时间霸占这些资源:比如全表扫描时,磁盘IO疯狂读写,其他SQL只能跟在后面排队;连接被占满后,新请求直接报“连接超时”——这就是为啥一条慢SQL能搞崩整个系统。
3、排查慢SQL的逻辑:“定位→诊断→优化→验证”
不管我们用啥数据库,我们排查慢SQL的基本流程就这四步:
- 定位:先把超过阈值的慢SQL“抓”出来,明确要优化的目标;
- 诊断:通过执行计划分析慢SQL的原因——是没走索引?索引失效?还是数据量太大?
- 验证:改完后我们需在非生产环境先验证(避免影响线上业务),测执行时间是否降至阈值内,看执行计划是否优化(如:从全表扫变为索引扫);验证通过后灰度发布到生产环境,持续观察1-2个业务周期,我们确认无新问题(如:索引导致的写入性能下降),才算优化完全生效。
二、MySQL:最常用!从“抓SQL”到“调优”的慢SQL排查流程
MySQL在互联网行业最常用,排查分析工具也最成熟,主要靠“慢查询日志”抓SQL,用“EXPLAIN
”诊断问题,我们按流程一步步来。
第一步:定位慢SQL:开启慢查询日志“抓现行”
MySQL默认关闭慢查询日志(怕占性能),我们排查慢SQL时必须手动打开,有“临时开启”和“永久开启”两种方式,我们按需选择。
1、先查当前慢查询配置(SQL命令)
我们打开MySQL客户端(Navicat、命令行mysql -u root -p
都行),执行下面的命令,摸清当前设置:
show variables like '%slow_query_log%'; -- 查看慢查询日志是否开启
show variables like 'long_query_time'; -- 查看慢查询阈值(默认10秒,太长了)
show variables like '%log_output%'; -- 查看日志输出方式(file=文件,table=数据库表)
执行后我们会看到类似结果:
slow_query_log = OFF
(未开启,需改成ON)long_query_time = 10.000000
(阈值10秒,我们建议改成2秒)log_output = FILE
(输出到文件,这种方式最方便查看)
2、临时开启慢查询(适合应急排查)
如果我们不想改配置文件重启MySQL,用下面的命令临时开启(重启后失效):
set global slow_query_log = ON; -- 开启慢查询日志
set global long_query_time = 2; -- 设阈值为2秒(新连接才生效,旧连接要重连)
set global slow_query_log_file = '/var/lib/mysql/localhost-slow.log'; -- 指定日志路径(Linux为例)
set global log_queries_not_using_indexes = ON; -- 可选:记录未走索引的SQL(即使没超阈值),注意:生产环境谨慎开启!大量小表无索引查询会导致日志暴增,占用磁盘空间
3、永久开启慢查询(适合生产环境)
我们改配置文件(Linux是/etc/my.cnf
,Windows是my.ini
),在[mysqld]
节点下加配置,重启后永久生效:
[mysqld]
slow_query_log = ON -- 开启慢查询
slow_query_log_file = /var/lib/mysql/localhost-slow.log -- 日志文件路径
long_query_time = 2 -- 阈值2秒
log_queries_not_using_indexes = ON -- 记录未用索引的SQL
log_output = FILE -- 输出到文件
改完保存,重启MySQL(Linux:service mysqld restart
;Windows:服务里重启),再用第一步的“show variables
”命令确认配置生效。
4、从日志里找慢SQL:主要信息别放过
日志文件里的内容长这样,我们挑重点来看:
# Time: 2024-05-20T10:30:00Z
# User@Host: root[root] @ localhost [] Id: 1234
# Query_time: 3.500000 Lock_time: 0.000100 Rows_sent: 100 Rows_examined: 100000
SET timestamp=1684583400;
select * from order_info where user_id = 123 and create_time > '2024-01-01';
主要字段解读:
Query_time
:执行时间3.5秒,超过2秒阈值,被记录;Lock_time
:锁等待时间0.0001秒,说明不是锁的问题;Rows_examined
:扫描了10万行数据(重点!扫描行数远大于返回行数,大概率没走索引);
如果日志太大,直接打开费劲,用MySQL自带的mysqldumpslow
工具分析:
- 看执行时间最长的10条:
mysqldumpslow -s t -t 10 /var/lib/mysql/localhost-slow.log
- 看扫描行数最多的10条:
mysqldumpslow -s r -t 10 /var/lib/mysql/localhost-slow.log
(注意:-s r
是按 Rows_sent
(返回行数) 排序,不是 Rows_examined
(扫描行数)。如需分析扫描行数,需直接查看慢日志文件或使用 pt-query-digest
等工具) - 看某个表的慢SQL:
mysqldumpslow -g 'order_info' /var/lib/mysql/localhost-slow.log
第二步:诊断慢SQL——用EXPLAIN看“执行计划”
抓到慢SQL后不能瞎改,我们得先搞懂MySQL是怎么执行它的,这就需要“执行计划”。用EXPLAIN
命令就能生成,比如针对上面的慢SQL:
1、生成执行计划(SQL命令)
EXPLAIN select * from order_info where user_id = 123 and create_time > '2024-01-01';
执行后返回一张表,我们重点看5个字段:id
、type
、key
、rows
、Extra
,其他字段暂时不用管。
2、执行计划关键字段解读(新手必看)
我们先看优化前的执行计划:
逐个解释:
- id:查询序号,单表查询就是1,多表联查时序号越大越先执行;
- type:连接类型,判断效率的核心!从好到差排序:
system > const > eq_ref > ref > range > index > ALL
。这里是ALL
(全表扫描),最差的情况,难怪慢; - possible_keys:MySQL觉得可能用的索引(但不一定真用);
- key:实际用到的索引(这里是
NULL
,说明没用到任何索引); - rows:预估扫描行数(10万行,和日志里的
Rows_examined
一致); - Extra:额外信息,
Using where
说明用了where过滤,但没走索引,还是全表扫。
我们再看给order_info
表建了(user_id, create_time)
联合索引后的执行计划:
变化很明显:type
变成range
(范围查询,不错),key
用到了新建的索引,rows
从10万降到100——扫描行数大幅减少,这样执行起来自然变快。
3、慢SQL常见“病因”及执行计划表现
通过执行计划,我们能快速定位慢SQL的问题所在:
- 没建索引:
type=ALL
,key=NULL
,rows
很大; - 索引失效:
possible_keys
有值,但key=NULL
(比如where用了!=
、左模糊%xxx
、函数date(create_time)
); - 索引建得不对:比如只给
user_id
建索引,没加create_time
,type
可能是ref
,但rows
还是比较大; - 用了select *:
Extra
可能出现Using filesort
(排序没走索引)或Using temporary
(用临时表,比如group by
)。
第三步:优化慢SQL,针对性解决问题
找到慢SQL“病因”后,优化就有的放矢了,针对上面的例子,我们一步一步来。
1、重点优化:建对索引
索引是优化慢SQL最有效的手段,但我们不能乱建。针对select * from order_info where user_id = 123 and create_time > '2024-01-01'
,正确的做法是建联合索引(因为两个字段都在where条件里):
create index idx_user_create on order_info(user_id, create_time);
建索引的原则:
- 我们优先给
where
、join
、order by
里的字段建索引; - 联合索引,我们要遵循“最左前缀原则”(比如(a,b,c)的索引,能匹配a(等值查询)、a+b(a等值 +b等值 / 范围)、a+b+c(a等值 +b等值 +c等值 / 范围),但不能匹配b、b+c、a+c(a等值但跳过b查c也无法命中完整索引));
- 我们别给每个字段都建索引(会增加插入/更新的成本)。
2、其他常见优化手段
- 避免select *:我们只查需要的字段,比如
select order_id, pay_time from order_info ...
,减少数据传输; - 优化where条件:我们把
like '%2024%'
改成like '2024%'
(右模糊能走索引),把date(create_time) = '2024-01-01'
改成create_time between '2024-01-01 00:00:00' and '2024-01-01 23:59:59'
; - 拆分大表:如果
order_info
有1000万行,光建索引不够,我们按时间分表(如:order_info_202401
、order_info_202402
),查询时只查对应月份的表; - 调优参数:增大
innodb_buffer_pool_size
(InnoDB缓存池,建议设为服务器内存的50%-70%),让更多数据放内存,减少磁盘IO。
3、验证优化效果
改完后我们必须验证优化效果,确保真的变快:
- 执行原SQL,看执行时间是否降到阈值内(如:从3.5秒降到0.01秒);
- 我们再用
EXPLAIN
看执行计划,确认type
变好、key
有值、rows
减少;
三、PostgreSQL:开源“优等生”,工具灵活高效
PostgreSQL(简称PG)在政企、金融行业用得较多,排查慢SQL逻辑和MySQL一致,但工具不同:我们用pg_stat_statements
插件抓SQL,用EXPLAIN ANALYZE
诊断,比MySQL的EXPLAIN
更精准(因为会实际执行SQL)。
第一步:定位慢SQL,用pg_stat_statements插件
PG没有“慢查询日志开关”,pg_stat_statements
是我们最常用的工具,它能记录所有SQL的执行统计信息(执行次数、总耗时、平均耗时等)。
1、启用插件(必须改配置文件)
编辑PG的配置文件(Linux路径:/etc/postgresql/14/main/postgresql.conf
,14是版本号),我们在[postgresql]
节点下加:
shared_preload_libraries = 'pg_stat_statements' -- 加载插件,若已有其他插件(如:pg_buffercache),用逗号分隔:'pg_stat_statements,pg_buffercache'
pg_stat_statements.track = all -- 跟踪所有SQL
pg_stat_statements.max = 10000 -- 最多记录1万条SQL
log_min_duration_statement = 2000 -- 可选:超过2秒的SQL记录到日志
保存后我们重启PG(service postgresql restart
),然后登录PG客户端(psql -U postgres -d 数据库名
),执行下面的命令创建插件(每个数据库都要执行一次):
create extension if not exists pg_stat_statements;
2、查询慢SQL(SQL命令)
执行下面的SQL,按“平均执行时间”排序,我们找出最慢的SQL:
select
queryid, -- SQL唯一ID
query, -- SQL语句
calls, -- 执行次数
total_time, -- 总执行时间(毫秒)
mean_time, -- 平均执行时间(毫秒)
rows -- 返回行数
from pg_stat_statements
where mean_time > 2000 -- 只看平均超过2秒的
order by mean_time desc;
返回结果示例:
queryid: 123456789
query: select * from customer where age > 30 and register_time > '2024-01-01';
calls: 50
mean_time: 3200 -- 平均3.2秒
rows: 200
这条就是要优化的慢SQL。
3、备选:用日志定位(插件用不了时)
如果装不了插件,我们用PG的日志文件:在postgresql.conf
里开启日志:
logging_collector = on -- 开启日志收集
log_directory = 'pg_log' -- 日志目录(默认在PG数据目录下)
log_filename = 'postgresql-%Y-%m-%d.log' -- 日志文件名格式
log_min_duration_statement = 2000 -- 超过2秒的SQL记录
重启后,日志里会有类似记录:
2024-05-20 11:00:00 UTC:postgres@testdb:[12345]:LOG: duration: 3200.123 ms statement: select * from customer where age > 30 and register_time > '2024-01-01';
duration
就是执行时间,后面是SQL语句。
第二步:诊断慢SQL,用EXPLAIN ANALYZE看执行计划
PG的EXPLAIN ANALYZE
比MySQL的EXPLAIN
更实用,因为它会实际执行SQL,返回“预估”和“真实”的执行情况对比,它不会骗人。
1、生成执行计划(SQL命令)
针对上面的慢SQL:
EXPLAIN ANALYZE select * from customer where age > 30 and register_time > '2024-01-01';
2、执行计划解读(简化版)
优化前的执行计划:
Seq Scan on customer (cost=0.00..1000.00 rows=200 width=100) (actual time=0.01..3200.00 rows=200 loops=1)
Filter: ((age > 30) AND (register_time > '2024-01-01 00:00:00'::timestamp without time zone))
Rows Removed by Filter: 99800
Planning Time: 0.10 ms
Execution Time: 3200.12 ms
主要信息:
Seq Scan
:全表扫描(和MySQL的ALL
一样,慢的根源);actual time
:真实执行时间(3200毫秒);Rows Removed by Filter
:过滤掉9980行,说明全表扫了10万行才找到200行符合条件的数据。
优化后(我们建了(age, register_time)
联合索引)的执行计划:
Index Scan using idx_age_register on customer (cost=0.29..200.00 rows=200 width=100) (actual time=0.02..10.00 rows=200 loops=1)
Index Cond: ((age > 30) AND (register_time > '2024-01-01 00:00:00'::timestamp without time zone))
Planning Time: 0.15 ms
Execution Time: 10.10 ms
变化一目了然:Seq Scan
变成了Index Scan
(索引扫描),执行时间从3200毫秒骤降到10毫秒。
3、PG特有的“坑”:表空洞导致的慢查询
PG的表默认是“堆表”结构,删除数据后不会立即释放空间,会留下“空洞”(无效数据块)。如果表经常被删改,空洞越来越多,这样就会导致全表扫时扫描大量无效数据,最终执行变慢。
查空洞大小的命令:
-- 一般用途
SELECT
psut.relname AS table_name,
pg_size_pretty(pg_relation_size(psut.relid)) AS table_size,
pg_size_pretty(
CASE WHEN (psut.n_live_tup + psut.n_dead_tup) = 0
THEN 0
ELSE psut.n_dead_tup * pg_relation_size(psut.relid) / (psut.n_live_tup + psut.n_dead_tup)
END
) AS dead_tuple_size
FROM pg_stat_user_tables psut
JOIN pg_class pc ON psut.relid = pc.relid
WHERE psut.relname = 'customer'
AND pc.relnamespace = (SELECT oid FROM pg_namespace WHERE nspname = 'public'); -- schema筛选,避免跨schema匹配
-- 生产环境
SELECT
quote_ident(psut.schemaname) || '.' || quote_ident(psut.relname) AS full_table_name,
pg_size_pretty(pg_total_relation_size(psut.relid)) AS total_size,
pg_size_pretty(pg_relation_size(psut.relid)) AS table_size,
psut.n_live_tup AS live_tuples,
psut.n_dead_tup AS dead_tuples,
pg_size_pretty(
CASE
WHEN (psut.n_live_tup + psut.n_dead_tup) = 0 THEN 0::bigint
ELSE (psut.n_dead_tup * pg_relation_size(psut.relid) / (psut.n_live_tup + psut.n_dead_tup))
END
) AS estimated_dead_size
FROM pg_stat_user_tables psut
WHERE psut.relname = 'customer'
AND psut.schemaname = 'public'; -- 明确指定 schema
如果dead_tuple_size
很大,执行VACUUM ANALYZE customer;
清理空洞并更新统计信息,能明显提升查询速度。
第三步:优化慢SQL,PG的特色优化手段
PG的基础优化(建索引、优化SQL写法)和MySQL一致,但有几个特色功能能进一步提升效率。
1、基础优化:建联合索引
针对慢SQLselect * from customer where age > 30 and register_time > '2024-01-01';
,建联合索引:
create index idx_age_register on customer(age, register_time);
2、特色优化1:部分索引(只索引符合条件的数据)
如果业务中90%的查询都是“age>30”,我们可以建“部分索引”,只索引age>30的数据,索引体积更小,查询更快:
create index idx_age_30_register on customer(register_time) where age > 30;
3、特色优化2:覆盖索引(避免回表查询)
如果SQL只查age
、register_time
、customer_name
三个字段,我们可以把customer_name
加入索引的INCLUDE
子句,形成“覆盖索引”,查询时不用回表查原表,直接从索引拿数据:
create index idx_age_register_cover on customer(age, register_time) include (customer_name);
4、验证优化效果
和MySQL一样,验证步骤:
- 我们用
EXPLAIN ANALYZE
确认执行计划为Index Scan
,无全表扫描; - 我们查看
pg_stat_statements
,确认mean_time
大幅下降。
四、SQL Server:微软系“实力派”,图形化工具友好
SQL Server在.NET开发的企业级项目中很常见,它的排查分析工具更偏向图形化,操作简单,适合我们新手。主要用“SQL Server Profiler/Extended Events”抓SQL,用“实际执行计划”诊断问题。
第一步:定位慢SQL,有两种常用工具
SQL Server定位慢SQL有两种主流方式:图形化的“SQL Server Profiler”(直观)和轻量的“Extended Events”(高效,推荐生产环境用)。
方式1:SQL Server Profiler(适合新手入门)
1、我们打开“SQL Server Management Studio (SSMS)”,连接数据库后,点击菜单栏“工具”→“SQL Server Profiler”;
2、新建跟踪:连接数据库实例,我们在“跟踪属性”窗口切换到“事件选择”页,勾选以下事件:
- TSQL → SQL:BatchCompleted(批处理SQL执行完成)
- Stored Procedures → RPC:Completed(存储过程执行完成)
3、设置筛选条件:点击“列筛选器”,我们找到“Duration”(执行时间),在“大于或等于”中输入“2000000”(SQL Server 2008及以后版本,单位是微秒) 或 2000(SQL Server 2005及更早版本,单位是毫秒);
4、我们点击“运行”开始跟踪,超过2秒的SQL会实时显示,重点看“TextData”(SQL语句)、“Duration”(执行时间)、“Reads”(逻辑读次数,越高越慢)。
跟踪结果示例:
| | |
---|
select * from product where category_id = 4 and price > 100; | | |
这条SQL执行3.5秒,逻辑读15000次,是慢SQL。
方式2:Extended Events(生产环境推荐)
Profiler比较耗资源,生产环境我们推荐用Extended Events(轻量级,性能影响小):
1、在SSMS中展开“管理→Extended Events→会话”,右键“新建会话”;
2、会话名称:比如“SlowSQL_Trace”;
3、选择事件:在“事件库”中我们搜索并勾选“sqlserver.sql_statement_completed”;
4、添加筛选器:点击“筛选器”,选“duration”,运算符“大于”,值填“2000000”;
5、配置数据存储:选择“事件文件”,指定保存路径,点击“确定”;
6、右键新建的会话,点击“启动”,我们即可开始捕获慢SQL。
第二步:诊断慢SQL,用“实际执行计划”
SQL Server的执行计划分析非常直观,不用我们写命令,点一下按钮就行。
1、生成实际执行计划(图形化操作)
(1)在SSMS中粘贴慢SQL:select * from product where category_id = 4 and price > 100;
;
(2)点击工具栏的“包括实际执行计划”按钮(快捷键Ctrl+M);
(3)执行SQL(F5),会自动弹出“执行计划”标签页。
2、执行计划关键图标解读
执行计划用图标展示流程,我们新手只要记住几个核心图标:
- 表扫描(Table Scan):红色警告图标,代表全表扫,必须优化;
- 索引扫描(Index Scan):扫描整个索引,效率中等,适合小索引;
- 索引查找(Index Seek):绿色图标,精准定位索引,效率最高;
- 键查找(Key Lookup):黄色警告图标,代表走了索引但需要回表查数据,可通过“覆盖索引”优化。
优化前的执行计划:显示“表扫描(product)”,占总执行成本的98%,且有黄色警告“缺少索引建议”。
优化后的执行计划:显示“索引查找(非聚集)”,占总执行成本的5%,无警告。
3、执行计划中的主要指标
除了图标,我们还要看“属性”面板(右键执行计划图标→“属性”):
- 逻辑读(Logical Reads):从内存读取数据页的次数,越高越慢(优化前15000,优化后100);
- 执行时间(Actual Time):真实执行时间(优化前3500毫秒,优化后10毫秒);
- 估计行数 vs 实际行数:如果差距很大,说明统计信息过时,需要更新(后面会讲)。
第三步:优化慢SQL,SQL Server的特色技巧
SQL Server的优化思路和前两者一致,但有几个特色功能需要注意。
1、基础优化:建非聚集索引
针对慢SQL,我们建(category_id, price)
的非聚集索引:
create nonclustered index idx_product_cat_price on product(category_id, price);
2、特色优化1:覆盖索引(解决“键查找”问题)
如果建了上面的索引后,执行计划仍有“键查找”(因为SQL用了select *
,需要查索引外的字段),我们可以用INCLUDE
子句把需要的字段加入索引,形成覆盖索引:
create nonclustered index idx_product_cat_price_cover on product(category_id, price)
include (product_name, stock, create_time); -- 把select需要的字段加进来
3、特色优化2:更新统计信息
SQL Server的执行计划依赖“统计信息”(表的行数、字段值分布等),如果统计信息过时,会生成错误的执行计划(比如该走索引却走全表扫)。
更新统计信息的命令:
-- 更新单个表的统计信息(使用默认抽样,我们推荐常规使用)
UPDATE STATISTICS dbo.product;
-- UPDATE STATISTICS dbo.product WITH FULLSCAN; -- (资源消耗大,仅用于关键表或抽样不准时)
-- UPDATE STATISTICS dbo.product WITH SAMPLE 50 PERCENT; -- (指定抽样比例)
-- 更新整个数据库的统计信息(使用各表原有的采样率,谨慎使用)
EXEC sp_updatestats;
-- EXEC sp_updatestats 'resample'; -- (较新版本的显式写法,效果同上)
4、特色优化3:索引提示(强制指定索引)
如果SQL Server自动选择的索引不好,我们可以用WITH(INDEX(索引名))
强制指定索引(请谨慎使用,仅在确认索引正确时用):
select * from product with(index(idx_product_cat_price_cover))
where category_id = 4 and price > 100;
5、验证优化效果
- 执行原SQL,我们看“消息”面板中的“逻辑读”和“执行时间”是否大幅下降;
- 用Extended Events跟踪,确认该SQL不再被捕获为慢SQL。
五、通用进阶:排查慢SQL时的避坑指南
不管用什么数据库,排查慢SQL时都容易踩坑,提前避开我们可以少走很多弯路。
1、别只看SQL写法,执行计划才是“真相”
我们很多人觉得“SQL写得简单就不会慢”,比如select * from user where id = 1
,如果id
没建索引,执行计划还是全表扫。永远以执行计划为准,我们不要凭感觉判断。
2、索引不是越多越好,合理才是关键
索引能加速查询,但会减慢插入、更新、删除(因为要维护索引)。建索引的原则:
- 只给
where
、join
、order by
、group by
中的字段建索引; - 联合索引遵循“最左前缀原则”,把过滤性强的字段放前面(比如
where a=1 and b>10
,a
的过滤性比b
强,就建(a,b)
); - 定期删除无用索引(用
sys.dm_db_index_usage_stats
(SQL Server)、pg_stat_user_indexes
(PG)查看索引使用情况)。
3、我们要警惕“参数嗅探”导致的慢查询
数据库会缓存第一条执行SQL的执行计划,后续相同SQL(参数不同)会复用该计划。如果第一条SQL的参数过滤性差(比如where id=1000
,而大部分查询是id=1
),会导致后续查询用坏的执行计划。
解决办法:
- 优先尝试确保统计信息准确 (
ANALYZE TABLE table_name
); - 5.7及以上版本:优先使用
USE INDEX (index_name)
提示优化器。仅在确认优化器持续错误选择时,才考虑使用 FORCE INDEX (index_name)
强制使用索引; - 8.0版本:可调整优化器开关 (如:
SET SESSION optimizer_switch = 'condition_fanout_filter=off'
) 或使用优化器提示;
- SQL Server:用
option(recompile)
重新编译执行计划。
4、大表优化不能只靠索引,分表分区是关键
当表数据量超过1000万行,光靠索引效果有限,需要分表或分区:
- 按时间 (如:
create_time
):order_202401
。优点:易于管理,适合时间范围查询。缺点:若按 user_id
查询,需扫描所有分表,性能差。需配套路由表或全局二级索引; - 按业务键哈希 (如:
user_id):order_%
。优点:同一用户的数据通常在同一个分表,适合点查询。缺点:范围查询和跨用户聚合查询性能差; - 混合策略:常见于大型系统,比如先按
user_id
哈希分库,再在库内按时间分区;
- 垂直分表:按列拆分(比如用户表拆成
user_base
(基本信息)和user_detail
(详细信息)); - 分区表:PG、SQL Server支持原生分区表,把大表拆成多个小分区,查询时只扫描对应分区。
5、监控比事后排查更重要
定期监控慢SQL,能让我们提前发现问题,避免系统故障:
- MySQL:开启慢查询日志,每天用
mysqldumpslow
分析; - PG:用
pg_stat_statements
生成日报,关注mean_time
上升的SQL; - SQL Server:用“SQL Server Agent”定时生成性能报告,监控慢查询趋势。
六、总结:排查慢SQL的基本流程
不管是MySQL、PG还是SQL Server,只要我们记住这套“四步流程”,90%的慢SQL问题都能解决:
1、定位(抓):用数据库自带工具(慢查询日志、pg_stat_statements、Extended Events)把超过阈值的慢SQL找出来,明确优化目标;
2、诊断(看):通过执行计划(EXPLAIN
、EXPLAIN ANALYZE
、实际执行计划)分析瓶颈,判断是全表扫、索引失效还是统计信息过时;
3、优化(改):优先建合适的索引(联合索引、覆盖索引),再优化SQL写法(避免select *
、优化where
条件),最后考虑分表分区和参数调优;
4、验证(测):重新执行SQL看执行时间,检查执行计划是否变好,确认慢SQL不再被监控工具捕获。
慢SQL排查不是“玄学”,而是“实操性很强的技术”,多练几个实际案例,熟悉执行计划的解读,我们会发现“搞定慢SQL”其实很简单。最后提醒一句:预防永远比排查更重要,开发时我们要养成建索引、优化SQL的习惯,后续会少很多麻烦!
阅读原文:原文链接
该文章在 2025/9/9 16:30:46 编辑过