常见的索引模型
1、hash
hash表是一个键值对,把值放在数组里。key经过hash函数换算成一个确定的位置。value放在数组的这个位置。
但是多个key,经过hash函数不可避免的会换算成相同值,出现这种情况会拉出一个链表。但是链表不是有序的,区间查询会很慢。
所以hash只适合存放一些等值查询的场景。
2、有序数组
存在数组的普遍特点:查询效率很高,但是更新效率很低,因为中间插入一条数据,为了保证有序,需要移动后面所有的数据。
所以有序数组只适合存储仅查询的静态存储引擎。比如要保存一个城市所有2017年的数据,只需要查询,不需要更新。
3、innoDB引擎的B+树
每一个索引在innoDB引擎中都会维护一个B+树,例如一个表有个主键ID,字段k,并且字段k上存在索引,如下:
主键索引叶子节点存储的是全部数据,称之为聚簇索引。
非主键索引叶子节点存储的是主键ID,称之为二级索引。
一条sql语句:select * from table where id=100;只需要查询主键ID的B+树
select * from table where k=1;则需要查询索引k的B+树,得到对应的主键ID=100,再根据主键ID查询主键ID的B+树,我们称之为回表。
而如果sql语句变为:select id from table where k=1;则查询到索引k的B+树以后,就能得到想得到的ID值,而不需要再去查询主键ID的B+树,可以避免回表。
索引维护
B+ 树为了维护索引有序性,在插入新值的时候需要做必要的维护。以上面这个图为例,如果插入新的行 ID 值为 700,则只需要在 R5 的记录后面插入一个新记录。如果新插入的 ID 值为 400,就相对麻烦了,需要逻辑上挪动后面的数据,空出位置。
而更糟的情况是,如果 R5 所在的数据页已经满了,根据 B+ 树的算法,这时候需要申请一个新的数据页,然后挪动部分数据过去。这个过程称为页分裂。在这种情况下,性能自然会受影响。
为什么普遍要用一个自增字段当主键索引?
因为自增主键的每次插入数据都是追加操作,不存在页分裂。
而且自增主键的占用空间大小也比业务主键小。
最左原则
如下图所示:
select * from table where name like '张%';会走到索引
组合索引(name,age)
索引下推
上面的索引,查询条件:
select * from table where name=张三 and age=10;
会根据name=张三找到ID4,age符合然后进行回表查询,对应的ID5也会进行回表查询数据。而ID6的age不符合条件,则不进行回表,直接过滤掉。
所以此时创建组合索引(name,age)就比只有一个name的索引少了一些数据的回表操作。