索引规范
- 自增列或全局ID做InnoDB主键,推荐用独立于业务的
AUTO_INCREMENT
列或全局ID生成器做代理主键 - 非唯一索引命名默认为
idx_字段名
来命名 - 唯一索引使用
uk_字段名
来命名 - 主键索引
pk_
来命名 - 单张表索引数量建议控制在5个以内
- 合理添加索引可以改善查询,但会减慢更新,并不是索引越多越好,索引字段数最好不超过表字段的20%
- 字符字段必须建前缀索引,
like '%name%'
不会使用索引 - 不在索引列做运算,否则无法使用索引,导致全表扫描
- 尽量不用外键,高并发时容易死锁(外键使得表之间相互耦合,影响update/delete等SQL性能,有可能造成死锁,高并发情况下容易成为数据库瓶颈)
- 非必要不要进行
JOIN
查询,如果要进行JOIN
查询,被JOIN
的字段必须类型相同,并建立索引 - 理解组合索引最左前缀原则,避免重复建设索引,如果建立了(a,b,c),相当于建立了(a), (a,b), (a,b,c)
- 防止因字段类型不同造成的隐式转换,导致索引失效
- 避免负向查询和%前缀模糊查询,使用不了索引,导致全表扫描
哪些字段上建立索引效果好?
- 索引列应该建立在数据区分度值大的列上,区分度越大,数据的不重复率就越大,新建索引效果就越好。
为什么要创建组合索引呢?
- 在MySQL中执行查询时,只能使用一个索引,如果我们在多列(
last_name、first_name、age
)上分别建索引,执行查询时,只能使用一个索引,MySQL会选择一个最严格(获得结果集记录数最少)的索引。 - 理解组合索引最左前缀原则,避免重复建设索引,如果建立了(a,b,c),相当于建立了(a), (a,b), (a,b,c)
- 如果有一个组合索引(
col_a,col_b,col_c
),下面的情况都会用到这个索引:
col_a = "value";
col_a = "value" and col_b = "value";
col_a = "value" and col_b = "value" and col_c = "value";
col_b = "value" and col_a = "value" and col_c = "value";
- 对于最后一条语句,MySQL会自动优化成第三条一样(
=
和in
可以乱序,比如a = 1 and b = 2 and c = 3
建立(a,b,c
)索引可以任意顺序,MySQL的查询优化器会优化成索引可以识别的形式)。下面的情况就不会用到索引:
col_b = "value";
col_b = "value" and col_c = "value";
理解单列索引、多列索引以及最左前缀原则
示例: 现在我们想查出满足以下条件的用户id:
mysql>SELECT `id` FROM user WHERE last_name`='L' AND `first_name`='ZW' AND `age`= 30 ,因为我们不想扫描整表,故考虑用索引。
单列索引:
ALTER TABLE user ADD INDEX last_name (last_name);
将lname列建索引,这样就把范围限制在 last_name='L'
的结果集1上,之后扫描结果集1,产生满足 first_name='ZW'
的结果集2,再扫描结果集2,找到 age=30
的结果集3,即最终结果。
由于建立了 last_name
列的索引,与执行表的完全扫描相比,效率提高了很多,但我们要求扫描的记录数量仍旧远远超过了实际所需要的。虽然我们可以删除 last_name
列上的索引,再创建 first_name
或者age
列的索引,但是,不论在哪个列上创建索引搜索效率仍旧相似。
多列索引:
ALTER TABLE user ADD INDEX lname_fname_age (last_ame,first_name,age);
为了提高搜索效率,我们需要考虑运用多列索引,由于索引文件以B-Tree格式保存,所以我们不用扫描任何记录,即可得到最终结果。**多列索引中其实还有一个字段的先后顺序问题,一般是将区分度较高的放在前面,这样联合索引才更有效。 **
最左前缀:
顾名思义,就是最左优先,上例中我们创建了 lname_fname_age
多列索引,相当于创建了(last_name
)单列索引,(last_name,first_name
)组合索引以及(last_name,first_name,age
)组合索引。
注:在创建多列索引时,要根据业务需求,where子句中使用最频繁的一列放在最左边。 MySQL会一直向右匹配直到遇到范围查询(>、<、between、like)就停止匹配
在什么情况下建立索引呢?
一般来说,在 WHERE
和 JOIN
中出现的列需要建立索引。
索引的不足之处
- 上面都在说使用索引的好处,但过多的使用索引将会造成滥用。因此索引也会有它的缺点:
虽然索引大大提高了查询速度,同时却会降低更新表的速度,如对表进行INSERT、UPDATE和DELETE。因为更新表时,MySQL不仅要保存数据,还要保存一下索引文件。
建立索引会占用磁盘空间的索引文件。一般情况这个问题不太严重,但如果你在一个大表上创建了多种组合索引,索引文件的会膨胀很快。索引只是提高效率的一个因素,如果你的MySQL有大数据量的表,就需要花时间研究建立最优秀的索引,或优化查询语句。
使用索引的注意事项
- 索引不会包含有NULL值的列
只要列中包含有NULL值都将不会被包含在索引中,复合索引中只要有一列含有NULL值,那么这一列对于此复合索引就是无效的。所以我们在数据库设计时不要让字段的默认值为NULL。
- 使用短索引
对串列进行索引,如果可能应该指定一个前缀长度。例如,如果有一个CHAR(255)的列,如果在前10个或20个字符内,多数值是惟一的,那么就不要对整个列进行索引。短索引不仅可以提高查询速度而且可以节省磁盘空间和I/O操作。
- 索引列排序
MySQL查询只使用一个索引,因此如果where子句中已经使用了索引的话,那么order by中的列是不会使用索引的。因此数据库默认排序可以符合要求的情况下不要使用排序操作;尽量不要包含多个列的排序,如果需要最好给这些列创建复合索引。
- like语句操作
一般情况下不鼓励使用like操作,如果非使用不可,如何使用也是一个问题。like “%aaa%” 不会使用索引而like “aaa%”可以使用索引。
- 不要在列上进行运算
select id from user where YEAR(adddate)<2007;
将在每个行上进行运算,这将导致索引失效而进行全表扫描,因此我们可以改成
select id from user where adddate<‘2018-01-01’;
- 不使用NOT IN、<>、BETWEEN操作