什么是索引?
索引的本质是独立于数据本身之外的一种数据结构,它是基于某个或某些字段建立一堆有序且可快速高效查找的数据,类似字典中首字母索引,或图书馆中图书的索引;
索引存在核心是为了过滤数据+检索,同时能快速定位数据的实际物理位置。其根本作用是要减少查询时的逻辑和物理IO或CPU运算,索引在运行中很多时候会加载到缓存中,通过索引定位数据在磁盘上的物理地址,进行数据获取。
在mysql上索引最常用的数据结构就是B+树,类似平衡二叉树的平衡多叉树,它的叶子节点会指向数据相关物理地址,从而实现快速定位数据;其树的高度就决定了定位数据位置需要IO次数,假设总的数据量为N,树的每个节点的数据量为m,这树高度就为log(m+1)N,所以通过索引只需要log(m+1)N次IO加上计算就可以定位到数据,这对比整个数据表遍历会是一个极大的提升(完全不在一个数量级上)。
具体索引的原理,推荐看一下美团点评团队的一篇文章:索引原理和慢查询优化
维护索引需要的什么代价?
1. 索引需要占用额外储存空间,包含内存空间,也就意味着额外的IO操作
2. 数据表每一次更新,需同步调整相关的索引,额外的CPU和IO负载。
3. 当数据量增加到一定程度,索引本身的数据结构会特别庞大,维护起来成本会非常高
之前公司有做过数据迁移,千万级别的表,在有索引插入的情况下,执行了几个小时,但去掉索引,迁移完成后重建索引,不到1小时就完成
索引类型介绍
聚集索引和非聚集索引
在存储结构上不同可以将索引分为聚集索引和非聚集索引;一般来说,基于聚集索引的插入会特别快,碎片化数据也会少很多,单条查询也会快很多。
聚集索引
聚集索引单表上是唯一的,一个表最多只能有一个聚集索引。聚集索引在物理存储上有有序的,即代表数据行在磁盘的是按照聚集索引的顺序存储的。聚集索引的叶子节点是数据行,即在聚集索引的叶子节点上,直接可以找到数据本身。
非聚集索引
非聚集索引可以在单表存在多个逻辑上有序,物理上无序非聚集索引的叶子节点上存储的是索引块,里面对应的是相关数据的物理地址,并不是数据本身
类型划分
【普通索引】
【唯一索引】 值唯一,可以为null
【主键索引】 值唯一,不可为null,且每个表唯一
【组合索引】 多个字段的组合索引
【全文索引】 富文本,简易搜索引擎使用
数据结构划分
B+树
B-树
哈希索引
使用索引需要注意什么?(阅读划重点)
1. 频繁更新或读少写多的表不适合过多索引
2. 最好的索引是能在平衡树叶子节点上快速定位到数据,过滤掉不在查询区间的数据,这就意味重复性低的字段更适合作为索引,比如自增键、时间戳、唯一字段等;不适合索引的字段比如少量状态、逻辑删除标记字段、性别等
3. 索引列切记不能有空值(null),请设置字段not null+默认值,null会影响索引效率
4. 数字字段比字符字段更适合索引,索引的核心在排序,数字的比较CPU运算量更少,需要的内存、逻辑IO和物理IO也更少,建立字段时如果可以尽量使用数字类型
5. 索引的建立和查询是基于字段的排序,如果字符字段过长,会出现极大增加排序负荷,并且会导致额外IO,使用索引字段请注意调整字段长度,不定长度或过长字段不适合索引,相反较短的字段就更适合;而一般数字字段都比字符字段短,占用空间也越少
6. 每个表最好都要有一个主键,使用主键在做表更新和删除会更加方便和安全,同时方便扩展、松耦合系统;在innodb引擎上,建议使用于业务无关的自增键来做主键,因为innodb会默认使用自增键来作为表的聚集索引(见索引类型章节),可以增加更新效率,减少数据碎片;自增主键还有特点是有时序,在业务逻辑上很多时候很有用。
7. 组合索引是指在业务查询逻辑,经常一起出现的字段组合成一个索引,会更加高效;列重复值较多的、单独索引效率低的字段也可考虑使用组合索引(比如一级分类和二级分类的组合索引);组合索引的建立要尽量对查询形成索引覆盖,其前置索引字段一定是使用要是最频繁的;
8. 组合索引要注意最左前缀匹配原则,组合索引的原理是将多个字段按照提供的顺序组合后的整体作为索引排序;在mysql中,引擎会一直向右匹配直到遇到范围查询(>、<、between、like)就停止匹配,所以组合索引(a,b,c)可以匹配(a=1,b>1)的查询,但匹配不到(a=1,b>1,c=1),其中c=1会进行单独查询;(a=1,b=1)会用到索引,(b=1,c=1)会用不到。同样模糊查询中(a like 'xxx%')会用到a索引,但(a like '%xxxx')用不到;
9. 尽量的扩展索引,不要新建索引。根据实际业务情况,调整索引时,如无必要,不要新增索引,考虑扩展组合索引
10. 索引列请保持干净,不要有任何计算,会导致索引无效;比如unix_timstamp()-[timefield] < 180,替换为[timefield]>unix_timstamp()-180
11. 反向查询会使索引无效,比如a<>1,not in查询,not in使用not exist替代
12. 使用组合索引字段是可以乱序的,查询引擎会根据索引来调整顺序。(a,b,c)组合索引,会匹配c>1 and a=2 and b=3的情况
13. 使用union替代or,在某些情况or查询会导致映射不到索引,比如select * from table where a=1 or b=2 替换为 select * from table where a=1 union select * from table where b=2,这样就可以使用a,b两个索引;同一字段的or查询使用in替代。在新版的mysql中(5.6?)查询引擎已可自动转化,但遇到复杂sql时,可能会转换失败
14. 如果查询用到多个索引的时,可以手动将过滤掉更多数据的索引字段放到前面,可以优化查询
15. 大表比如千万级别的表调整索引会很长时间锁表,尽量在一开始考虑好索引建立
16. 索引越多,维护成本也越高;不要过度索引,能在业务逻辑层做优化的,就不要放到数据库上来,特别是在高并发系统中,数据库一般都是瓶颈,请解放数据库。索引的使用的大的方向是:宜短不宜长,宜少不宜多
17. 技术的事没有绝对标准,可以考虑实际业务需求添加需要索引,但添加之前要考虑上面的事项。