节点|曾经,我以为我很懂MySQL索引...( 四 )


比如索引 abc_index:(a , b , c)是 a , b , c 三个字段的联合索引 , 下列 sql 执行时都无法命中索引 abc_index 的 。

  1. select * from table where c = '1';
  2. select * from table where b ='1' and c ='2';
以下三种情况却会走索引:
  1. select * from table where a = '1';
  2. select * from table where a = '1' and b = '2';
  3. select * from table where a = '1' and b = '2' and c='3';
从上面两个例子大家是否阔以看出点眉目?
是的 , 索引 abc_index:(a , b , c) , 只会在(a)、(a , b)、(a , b , c)三种类型的查询中使用 。
其实这里说的有一点歧义 , 其实(a , c)也会走 , 但是只走 a 字段索引 , 不会走 c 字段 。
另外还有一个特殊情况说明下 , 下面这种类型的也只会有 a 与 b 走索引 , c 不会走 。
  1. select * from table where a = '1' and b > '2' and c='3';
像上面这种类型的 sql 语句 , 在 a、b 走完索引后 , c 已经是无序了 , 所以 c 就没法走索引 , 优化器会认为还不如全表扫描 c 字段来的快 。
最左前缀:顾名思义 , 就是最左优先 , 上例中我们创建了 a_b_c 多列索引 , 相当于创建了(a)单列索引 , (a , b)组合索引以及(a , b , c)组合索引 。
因此 , 在创建多列索引时 , 要根据业务需求 , where 子句中使用最频繁的一列放在最左边 。
④索引下推优化
还是索引 name_age_index , 有如下 sql:
  1. select * from table where name like '陈%' and age > 26;
该语句有两种执行可能:
  • 命中 name_age_index 联合索引 , 查询所有满足 name 以"陈"开头的数据 ,然后回表查询所有满足的行 。
  • 命中 name_age_index 联合索引 , 查询所有满足 name 以"陈"开头的数据 , 然后顺便筛出 age>20 的索引 , 再回表查询全行数据 。
显然第 2 种方式回表查询的行数较少 , I/O 次数也会减少 , 这就是索引下推 。 所以不是所有 like 都不会命中索引 。
使用索引时的注意事项
①索引不会包含有 null 值的列
只要列中包含有 null 值都将不会被包含在索引中 , 复合索引中只要有一列含有 null 值 , 那么这一列对于此复合索引就是无效的 。 所以我们在数据库设计时建议不要让字段的默认值为 null 。
②使用短索引
对串列进行索引 , 如果可能应该指定一个前缀长度 。
例如 , 如果有一个 char(255)的列 , 如果在前 10 个或 20 个字符内 , 多数值是惟一的 , 那么就不要对整个列进行索引 。 短索引不仅可以提高查询速度而且可以节省磁盘空间和 I/O 操作 。
③索引列排序
查询只使用一个索引 , 因此如果 where 子句中已经使用了索引的话 , 那么 order by 中的列是不会使用索引的 。
因此数据库默认排序可以符合要求的情况下不要使用排序操作;尽量不要包含多个列的排序 , 如果需要最好给这些列创建复合索引 。
④like 语句操作
一般情况下不推荐使用 like 操作 , 如果非使用不可 , 如何使用也是一个问题 。 like “%陈%” 不会使用索引而 like “陈%”可以使用索引 。
⑤不要在列上进行运算
这将导致索引失效而进行全表扫描 , 例如:
  1. SELECT * FROM table_name WHERE YEAR(column_name)<2017;
⑥不使用 not in 和 <> 操作
这不属于支持的范围查询条件 , 不会使用索引 。
我的体会
曾经 , 我一度以为我很懂 MySQL 。
刚入职那年 , 我还是个孩子 , 记得第一个需求是做个统计接口 , 查询近两小时每隔 5 分钟为一时间段的网站访问量 , JSONArray 中一共返回 24 个值 , 当时菜啊 , 写了个接口循环二十四遍 , 发送 24 条 SQL 去查(捂脸) 。


推荐阅读