LOGO OA教程 ERP教程 模切知识交流 PMS教程 CRM教程 开发文档 其他文档  
 
网站管理员

以3个数据库为例,从抓包到优化,入门如何排查慢SQL?

admin
2025年9月8日 19:1 本文热度 91

我们肯定遇到过这样的糟心情况:系统功能明明没问题,但就是卡得让人抓狂,刷新半天才加载出来,我们查来查去最后揪出元凶“慢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的原因——是没走索引?索引失效?还是数据量太大?
  • 优化:针对性改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_sent:返回100行数据;
  • Rows_examined:扫描了10万行数据(重点!扫描行数远大于返回行数,大概率没走索引);
  • 最后一行就是具体的慢SQL。

如果日志太大,直接打开费劲,用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个字段idtypekeyrowsExtra,其他字段暂时不用管。

2、执行计划关键字段解读(新手必看)

我们先看优化前的执行计划:

id
select_type
table
type
possible_keys
key
key_len
ref
rows
Extra
1
SIMPLE
order_info
ALL
NULL
NULL
NULL
NULL
100000
Using where

逐个解释:

  • 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)联合索引后的执行计划:

id
select_type
table
type
possible_keys
key
key_len
ref
rows
Extra
1
SIMPLE
order_info
range
idx_user_create
idx_user_create
5
NULL
100
Using where

变化很明显:type变成range(范围查询,不错),key用到了新建的索引,rows从10万降到100——扫描行数大幅减少,这样执行起来自然变快。

3、慢SQL常见“病因”及执行计划表现

通过执行计划,我们能快速定位慢SQL的问题所在:

  • 没建索引type=ALLkey=NULLrows很大;
  • 索引失效possible_keys有值,但key=NULL(比如where用了!=、左模糊%xxx、函数date(create_time));
  • 索引建得不对:比如只给user_id建索引,没加create_timetype可能是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);

建索引的原则

  • 我们优先给wherejoinorder 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_202401order_info_202402),查询时只查对应月份的表;
  • 调优参数:增大innodb_buffer_pool_size(InnoDB缓存池,建议设为服务器内存的50%-70%),让更多数据放内存,减少磁盘IO。
3、验证优化效果

改完后我们必须验证优化效果,确保真的变快:

  • 执行原SQL,看执行时间是否降到阈值内(如:从3.5秒降到0.01秒);
  • 我们再用EXPLAIN看执行计划,确认type变好、key有值、rows减少;
  • 查看慢查询日志,确认这条SQL不再被记录。

三、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一样,慢的根源);
  • cost:预估成本(0-1000毫秒);
  • 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只查ageregister_timecustomer_name三个字段,我们可以把customer_name加入索引的INCLUDE子句,形成“覆盖索引”,查询时不用回表查原表,直接从索引拿数据:

create index idx_age_register_cover on customer(age, register_time) include (customer_name);
4、验证优化效果

和MySQL一样,验证步骤:

  • 执行原SQL,我们看执行时间是否降到阈值内;
  • 我们用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”(逻辑读次数,越高越慢)。

跟踪结果示例

TextData
Duration(微秒)
Reads
select * from product where category_id = 4 and price > 100;
3500000
15000

这条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、索引不是越多越好,合理才是关键

索引能加速查询,但会减慢插入、更新、删除(因为要维护索引)。建索引的原则

  • 只给wherejoinorder bygroup by中的字段建索引;
  • 联合索引遵循“最左前缀原则”,把过滤性强的字段放前面(比如where a=1 and b>10a的过滤性比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),会导致后续查询用坏的执行计划。

解决办法

  • MySQL:
    • 优先尝试确保统计信息准确 (ANALYZE TABLE table_name);
    • 5.7及以上版本:优先使用 USE INDEX (index_name)提示优化器。仅在确认优化器持续错误选择时,才考虑使用 FORCE INDEX (index_name)强制使用索引;
    • 8.0版本:可调整优化器开关 (如:SET SESSION optimizer_switch = 'condition_fanout_filter=off') 或使用优化器提示;
  • PG:用prepare语句生成专用执行计划;
  • 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、诊断(看):通过执行计划(EXPLAINEXPLAIN ANALYZE、实际执行计划)分析瓶颈,判断是全表扫、索引失效还是统计信息过时;
3、优化(改):优先建合适的索引(联合索引、覆盖索引),再优化SQL写法(避免select *、优化where条件),最后考虑分表分区和参数调优;
4、验证(测):重新执行SQL看执行时间,检查执行计划是否变好,确认慢SQL不再被监控工具捕获。

慢SQL排查不是“玄学”,而是“实操性很强的技术”,多练几个实际案例,熟悉执行计划的解读,我们会发现“搞定慢SQL”其实很简单。最后提醒一句:预防永远比排查更重要,开发时我们要养成建索引、优化SQL的习惯,后续会少很多麻烦!


阅读原文:原文链接


该文章在 2025/9/9 16:30:46 编辑过
关键字查询
相关文章
正在查询...
点晴ERP是一款针对中小制造业的专业生产管理软件系统,系统成熟度和易用性得到了国内大量中小企业的青睐。
点晴PMS码头管理系统主要针对港口码头集装箱与散货日常运作、调度、堆场、车队、财务费用、相关报表等业务管理,结合码头的业务特点,围绕调度、堆场作业而开发的。集技术的先进性、管理的有效性于一体,是物流码头及其他港口类企业的高效ERP管理信息系统。
点晴WMS仓储管理系统提供了货物产品管理,销售管理,采购管理,仓储管理,仓库管理,保质期管理,货位管理,库位管理,生产管理,WMS管理系统,标签打印,条形码,二维码管理,批号管理软件。
点晴免费OA是一款软件和通用服务都免费,不限功能、不限时间、不限用户的免费OA协同办公管理系统。
Copyright 2010-2025 ClickSun All Rights Reserved