MySQL Index--关联条件列索引缺失导致执行计划性能不佳

某系统反馈慢SQL影响生产,查看SLOW LOG后发现问题。初步断定查询走错索引,经多次尝试,发现表ob_batch_d关联条件上缺少索引。在优化INNER JOIN操作时,需确定两表关系,在相应列创建索引,有时经验也会导致误区。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

某系统反馈慢SQL影响生产,查看SLOW LOG发现下面慢SQL:

SELECT COUNT(DISTINCT m.batch_no) 
FROM ob_relation r
INNER JOIN ob_batch_d d
ON r.sub_order_no = d.outbound_no
INNER JOIN ob_batch_m m 
ON d.batch_no = m.batch_no
WHERE r.production_mode =1 
AND r.yn=0 
AND r.outbound_no ='xxxx2156'
AND d.yn=0 
AND m.yn=0 
AND m.SEND_FLAG=0 
AND m.batch_no!='xxx00025984';

 

设计表上索引情况如下:

表ob_relation上索引:
KEY `idx_outbound_no` (`OUTBOUND_NO`),
KEY `idx_sub_order_no` (`SUB_ORDER_NO`)
  

表ob_batch_d上索引:
KEY `idx_update_time` (`UPDATE_TIME`),
KEY `idx_ob_outbound_batch_d_batch_no` (`BATCH_NO`),
KEY `idx_ORDER_GROUP_ID` (`ORDER_GROUP_ID`),


表ob_batch_m上索引:
KEY `idx_update_time` (`UPDATE_TIME`),
KEY `idx_BATCH_NO` (`BATCH_NO`),
KEY `idx_BEAT_NO` (`BEAT_NO`),
KEY `idx_BATCH_TYPE_OPT_STATUS` (`BATCH_TYPE`,`OPT_STATUS`),
KEY `ix_SEND_FLAG` (`SEND_FLAG`)

 

查看执行计划为:

+----+-------------+-------+------------+------+----------------------------------+----------------------------------+---------+--------------------+-------+----------+-------------+
| id | select_type | table | partitions | type | possible_keys                    | key                              | key_len | ref                | rows  | filtered | Extra       |
+----+-------------+-------+------------+------+----------------------------------+----------------------------------+---------+--------------------+-------+----------+-------------+
|  1 | SIMPLE      | r     | NULL       | ref  | idx_outbound_no,idx_sub_order_no | idx_outbound_no                  | 92      | const              |     4 |     1.25 | Using where |
|  1 | SIMPLE      | m     | NULL       | ref  | idx_BATCH_NO,ix_SEND_FLAG        | ix_SEND_FLAG                     | 2       | const              | 54292 |     5.07 | Using where |
|  1 | SIMPLE      | d     | NULL       | ref  | idx_ob_outbound_batch_d_batch_no | idx_ob_outbound_batch_d_batch_no | 92      | ob_task.m.BATCH_NO |    16 |     1.00 | Using where |
+----+-------------+-------+------------+------+----------------------------------+----------------------------------+---------+--------------------+-------+----------+-------------+

 

本着直觉判定使用索引ix_SEND_FLAG但影响行数为54292步骤存在问题,虽然过滤条件中包含SEND_FLAG=0且列SEND_FLAG上有索引,但选择性较差,初步断定查询走错索引导致。

尝试1:删除列SEND_FLAG上索引,迫使查询走列BATCH_NO上索引,删除列SEND_FLAG上索引后,执行计划变为:

+----+-------------+-------+------------+-------+----------------------------------+----------------------------------+---------+--------------------+--------+----------+---------------------------------------------------------------------------+
| id | select_type | table | partitions | type  | possible_keys                    | key                              | key_len | ref                | rows   | filtered | Extra                                                                     |
+----+-------------+-------+------------+-------+----------------------------------+----------------------------------+---------+--------------------+--------+----------+---------------------------------------------------------------------------+
|  1 | SIMPLE      | r     | NULL       | ref   | idx_outbound_no,idx_sub_order_no | idx_outbound_no                  | 92      | const              |      4 |     1.25 | Using where                                                               |
|  1 | SIMPLE      | m     | NULL       | range | idx_BATCH_NO                     | idx_BATCH_NO                     | 92      | NULL               | 146187 |     9.99 | Using index condition; Using where; Using join buffer (Block Nested Loop) |
|  1 | SIMPLE      | d     | NULL       | ref   | idx_ob_outbound_batch_d_batch_no | idx_ob_outbound_batch_d_batch_no | 92      | ob_task.m.BATCH_NO |     16 |     1.00 | Using where                                                               |
+----+-------------+-------+------------+-------+----------------------------------+----------------------------------+---------+--------------------+--------+----------+---------------------------------------------------------------------------+

查询影响行数变得更高,性能更差。

 

