MySQL梳理三:查询与优化
一、单表查询的访问方法
MySQL 的访问方法定义了查询语句的执行方式,类似于从起点到终点的路线选择。查询优化器根据条件、索引和统计信息选择最优访问方法,以最小化执行成本(I/O、CPU、内存等)。虽然查询结果相同,但不同访问方法的效率差异显著
1. const
- 描述: 通过主键或唯一二级索引与常数等值比较,定位单条记录
- 性能: 效率最高(“坐火箭”),成本为常数级别,无需扫描或回表
- 适用场景: 主键或唯一索引的等值查询,联合索引需所有列等值匹配
- 注意: 唯一索引查询 NULL 值(如
key IS NULL
)无法使用const
,因 NULL 可重复 - 示例:
SELECT * FROM table WHERE id = 100;
使用主键,定位单条记录
2. ref
- 描述: 使用普通二级索引进行等值查询,可能匹配多条记录,需回表获取完整记录
- 性能: 效率次于
const
(“坐高铁”),适合选择性高的索引,成本随匹配记录数增加 - 适用场景: 普通二级索引或联合索引最左连续列等值匹配,包括 NULL 值查询
- 示例:
SELECT * FROM table WHERE key1 = 'abc';
使用普通索引idx_key1
3. ref_or_null
- 描述: 扩展
ref
,额外查找索引列为 NULL 的记录 - 性能: 效率略低于
ref
,因需处理 NULL 检查 - 适用场景: 等值查询结合 NULL 检查,如
WHERE key1 = 'abc' OR key1 IS NULL
- 注意: 若无 NULL 值,可能降级为
ref
4. range
- 描述: 使用索引(聚簇或二级索引)进行范围查询,条件对应数轴上的区间(单点或连续区间)
- 性能: 效率依赖范围大小,选择性高时接近
ref
,需回表 - 适用场景: 支持
>
,<
,IN
,BETWEEN
,LIKE
(前缀匹配)等操作符 - 示例:
SELECT * FROM table WHERE key1 IN ('a', 'b');
生成单点区间
5. index_merge
-
描述: 使用多个二级索引合并结果,分为三种算法:
- Intersection: 取索引结果交集,适用于等值匹配或主键范围匹配,需按主键排序
- Union: 取索引结果并集,适用于 OR 连接的等值匹配
- Sort-Union: 范围查询结果按主键排序后取并集,成本略高
-
性能: 效率低于单索引查询(
range
/ref
),因涉及多索引操作和回表,但优于全表扫描 -
适用场景: 复杂条件涉及多个索引,如
WHERE key1 = 'a' OR key2 = 'b'
-
注意: 优化器根据成本选择是否使用,联合索引可替代以降低成本
6. index
- 描述: 直接扫描二级索引全部叶子节点,查询列和条件均包含在索引中,免回表
- 性能: 优于全表扫描,因索引记录小,但仍需全索引扫描
- 适用场景: 查询列和条件全在索引中,如
SELECT key1 FROM table WHERE key1 = 'a';
- 注意: 需确保查询不涉及非索引列
7. all
- 描述: 扫描整个聚簇索引,逐行检查条件
- 性能: 效率最低(“坐乌龟”),扫描全表记录,I/O 成本高
- 适用场景: 无索引可用或条件选择性低
- 示例:
SELECT * FROM table WHERE non_indexed_col = 1;
注意事项
-
二级索引与回表: 通常使用单个二级索引,先定位记录,再回表获取完整数据。非索引条件在回表后过滤
-
范围区间确定:
AND
连接取交集,OR
连接取并集- 无法使用索引的条件替换为
TRUE
,简化区间计算 - 复杂条件需逐一分析,提取有效区间
-
联合索引优化: 多列查询可通过联合索引替代
index_merge
,但需平衡其他查询对单列索引的需求 -
优化器决策: 优化器基于统计信息(
ANALYZE TABLE
更新)选择最低成本访问方法,通过合理设计索引和查询,MySQL 优化器能选择高效访问方法,显著提升查询性能
二、联表查询
联表的本质:联表查询将多个表的记录组合成笛卡尔积,通过过滤条件生成结果集,笛卡尔积是各表记录逐一匹配形成的组合,记录数为各表行数的乘积(如两表各 100 行,生成 100×100=10000 行)。MySQL 语法简单,在 FROM
子句列出表名即可,但需通过过滤条件(涉及表间比较,如 table1.col1 = table2.col2
)控制结果集规模
1. 联表查询的种类
联表查询分为内连接和外连接,二者的区别在于是否保留无匹配记录
内连接:
-
仅保留符合连接条件和过滤条件的记录组合,驱动表与被驱动表可互换
-
示例:
SELECT * FROM table1 INNER JOIN table2 ON table1.col1 = table2.col2;
仅返回匹配记录(默认的联表查询)
外连接:
-
驱动表记录即使无匹配也保留,被驱动表字段补
NULL
-
左外连接: 左表为驱动表,
SELECT * FROM table1 LEFT JOIN table2 ON ... [WHERE ...]
. -
右外连接: 右表为驱动表,
SELECT * FROM table1 RIGHT JOIN table2 ON ...
.
2. 联表查询执行过程
联表查询由优化器驱动,分为两步:
查询驱动表:
- 选择第一个表(驱动表),使用单表访问方法(如
const
,ref
,all
)执行查询,获取符合单表条件的记录 - 示例:
SELECT * FROM table1, table2 WHERE table1.col1 > 10 AND table1.col1 = table2.col2;
先查询table1
满足col1 > 10
的记录。
查询被驱动表:
- 对驱动表结果集的每条记录,查询被驱动表,应用两表条件(如
table1.col1 = table2.col2
)和被驱动表单表条件 - 被驱动表访问次数等于驱动表结果集记录数
3. 连接算法与优化
MySQL 使用以下算法执行联表查询,优化器会自己选择成本最低的方案
嵌套循环连接:
-
驱动表查询一次,被驱动表查询多次(次数等于驱动表结果集记录数)。多表连接时,上一轮结果集作为新驱动表,重复过程
-
特点: 简单但效率低,被驱动表可能多次全表扫描
-
性能: 成本随驱动表记录数和被驱动表访问方式增加
索引优化:
-
被驱动表查询可利用索引加速,参考常见访问方法
-
建议为被驱动表的关键列(如连接条件中的列)添加索引,确保选择性高,查询列表仅包含必要列以触发
index
基于块的嵌套循环连接:
- 被驱动表数据量大时,多次全表扫描导致高 I/O 成本,所以采用
join buffer
(默认 256KB,可通过join_buffer_size
调整)缓存驱动表结果集记录 - 具体过程为:先将驱动表查询列和条件放入
join buffer
,再扫描被驱动表,记录一次性与join buffer
中多条记录匹配以减少被驱动表 I/O,最佳情况为join buffer
容纳所有驱动表记录,仅需扫描被驱动表一次
三、查询成本
1. MySQL 查询成本
MySQL 查询的执行成本主要分为I/O成本和CPU成本
I/O 成本:
从磁盘加载数据或索引页面到内存的耗时,以页面(page)为基本单位,默认页面大小为 16KB,成本常数默认为 1.0
,出现在涉及表数据或索引的加载这样的场景中,例如全表扫描或索引范围查询
CPU 成本:
读取记录、检测搜索条件、排序等操作的耗时,读取并检测一条记录是否符合条件的默认成本为 0.2
(无论是否需要条件检测),主要包括条件比较、结果集排序等
成本常数:1.0
(页面读取)和 0.2
(记录检测)是默认值,硬编码在 MySQL 源码中,部分微调值(如 1.1
或 0.01
)用于调整成本估算
2. 单表查询的成本计算
MySQL 查询优化器在执行单表查询前会评估所有可能执行方案的成本,选择成本最低的方案作为执行计划:
-
根据搜索条件,找出可能使用的索引
-
计算全表扫描的成本
-
计算使用索引的成本
-
考虑索引合并
-
选择成本最低的执行方案
3. 联表的查询成本计算
总成本 = 单次访问驱动表的成本 + 驱动表扇出(驱动表查询结果的记录数) × 单次访问被驱动表的成本
当无法直接确定扇出时,优化器通过启发式规则(heuristic)估算剩余条件的过滤效果
以内连接方式举例示例:
SELECT * FROM s1 INNER JOIN s2 ON s1.key1 = s2.common_field
WHERE s1.key2 > 10 AND s1.key2 < 1000 AND s2.key2 > 1000 AND s2.key2 < 2000;
MySQL 优化器评估两种连接顺序(s1驱动s2或s2驱动s1),计算对应的总成本
简单总结:MySQL 查询优化器通过比较全表扫描和索引访问的 I/O 和 CPU 成本,选择成本最低的执行计划。联表查询中,优化器评估不同连接顺序和访问方法,结合条件过滤和统计数据提高估算准确性
四、基于规则的查询优化
MySQL 查询优化器通过基于规则的优化手段,将用户编写的复杂或低效查询语句重写为更高效的形式(包括条件化简、外连接消除和子查询优化等),以提升查询性能
1. 条件化简
MySQL 查询优化器会对查询中的搜索条件(表达式)进行化简,以减少计算复杂度和提高执行效率,主要的条件化简规则有:
移除不必要的括号
优化器会移除查询中多余的括号,简化表达式,例如:
((a = 5 AND b = c) OR ((a > c) AND (c < 5)))
被简化为:
(a = 5 AND b = c) OR (a > c AND c < 5)
常量传递
当某个列与常量等值匹配,并且与其他表达式通过 AND 连接时,优化器会将常量值传递到其他表达式中,例如:
a = 5 AND b > a
简化为:
a = 5 AND b > 5
等值传递
当多个列之间存在等值关系时,优化器会将等值条件传递,例如:
a = b AND b = c AND c = 5
简化为:
a = 5 AND b = 5 AND c = 5
移除没用的条件
对于恒为 TRUE 或 FALSE 的条件,优化器会直接移除,例如:
(a < 1 AND b = b) OR (a = 6 OR 5 != 5)
简化为:
a < 1 OR a = 6
表达式计算
对于只包含常量的表达式,优化器会在查询执行前计算其值,例如:
a = 5 + 1
简化为:
a = 6
但对于涉及复杂函数或非单独列的表达式(如 ABS(a) > 5
),优化器不会进行化简
HAVING 和 WHERE 子句合并
如果查询不包含聚合函数(如 SUM、MAX)或 GROUP BY 子句,优化器会将 HAVING 子句合并到 WHERE 子句中
2. 外连接消除
外连接(LEFT/RIGHT JOIN)与内连接(INNER JOIN)的区别在于,驱动表的记录即使在被驱动表中找不到匹配记录,也会被保留(被驱动表字段填充为 NULL)。优化器通过以下方式优化外连接:
空值拒绝
如果 WHERE 子句中指定被驱动表的列不为 NULL(如 t2.n2 IS NOT NULL
),外连接效果等价于内连接。例如:
SELECT * FROM t1 LEFT JOIN t2 ON t1.m1 = t2.m2 WHERE t2.n2 IS NOT NULL;
等价于:
SELECT * FROM t1 INNER JOIN t2 ON t1.m1 = t2.m2;
这种转换允许优化器调整连接顺序,降低查询成本
3. 子查询优化
子查询是嵌套在查询中的子查询,优化器通过物化表和半连接等策略优化其执行效率
子查询出现位置
- SELECT 子句:如
(SELECT m1 FROM t1 LIMIT 1)
- FROM 子句:作为派生表,如
(SELECT m2 + 1 AS m, n2 AS n FROM t2 WHERE m2 > 2) AS t
- WHERE 或 ON 子句:如
WHERE m1 IN (SELECT m2 FROM t2)
- ORDER BY 或 GROUP BY 子句:有但较少使用
子查询分类
按返回结果集分类:
- 标量子查询:返回单一值(如
(SELECT m1 FROM t1 LIMIT 1)
) - 行子查询:返回单条记录,包含多列(如
(SELECT m2, n2 FROM t2 LIMIT 1)
) - 列子查询:返回单列多行(如
(SELECT m2 FROM t2)
) - 表子查询:返回多行多列(如
(SELECT m2, n2 FROM t2)
)
按与外层查询关系分类:
- 不相关子查询:独立执行,不依赖外层查询值
- 相关子查询:执行依赖外层查询值,如
WHERE m1 IN (SELECT m2 FROM t2 WHERE n1 = n2)
子查询在布尔表达式中的使用
- 比较操作符(=、>、<、等):与标量子查询或行子查询结合
- IN/NOT IN、ANY/SOME、ALL:与列子查询或表子查询结合
- EXISTS/NOT EXISTS:判断子查询结果集是否为空
子查询语法注意事项
- 子查询必须用括号括起来
- SELECT 子句中的子查询必须是标量子查询
- IN/ANY/SOME/ALL 子查询不支持 LIMIT 子句
- 冗余语句(如 ORDER BY、DISTINCT、无聚合函数的 GROUP BY)会被优化器移除
- 禁止在同一语句中对表进行增删改操作的同时进行子查询
子查询执行方式与优化
标量子查询和行子查询:
- 不相关子查询:分别执行子查询和外层查询,子查询结果作为外层查询参数。
- 相关子查询:外层查询逐条记录执行,提取相关值代入子查询,判断条件是否成立。
IN 子查询优化:
-
物化表:将不相关 IN 子查询结果集写入临时表(物化表),对物化表记录去重,建立哈希索引(内存)或 B+ 树索引(磁盘),将外层查询与物化表进行内连接,由优化器评估连接顺序成本。适用于子查询结果集较大时,物化表通过索引加速 IN 判断
-
半连接:将 IN 子查询转换为半连接,仅保留外层查询表的记录,不关心被驱动表匹配的记录数。适用于子查询在 WHERE/ON 子句中,与 IN 语句结合,或者子查询为单一查询(无 UNION、GROUP BY、HAVING 等)。不适用于子查询与 OR 连接、使用 NOT IN或者子查询在 SELECT 子句中
执行策略:
- 子查询查询列表为主键/唯一索引列时,将子查询表上拉到外层查询的 FROM 子句
- 使用临时表记录外层查询记录 ID,消除重复
- 对子查询表使用松散索引扫描,仅取索引值相同的首条记录
- 逐条检查外层查询记录,找到第一条匹配的子查询记录即停止
- 物化子查询后与外层查询表连接。
简单总结:MySQL 查询优化器通过条件化简、外连接消除和子查询优化等基于规则的优化手段,将用户编写的低效查询转换为高效形式。这些规则包括移除冗余条件、合并子句、将外连接转为内连接、以及通过物化表和半连接优化 IN 子查询。开发者可以通过编写简洁的 SQL 语句、利用索引和主键、避免冗余操作等方式,进一步配合优化器提升查询性能
五、执行计划
MySQL采用EXPLAIN
分析SQL查询的执行计划,帮助优化查询性能。它展示MySQL如何执行查询,包括表访问顺序、索引使用情况等。运行EXPLAIN
语句后,会返回一个结果集,包含多个字段,以下是重要参考字段及其含义的简要介绍:
id :表示查询中每个子查询的执行顺序。值越大,优先级越高,越先执行。相同id
表示同一执行层级
select_type :查询类型,常见值包括:
SIMPLE
:简单查询,无子查询或联合PRIMARY
:最外层查询SUBQUERY
:子查询DERIVED
:派生表(如FROM子句中的子查询)UNION
:UNION操作中的查询
table :显示查询涉及的表名或别名
partitions :显示查询涉及的分区(如果表分区了)。无分区表时为空
type:访问类型(即前面所说的访问方法),反映查询效率,常见值从优到劣:
system
:表只有一行数据const
:通过主键或唯一索引直接定位一行eq_ref
:通过主键或唯一索引进行等值匹配ref
:通过非唯一索引进行等值匹配range
:索引范围扫描(如>
、<
、IN
)index
:全索引扫描ALL
:全表扫描(效率最低)
possible_keys :MySQL可能使用的索引列表。为空表示没有可用索引。
key: 实际使用的索引。为空表示未使用索引。
key_len:使用索引的长度(字节),帮助判断联合索引的使用情况。值越小,索引选择性越高
ref :显示与key
索引比较的列或常量。例如,const
表示常量值,表名.列名表示关联表的列
rows:估计扫描的行数,值越小越好,反映查询效率
filtered:表示过滤后的行数百分比(0-100%)。值越高,表示过滤效果越好
Extra:额外信息,常见值:
Using index
:仅使用索引完成查询(覆盖索引)。Using where
:在WHERE条件中过滤。Using temporary
:使用了临时表(可能影响性能)。Using filesort
:需要额外排序(可能影响性能)。Using join buffer
:使用了连接缓冲区。
分析时着重点:
- 关注
type
,尽量避免ALL
或index
,优先const
、eq_ref
、ref
- 检查
key
和possible_keys
,确保使用合适的索引 - 观察
rows
和filtered
,减少扫描行数 - 注意
Extra
中的Using temporary
和Using filesort
,可能需要优化
通过分析这些字段,可定位查询瓶颈,优化索引或重写SQL以提高性能
六、慢sql治理与解决示例(信息已进行脱敏)
以下是对提供信息的脱敏处理(包括表名、字段值、URL、图片链接等敏感信息):
慢SQL一
原始SQL(脱敏后):
SELECT count(*)
FROM `脱敏`
WHERE (xxx_id = XXX) AND (`name` LIKE '%关键词%')
字段 | 值 | 含义 |
---|---|---|
id | 1 | 单表查询 |
select_type | SIMPLE | 简单查询 |
table | 脱敏 | 查询目标表 |
type | ref | 使用了索引,但非最优 |
key | idx_xxxid_xxxuserid | 使用的索引不含 name |
rows | 589328 | 预计扫描 58 万行 |
filtered | 11.11 | 低过滤率 |
Extra | Using where | 没有使用 name 字段索引,存在回表操作 |
性能瓶颈分析:模糊匹配效率低
问题点 | 描述 |
---|---|
%关键词% 前模糊匹配 | 无法使用 B+Tree 索引 |
name 无索引 | 即使改为前缀匹配 LIKE ‘xxx%’ 也无法优化 |
使用了错误索引 | 当前索引 (xxx_id, xxxuserid) 无法支持 name 查询 |
高数据量下回表扫描 | xxx_id = 177 的数据量大(58w),过滤性能差 |
优化方式:去掉前模糊匹配,改成 LIKE '关键词%'
慢SQL二
原始SQL(脱敏后):
SELECT count(*)
FROM `脱敏`
WHERE creator IN ('ID1', 'ID2', 'ID3', 'ID4', 'ID5', 'ID6', 'ID7') AND (xxx_id = YYY) AND (status = 1) AND (`name` LIKE '%活动关键词%')
性能瓶颈分析:同慢SQL一
字段 | 值 | 解读 |
---|---|---|
type | ref | 使用了 corp_id 索引,但效率不高 |
key | idx_xxxid_xxxuserid | 说明使用的是 (corp_id, userid) 的索引 |
rows | 518218 | 预估要扫描 50 万条数据(即 corp_id=156 的数据量) |
filtered | 0.56 | 超低过滤率,说明大多数数据都是“扫了但没用上” |
Extra | Using where | 多条件联合过滤 + 无法使用 name 的索引,导致回表严重 |
优化方式:使用搜索引擎(如Elasticsearch)进行模糊匹配
问题点 | 描述 |
---|---|
%模糊匹配% | LIKE ‘%素质学习活动%’ 无法走索引,依旧是性能杀手 |
无 name 索引 | 和慢 SQL 1 一样,name 字段未建立索引 |
使用错误索引 | 当前使用的是 (xxx_id, xxxuserid),和查询条件不匹配 |
多个 WHERE 条件 | creator IN (…) + status=1 + xxx_id=156 增加了查询复杂度 |
扫描行数巨大 | 扫描了 50 万行,过滤率不到 1%,极其低效 |
要做模糊匹配直接上es得了(开个玩笑哈哈哈哈
慢SQL三
原始SQL(脱敏后):
SELECT `id`, `media_id`
FROM `脱敏`
WHERE (xxx_id = XXX) AND (user_id = ZZZ) AND (url = 'https://example.com/feed?...') LIMIT 1
性能瓶颈分析:URL字段查询效率低
字段 | 值 | 含义 |
---|---|---|
type | ref | 使用了索引但是模糊匹配级别,不够高效 |
key | idx_xxxid_xxxuserid | 当前使用的是 (corp_id, user_id) 索引 |
rows | 27622 | 即便用了索引,预估扫描仍达 2 万+ 行 |
filtered | 10% | 表示大部分数据被过滤掉了(说明 url 没有参与索引) |
Extra | Using where | 表示 MySQL 仍需逐行判断 url=xxx 条件 |
优化方式:
添加新字段(如哈希值),改写SQL:
SELECT id, media_id
FROM 脱敏
WHERE xxx_id = XXXAND xxxuser_id = ZZZAND url_hash = '哈希值'AND url = 'https://example.com/feed?...'//换掉这个LIMIT 1
慢SQL四
原始SQL(脱敏后):
SELECT id, xxxuser_id1, xxxuser_id2, xxx_id, task_id, ...
FROM table_task
WHERE xxxuser_id2 = '用户ID'AND xxx_id = XXXAND create_time >= 时间戳AND task_status = 2;
性能瓶颈分析:
这条 SQL 单条执行并不慢,索引命中了(走的是 (xxx_userid, create_time)
),本质的问题是高并发下的系统级退化
当使用 Goroutine 并发 + 高请求量 时,这类 SQL 总体上出现了“指数级增长”的表现 —— 本质上是 I/O 饱和 / DB 锁 / CPU 争抢 / goroutine 堆积导致的
字段 | 值 | 含义 |
---|---|---|
type | range | 范围扫描,说明部分使用了索引 |
key | idx_wx_user_time | 使用了某个复合索引(猜测为 xxx_userid + create_time) |
rows | 68794 | 预估需要扫描约 6.8 万行 |
filtered | 1.00 | 过滤率很低(接近全扫描) |
Extra | Using index condition; Using where | 没有使用覆盖索引,部分条件未被索引消化 |
具体分析:
问题类型 | 原因 |
---|---|
✅ 数据库连接池耗尽 | 协程数超出 max_open_conns,大量阻塞 |
✅ 单连接响应慢 | 数据量大,每条查询虽然不慢,但处理行数多,连接占用时间长 |
✅ Goroutine 累积 | 协程爆炸,CPU、内存调度竞争,影响系统吞吐 |
✅ MySQL CPU 使用高 | 并发下 10w+ 次筛选,造成 DB CPU 持续高压 |
✅ 索引虽走,但行数多 | rows=68794 表示每次查近 7 万条,I/O 和网络传输压力大 |
优化方式:
- 限制协程并发度(从4降到2)。
- 查询范围从10天减少到3天。
- 执行频率从10分钟一次改为30分钟一次。
慢SQL五
原始SQL(脱敏后):
SELECT count(*)
FROM `脱敏`
WHERE (xxx_id = YYY) AND (user_id = AAA) AND (state = '状态值') AND (del_way != 0) AND (delete_time >= 时间戳1) AND (delete_time < 时间戳2)
性能瓶颈分析:
- 单条SQL不慢,但在高数据量和高并发场景下响应变慢
字段 | 值 | 含义 |
---|---|---|
key | idx_xxxuserid_followtime | 使用了 (xxxuser_id, follow_time) 联合索引,但不完全匹配查询条件 |
key_len | 8 | 实际只使用了索引的第一列 xxxuser_id(类型为 BIGINT,占用 8 字节) |
ref | const | xxxuser_id = 51967 为常量匹配,用于索引过滤 |
rows | 53472 | 预估扫描行数约为 5.3 万,说明该 xxxuser_id 拥有大量数据 |
filtered | 0.00 | 预估过滤率为 0%,说明几乎所有行都需进一步筛选,回表严重 |
Extra | Using where | 表示需回表做完整 WHERE 条件判断,未能使用覆盖索引或索引条件下推 |
同样的,单条 SQL 本身不慢,但在高数据量 & 高并发场景下导致整体响应变慢
具体分析:
问题 | 描述 |
---|---|
❗️数据倾斜 | 部分 xxxuser_id 拥有极多数据(几十万级),SQL执行时间呈不均衡状态 |
❗️并发导致放大 | 每个用户都执行这条 SQL,量一大就撑爆连接池或 DB 资源 |
❗️索引不命中 delete_time / state | 这两个高过滤条件字段只能靠回表判断,扫描行数增多 |
❗️逻辑层并发扫描 | 多个用户并发执行时,每人扫几万,系统瞬间承压(4核但代码设置了10个协程数) |
优化方式:
1、加复合索引
--理想索引--
CREATE INDEX idx_xxxuserid_delway_state_deltime
ON 脱敏(xxxuser_id, del_way, state, delete_time);
2、拆分冷热数据
按 delete_time
划分历史表(冷数据归档),热表走主逻辑查询,减少范围扫描
3、这次采用以下3种优化
优化手段 | 背景/动机 | 预期收益 |
---|---|---|
清理数据库表中的双删数据 | 当前 del_way != 0 过滤范围太大,无效数据长期滞留影响查询效率;部分旧数据可归档或物理删除 | 降低数据量,减少每次扫描行数,提升过滤效率 |
降低协程并发数 | 当前每个 xxxuser_id 并发触发多条 SQL,容易造成连接池耗尽、系统抖动 | 控制系统资源消耗,更稳定运行、更平滑负载 |
将时间范围由 3 天缩小到 1 天 | 原先扫描范围广、数据量大,导致执行时间不可控;改为每日定时执行或滚动聚合 | 明确可控的数据窗口,降低查询压力、支持更频繁调度 |
类型 | 内容 |
---|---|
数据优化 | 清理无效数据、减少扫描量 |
查询优化 | 改写 SQL 表达式、提升执行计划 |
系统优化 | 限制并发、平滑调度 |
窗口优化 | 缩小时间范围、减少数据量 |