1. 当使用索引列进行查询的时候尽量不要使用表达式,把计算放到业务层而不是数据库层
如下图 两个 sql 的结果是一样的,但是两个 sql 的执行计划是不一样,在 type 中 index 的效率远不如 const where 条件中 actor_id+4 表达式影响了执行计划
2. 尽量使用主键查询,而不是其他索引,主键查询不会出现回表查询。
我们所有的表基本都会有主键的,所以平时开发中能用索引就用索引,能用主键索引就用主键索引。
3. 使用前缀索引
很多时候我们的索引其实都是字符串,不可避免会出现长字符串,就会导致索引占用过大,降低其效率。尤其是对于 blob,text, varchar 这样的长列。这时候处理方式就是不使用字段的全值作为索引,而是只取其前半部分即可(选择的这部分前缀索引的选择性接近于整个列)。这样可以大大减少索引空间,从而提高效率,坏处就是降低了索引的选择性。
索引选择性:不重复的索引值和数据表记录总数的比值 (#T), 范围从 1/#T 到 1 之间。索引的选择性越高查询效率也高,因为数据的区分度很高,可以过滤掉更多的行。唯一性索引的选择性是 1,其性能也最好。
例如公司的员工表中邮箱字段,一个公司的邮箱后缀都是一样的如 xxxx@qq.com, 其实用邮箱作为索引有效的就 xxxx 部分,因为 @qq.com 都是一样的,对索引是无意义的,明显只用 xxxx 作为索引,其选择性和整个值的是一样的,但是 xxxx 作为索引明显就会减少索引空间。
下面我们已 employee 表为例子 (表结构和数据看文末)
我们以 email 字段建立索引为例:
这个数据的邮箱其实是手机号 +@qq.com 为例的,其实前 11 位后面都是相同的。我用下面的 sql 来看看这些数据的选择性(分别取前 10,11,12)位计算。
-- 当是11个前缀的时候选择性是1,在增加字段长度,选择性也不会变化select count(distinct left(email,10))/count(*) as e10, count(distinct left(email,11))/count(*) as e11, count(distinctleft(email,12))/count(*) as e12 from employee;
从上图我们可以看出前 10,前 11,前 12 的选择性分别是 0.14,1.0,1.0 , 在第 11 位的时候索引选择性是最高的 1,就没必要使用全部作为索引,增加了索引的空间。
-- 创建前缀索引
alter table employee add key(email(11));
我们也可以使用 count 计算频率来统计(出现的次数越少,说明重复率越低,选择性越大)
-- 查找前缀出现的频率
select count(*) as cnt,left(email,11) as pref from employee group by pref order by cnt desc limit 10;
4. 使用索引扫描来排序
我们经常会有排序的需求,使用 order by 但是 order by 是比较影响性能的,它是通过把数据加载到内存去排序的,如果数据量很大内存放不下,只能分多次处理。但是索引本身就是有序的,直接通过索引完成排序更省事。
扫描索引本身是很快的,因为只需要从一条索引记录移动到紧接着的下一条记录,但如果索引不能覆盖查询所需的所有列时,就不得不每扫描一条索引记录就回表查询一次对应行,这基本都是随机 IO。因此按索引顺序读取数据的速度通常比顺序的全表扫描慢。
mysql 可以使用同一个索引即满足排序,又用于查找行。如果可以的话请考虑建立这种索引。
只有当索引列顺序和 order by 子句的顺序完全一致,并且所有列的排序方向 (倒叙或者正序) 都是一样的,mysql 才能使用索引对结果做排序。如果查询需要关联多张表,只有当 order by 子句的字段全是第一张表时才能使用索引排序。order by 查询同时也需要满足组合索引的最左前缀,否则也不能使用索引排序。
其实在开发中主要注意两点:
where 条件中的字段和 order by 中的字段能够是组合索引而且满足最左前缀。
order by 中的字段的顺序需要一致,不能存在 desc, 又存在 asc。