尝试2:检查INNER JOIN 关联列和WHERE条件列上的数据类型,排除隐式转换导致。

尝试3: 去除与ob_batch_m的所有相关进行,仅剩表ob_relation和ob_batch_d做INNER JOIN,其执行计划为:

SELECT COUNT(DISTINCT d.batch_no) 
FROM ob_relation r
INNER JOIN ob_batch_d d
ON r.sub_order_no = d.outbound_no
WHERE r.production_mode =1 
AND r.yn=0 
AND r.outbound_no ='xxxx2156'
AND d.yn=0 

+----+-------------+-------+------------+------+----------------------------------+-----------------+---------+-------+---------+----------+----------------------------------------------------+
| id | select_type | table | partitions | type | possible_keys                    | key             | key_len | ref   | rows    | filtered | Extra                                              |
+----+-------------+-------+------------+------+----------------------------------+-----------------+---------+-------+---------+----------+----------------------------------------------------+
|  1 | SIMPLE      | r     | NULL       | ref  | idx_outbound_no,idx_sub_order_no | idx_outbound_no | 92      | const |       4 |     1.25 | Using where                                        |
|  1 | SIMPLE      | d     | NULL       | ALL  | NULL                             | NULL            | NULL    | NULL  | 4917114 |     1.00 | Using where; Using join buffer (Block Nested Loop) |
+----+-------------+-------+------------+------+----------------------------------+-----------------+---------+-------+---------+----------+----------------------------------------------------+

发现表ob_batch_d的关联条件上缺少索引导致。

 

问题总结:

查询中ob_relation和ob_batch_d关联,而ob_batch_d和ob_batch_m关联,但由于表ob_batch_d关联条件上没有索引且表ob_relation上WHERE过滤条件选择性极高,因此查询优化器将SQL转换为:

SELECT COUNT(DISTINCT m1.batch_no) FROM (
	SELECT m.batch_no,d.outbound_no
	FROM ob_batch_d d
	INNER JOIN ob_batch_m m 
	ON d.batch_no = m.batch_no
	WHERE d.yn=0 
	AND m.yn=0 
	AND m.SEND_FLAG=0 
	AND m.batch_no!='xxx00025984'
)AS m1
INNER JOIN (
	SELECT r.sub_order_no 
	FROM ob_relation r
	WHERE r.production_mode =1 
	AND r.yn=0 
	AND r.outbound_no ='xxxx2156'
)AS r1
ON m1.outbound_no=r1.sub_order_no

对于m1子查询内部,表ob_batch_m可以通过SEND_FLAG=0来过滤部分数据(虽然选择性较差),因此选择使用索引ix_SEND_FLAG,而表ob_batch_d上缺少索引的问题被“完美隐藏”。

 

在优化INNER JOIN操作时,通常需要先确定两表的关系(外表和内表),外表通过WHERE条件列来过滤数据,而内表通过关联条件列来定位数据。

如对于下列查询:

SELECT COUNT(1) 
FROM ob_relation r
INNER JOIN ob_batch_d d
ON r.sub_order_no = d.outbound_no
WHERE r.outbound_no ='xxxx2156';

表ob_relation和表ob_batch_d做INNER JOIN操作
1、表ob_relation作为外表,可以通过outbound_no ='xxxx2156'来过滤数据,因此需要在ob_relation(outbound_no)上创建索引。

2、表ob_batch_d作为内表,需要通过关联列outbound_no来定位数据,因此需要在ob_batch_d(outbound_no)上创建索引。

3、表ob_relation的关联条件sub_order_no并不需要单独作为索引第一列来创建索引,但在部分查中可以通过覆盖索引特性来优化查询,如上面查询中创建组合索引ob_relation(outbound_no,sub_order_no)能获得最佳性能。

 

总结:

有时候,经验能帮助你快速处理问题,但有些时候,经验能带你直接走入误区。

转载于:https://www.cnblogs.com/gaogao67/p/11046561.html

