索引模型
在innodb存储引擎下,只有主键索引(聚餐索引)的叶子节点才真正了存储的记录数据.其他的非聚簇索引叶子节点只存储索引列的值,叶子节点保存了叶子节点值和该条记录的主键值.
- 设置了id为主键以后,会自动为id列添加主键索引(聚簇索引),如果没有主键,则存储引擎会找一个有唯一约束的列生成聚簇索引,如果没有,则存储引擎会自已生成一个隐式的列来当做这条数据的主键.该聚簇索引的BTree示意图如下:
聚簇索引数据结构中叶子节点存储了数据,根据主键排好序,建立好索引,此时主键的长度尽可能不宜过长,因为一个目录页大小一般是16kb,而主键长度越短,一个目录页能存储的主键就越多,将来目录页节点就会越少,便于维护。这里注意根目录页是常驻内存的,因为每次根据主键查询时,必定会先加载根目录页,而只要加载一个目录页,就会发生一次io,降低了性能,所以根目录页常驻内存,就减少了一次io。同时根目录页保存了二级节点每个目录页的最小主键值,这样就可以确定这个主键在哪个目录页上,同时呢,二级目录页里面又保存了叶子节点上的每个具体的主键以及对应的数据记录,由于主键的唯一性,此时确定出一条记录就可不再查找。
举例:
select * from tb where id = 20
查找id等于20这条数据,首先来到根目录页,发现也25的最小值是28,则直接去查找页19,页19又发现页73的最小记录是24,此时直接查找页24,最终查找到了。
- 如果为name字段添加了索引,则该索引BTree示意图如下:
非聚簇索引的BTree的叶子节点不存储真正数据,而是存储了主键值,当确定某一条主键值,会进行回表查询,也就是拿着id去聚簇索引查询。
sql示例:
select * from tb where name = 'aabbcc'
此时还是根据name查询,从页19到页24,进而确定到 name = 'aabbcc’这条数据,然后根据对应的主键,拿着主键去聚簇索引中进行回表查找。
为什么目录节点除了保存name字段外,还保存了主键?因为如果此时查询条件带了主键,则还可以比对主键是否相等。
- 如果是name age class_id 字段的联合索引,则BTree示意图如下:
此时联合索引创建时顺序应是
create index mul_name_age_class_id on tb(name,age,class_id);
那么在这种情况下会是先按照name进行排序,如果name相同,再根据age进行排序,如果age相同,再按照class_id排序,也就是说,如果不看name字段,那么age和classid是乱序的。这就要求在查询时,必须要使用上name字段,否则索引就使用不上了。
select * from tb where age = 18 and name = 'zs' and class_id
以上sql是可以使用上索引的,虽然索引的顺序是name,age,classid,但是查询条件可以不按照这个顺序写,优化器会进行优化。
索引设计规范
- 适合建立索引的情况
- 表数据量较大
- 经常出现在where后的字段
- 列值散列度高的
- 增删改操作较小的
- 列值使用与等值查找的
- 不适合建立索引的情况
- 表数据量低于1000,当表数据量比较小时,不要建立索引了,查询速度几乎没有影响,还想去维护索引,影响性能。
- 散列度低的不适合。
- 在where中用不到的字段 不要使用索引
- 不要定义冗余或者重复的索引
- 避免对经常更新的表使用过多的索引
- 索引失效的情况
- 索引设计注意事项
-
如果是单列索引,则尽量选择散列度高的列建立索引 -
主键要尽可能单调递增,保持有序 -
如果是联合索引,则尽量将模糊查找和范围查找放到后面(像创建时间这种列),同时将查询度高的且是等值查找的放在前面 -
一张表尽量不要超过6个索引 -
如果是为字符串类型的列建立索引,则可考虑只为该字段前固定字符数来建立索引。 -
像表中存在可能为null的字段可以给一个默认值,varchar类型可以默认是空串,int类型默认可以是0,这样就不会有where is not null条件,从而丢失索引。
|