美文网首页
关于 charsets 的慢查询排查

关于 charsets 的慢查询排查

作者: 杰哥长得帅 | 来源:发表于2018-12-20 21:44 被阅读14次

下面的 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 的时候被隐式转换了,导致走不了索引

相关文章

网友评论

      本文标题:关于 charsets 的慢查询排查

      本文链接:https://www.haomeiwen.com/subject/oepukqtx.html