<think>嗯,用户问的是MySQL关联查询中走索引的场景及原因,还有优化方法。首先,我得回忆一下索引的基本知识,以及关联查询时索引如何起作用。根据引用里的内容,比如引用3提到组合索引使用第一列会失效,还有数据类型隐式转换会导致索引失效。还有引用2提到优化JOIN语句要确保连接字段有索引,用EXPLAIN检查。 首先,用户可能想知道在哪些情况下JOIN操作会使用索引,比如连接字段没索引,或者类型匹配,或者使用了函数导致索引失效。比如,如果连接的两个字段数据类型同,比如一个INT一个VARCHAR,这时候可能会走索引。还有,如果在连接字段上用了函数,比如CONVERT或者SUBSTRING,也会导致索引失效。 然后,需要结合引用里的例子,比如test_user和test_order表,如果user_id在test_order里没有索引,那么JOIN的时候可能全表扫描。另外,当使用OR条件或者范围查询时,可能部分情况索引失效,比如引用3里的最左前缀法则,组合索引的问题。 优化方法的话,应该包括添加合适的索引,检查数据类型是否一致,避免在连接字段上使用函数或表达式,使用EXPLAIN分析执行计划。引用2提到可以创建索引、调整JOIN的顺序,或者使用临时表来处理子查询。 还要注意,当使用外连接时,比如LEFT JOIN,如果右表的连接字段没有索引,也会导致性能问题。可能用户需要了解这些细节,以及如何通过优化表结构或者查询语句来改善性能。 总结下来,需要列举常见的几种走索引的场景,比如无索引、类型隐式转换、使用函数、OR条件、外键约束问题,然后针对每个场景给出优化方法。同时,结合引用中的例子和优化建议,确保回答准确且有依据。</think>### MySQL关联查询走索引的场景及优化方法 #### 一、走索引的典型场景及原因 1. **连接字段无索引** - **场景**:当`JOIN`操作的连接字段(如`user_id`)在**被驱动表**(如`test_order`)中未建立索引时,查询会进行全表扫描。 - **示例**: ```sql SELECT * FROM test_user u JOIN test_order o ON u.id = o.user_id; -- 若test_order.user_id无索引,则全表扫描 ``` [^4] 2. **隐式类型转换** - **场景**:连接字段的数据类型一致(如`CHAR`与`INT`),导致索引失效。 - **示例**: ```sql SELECT * FROM test_user u JOIN test_order o ON u.id = CAST(o.user_id AS CHAR); -- 强制类型转换使索引失效 ``` [^3] 3. **函数或表达式操作** - **场景**:对连接字段使用函数(如`CONCAT()`、`SUBSTRING()`)或表达式时,索引无法生效。 - **示例**: ```sql SELECT * FROM test_user u JOIN test_order o ON SUBSTRING(u.user_name,1,3) = o.order_name; -- 函数操作破坏索引 ``` 4. **OR条件导致部分失效** - **场景**:`WHERE`子句中的`OR`条件若涉及非索引字段,可能导致索引部分失效。 - **示例**: ```sql SELECT * FROM test_user u JOIN test_order o ON u.id = o.user_id WHERE u.age > 18 OR o.pay = 0; -- 若o.pay无索引,部分条件无法使用索引 ``` 5. **外键约束缺失** - **场景**:外键关联的表未正确设置索引时,可能影响`JOIN`效率(即使MySQL强制要求外键索引,但实际查询仍需依赖索引)。 --- #### 二、优化方法 1. **添加必要索引** - **操作**:为被驱动表的连接字段创建索引。例如: ```sql ALTER TABLE test_order ADD INDEX idx_user_id(user_id); -- 优化JOIN性能 ``` [^2] 2. **确保数据类型一致** - **操作**:检查连接字段类型是否一致,例如将`VARCHAR`与`CHAR`统一为相同类型。 3. **避免字段操作** - **原则**:在连接字段上使用函数或表达式,可通过调整查询逻辑规避。 - **示例**: ```sql -- 原始低效写法 SELECT * FROM test_user u JOIN test_order o ON DATE(u.create_time) = o.order_date; -- 优化后(假设order_date为DATE类型) SELECT * FROM test_user u JOIN test_order o ON u.create_time BETWEEN o.order_date AND o.order_date + INTERVAL 1 DAY; ``` 4. **使用覆盖索引** - **操作**:若查询仅需索引字段,可创建覆盖索引减少回表操作。例如: ```sql ALTER TABLE test_order ADD INDEX idx_covering(user_id, order_name); ``` 5. **调整JOIN顺序** - **策略**:通过`STRAIGHT_JOIN`强制指定驱动表(小表驱动大表),或依赖优化器自动选择。 - **示例**: ```sql SELECT * FROM small_table s STRAIGHT_JOIN large_table l ON s.id = l.small_id; -- 明确指定驱动表 ``` [^2] 6. **减少JOIN层级** - **方法**:拆分复杂查询为多个简单步骤,使用临时表存储中间结果。 - **示例**: ```sql CREATE TEMPORARY TABLE tmp_users SELECT id FROM test_user WHERE age > 18; -- 提前过滤并存储中间结果 SELECT * FROM tmp_users u JOIN test_order o ON u.id = o.user_id; ``` --- #### 三、验证工具:EXPLAIN 使用`EXPLAIN`分析查询计划,重点关注以下字段: - **type**:若为`ALL`,说明全表扫描;若为`ref`或`eq_ref`,说明使用索引。 - **key**:显示实际使用的索引名称。 - **rows**:预估扫描行数,数值越大性能风险越高。 ```sql EXPLAIN SELECT * FROM test_user u JOIN test_order o ON u.id = o.user_id; ``` ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值