此时索引使用情况和情况二相同,因为title未提供,所以查询只用到了索引的第一列,而后面的from_date虽然也在索引中,但是由于title不存在而无法和左前缀连接,因此需要对结果进行扫描过滤from_date(这里由于emp_no唯一,所以不存在扫描) 。如果想让from_date也使用索引而不是where过滤,可以增加一个辅助索引<emp_no, from_date>,此时上面的查询会使用这个索引 。除此之外,还可以使用一种称之为“隔离列”的优化方法,将emp_no与from_date之间的“坑”填上 。
首先我们看下title一共有几种不同的值:
【MySQL索引背后的数据结构及算法原理】SELECT DISTINCT(title) FROM employees.titles;+--------------------+| title|+--------------------+| Senior Engineer|| Staff|| Engineer|| Senior Staff|| Assistant Engineer || Technique Leader|| Manager|+--------------------+
只有7种 。在这种成为“坑”的列值比较少的情况下,可以考虑用“IN”来填补这个“坑”从而形成最左前缀:
EXPLAIN SELECT * FROM employees.titlesWHERE emp_no='10001'AND title IN ('Senior Engineer', 'Staff', 'Engineer', 'Senior Staff', 'Assistant Engineer', 'Technique Leader', 'Manager')AND from_date='1986-06-26';+----+-------------+--------+-------+---------------+---------+---------+------+------+-------------+| id | select_type | table| type| possible_keys | key| key_len | ref| rows | Extra|+----+-------------+--------+-------+---------------+---------+---------+------+------+-------------+|1 | SIMPLE| titles | range | PRIMARY| PRIMARY | 59| NULL |7 | Using where |+----+-------------+--------+-------+---------------+---------+---------+------+------+-------------+
这次key_len为59,说明索引被用全了,但是从type和rows看出IN实际上执行了一个range查询,这里检查了7个key 。看下两种查询的性能比较:
SHOW PROFILES;+----------+------------+-------------------------------------------------------------------------------+| Query_ID | Duration| Query|+----------+------------+-------------------------------------------------------------------------------+|10 | 0.00058000 | SELECT * FROM employees.titles WHERE emp_no='10001' AND from_date='1986-06-26'||11 | 0.00052500 | SELECT * FROM employees.titles WHERE emp_no='10001' AND title IN ...|+----------+------------+-------------------------------------------------------------------------------+
“填坑”后性能提升了一点 。如果经过emp_no筛选后余下很多数据,则后者性能优势会更加明显 。当然,如果title的值很多,用填坑就不合适了,必须建立辅助索引 。
情况四:查询条件没有指定索引第一列 。EXPLAIN SELECT * FROM employees.titles WHERE from_date='1986-06-26';+----+-------------+--------+------+---------------+------+---------+------+--------+-------------+| id | select_type | table| type | possible_keys | key| key_len | ref| rows| Extra|+----+-------------+--------+------+---------------+------+---------+------+--------+-------------+|1 | SIMPLE| titles | ALL| NULL| NULL | NULL| NULL | 443308 | Using where |+----+-------------+--------+------+---------------+------+---------+------+--------+-------------+
由于不是最左前缀,索引这样的查询显然用不到索引 。
情况五:匹配某列的前缀字符串 。EXPLAIN SELECT * FROM employees.titles WHERE emp_no='10001' AND title LIKE 'Senior%';+----+-------------+--------+-------+---------------+---------+---------+------+------+-------------+| id | select_type | table| type| possible_keys | key| key_len | ref| rows | Extra|+----+-------------+--------+-------+---------------+---------+---------+------+------+-------------+|1 | SIMPLE| titles | range | PRIMARY| PRIMARY | 56| NULL |1 | Using where |+----+-------------+--------+-------+---------------+---------+---------+------+------+-------------+
此时可以用到索引,但是如果通配符不是只出现在末尾,则无法使用索引 。(原文表述有误,如果通配符%不出现在开头,则可以用到索引,但根据具体情况不同可能只会用其中一个前缀)
情况六:范围查询 。EXPLAIN SELECT * FROM employees.titles WHERE emp_no < '10010' and title='Senior Engineer';+----+-------------+--------+-------+---------------+---------+---------+------+------+-------------+| id | select_type | table| type| possible_keys | key| key_len | ref| rows | Extra|+----+-------------+--------+-------+---------------+---------+---------+------+------+-------------+|1 | SIMPLE| titles | range | PRIMARY| PRIMARY | 4| NULL |16 | Using where |+----+-------------+--------+-------+---------------+---------+---------+------+------+-------------+
推荐阅读
- 日本|号称最轻松赚钱方式!日本新奇打工背着电脑走 网友直呼无聊
- Mysql事务隔离级别
- MySQL主从不一致情形与解决方法
- MySQL INSERT 有哪4种形态?
- 中老年耳背会遗传吗?
- 女性妇科炎症背酸怎么办呢?
- 泰姬陵背后的血腥恐怖袭击 泰姬陵的神秘事件
- MYSQL主主模式 LNMP 独立部署配置指导书
- MySQL的隐式转化
- 通用搜索引擎背后的技术点