下面的 sql 报了慢查询:
select
c.c_code as cCode,
u.u_code as uCode,
h.h_code as hCode
from c
join u on c.u_code = u.u_code
join h on u.h_code = h.h_code
WHERE c.c_code = 'xxxxxxxxxxxxx';
其中 c_code、u_code、h_code 分别是 c、u、h 表的一个 unique index
explain
explain SELECT
-> c.c_code as cCode,
-> u.u_code as uCode,
-> h.h_code as hCode
-> FROM c
-> join u on c.u_code = u.u_code
-> join h on h.h_code = u.h_code
-> WHERE c.c_code = 'xxxxxxxxxxxxx'\G;
***************************[ 1. row ]***************************
id | 1
select_type | SIMPLE
table | c
type | const
possible_keys | uniq_c,idx_u_code
key | uniq_c
key_len | 82
ref | const
rows | 1
Extra | <null>
***************************[ 2. row ]***************************
id | 1
select_type | SIMPLE
table | h
type | ALL
possible_keys | uniq_h_code
key | <null>
key_len | <null>
ref | <null>
rows | 3110
Extra | <null>
***************************[ 3. row ]***************************
id | 1
select_type | SIMPLE
table | u
type | ref
possible_keys | idx_h_code
key | idx_h_code
key_len | 62
ref | **.h.h_code
rows | 5
Extra | Using where
3 rows in set
c 和 u 表都按照我们的设计,走了索引,那问题的关键就是在 h 表上了:明明找到了 uniq_h_code,为啥最终没有走这个唯一索引呢?
分析
先缩小问题的范围,拆分这个 SQL:先试 c 与 u 的 join,再试 u 与 h 的 join
首先 explain c 与 u:
explain SELECT
-> c.c_code as cCode,
-> u.u_code as uCode
-> FROM c
-> join u on c.u_code = u.u_code
-> WHERE c.c_code = 'xxxxxxxxxxxxx'\G;
***************************[ 1. row ]***************************
id | 1
select_type | SIMPLE
table | c
type | const
possible_keys | uniq_c,idx_u_code
key | uniq_c
key_len | 82
ref | const
rows | 1
Extra | <null>
***************************[ 2. row ]***************************
id | 1
select_type | SIMPLE
table | u
type | index
possible_keys | <null>
key | uniq_u_code
key_len | 62
ref | <null>
rows | 20434
Extra | Using where; Using index
2 rows in set
然后是 explain u 与 h:
explain SELECT
-> u.u_code as uCode,
-> h.h_code as hCode
-> FROM u
-> join h on h.h_code = u.h_code
-> WHERE c.u_code = 'xxxxxxxxxxxxx'\G;
***************************[ 1. row ]***************************
id | 1
select_type | SIMPLE
table | h
type | const
possible_keys | uniq_h_code
key | uniq_h_code
key_len | 62
ref | const
rows | 1
Extra | Using index
***************************[ 2. row ]***************************
id | 1
select_type | SIMPLE
table | u
type | ref
possible_keys | idx_h_code
key | idx_h_code
key_len | 62
ref | const
rows | 1
Extra | Using index condition
2 rows in set
答案很明显,u 和 h 的 join 没任何问题,按照期望走了索引。而 c 和 u 的 join 出现了问题,虽然 type 是 index,但是仍然属于全表扫描的范畴,最为诡异的是,possible_keys 里没找到对应的索引
为了继续找原因,决定试一试用 FORCE INDEX 的方式去限制下 MySQL 的索引使用,仅局限到我们指定的索引上:
explain SELECT
-> c.c_code as cCode,
-> u.u_code as uCode
-> FROM c
-> join u on c.u_code = u.u_code
-> WHERE c.c_code = 'xxxxxxxxxxxxx'\G;
***************************[ 1. row ]***************************
id | 1
select_type | SIMPLE
table | c
type | const
possible_keys | uniq_c,idx_u_code
key | uniq_c
key_len | 82
ref | const
rows | 1
Extra | <null>
***************************[ 2. row ]***************************
id | 1
select_type | SIMPLE
table | u
type | index
possible_keys | <null>
key | uniq_u_code
key_len | 62
ref | <null>
rows | 20434
Extra | Using where; Using index
2 rows in set
然而,MySQL 仍然没有按照预期去走索引
这边可以看出 u 和 h join 的时候是正常的,说明可能是 c 本身的问题,比如走了隐式转化?
看了一下 c 表的结构,发现原因:因为 c 表的 charsets 是 utf8mb4,而 u 和 h 表的 charsets 是 utf8,所以 join 的时候被隐式转换了,导致走不了索引
网友评论