索引的本质
索引是帮助MySQL 高效获取数据的排好序的数据结构。
MySQL 默认使用的是B+Tree数据结构。
磁盘的读取原理
在介绍结构之前,先简单说下磁盘的读取原理:
操作系统读写磁盘的基本单位是扇区,而文件系统的基本单位是簇(Cluster)。
MySQL在存储数据时数据都存储在磁盘的一道道扇区中,磁盘是旋转的,每次存储的数据可能会存储在不同的扇区中。
在读取时,磁道每次需要先进行寻道操作(速度慢),磁头只能进行左右移动,每一次寻道都是一次I/O操作。
如果数据都存在同一磁道中,那么旋转磁盘(速度快)就可以取到数据,不需要进行寻道操作,会大大减少开销。
(这里是我自己的理解,如果有不对的地方,欢迎大家指出来)
索引结构
二叉树
如果普通的一颗二叉树,在极端情况下会退化成链表结构。如图:
红黑树
和二叉树一样,在数据量大的情况下,树的深度太大,查询次数过高。
比如,有500W的数据,假如树的高度有20层,你要查询的数据刚好在叶子节点,那么需要进行20次寻道操作,和磁盘进行20次I/O交互操作,大大加大了开销。
HASH 索引
HASH索引,相比较于B-Tree 而言,不需要从根节点到叶子节点的遍历,可以一次定位到位置,查询效率更高,但缺点也很明显:
-
仅能满足全值查询,不能使用范围查询。因为是通过HASH值进行计算,HASH值是没规律的,所以只能精确查询,不能保证顺序和原来一致,所以不行进行范围查询。
-
不能进行排序,原因如上。
-
HASH碰撞。
B+Tree
-
B+Tree 是平衡树的一种,并且所有叶子节点位于同一层,是不会退化成链表结构的。
-
B+Tree 有一个度的概念,指节点的数据存储个数。度越大,节点保存的数据个数就越多。那么相应的树的高度降低,在进行查询时,需要进行的I/O操作更少,读取数据的速度更快。
-
非叶子节点不存储data,只存储key,可以增大度。叶子节点只存储data, 不存储指针,顺序访问指针,可以提高区间访问的性能。
-
进行查找操作时,首先在根节点进行二分查找,找到一个key所在的指针,然后递归的在指针所指向的节点进行查找。直到查找到叶子节点,然后在叶子节点上进行二分查找,找出 key 所对应的 data。
B+Tree 结构示意图
B+Tree 索引的性能分析
-
一般使用磁盘I/O次数评价索引结构的优劣
-
预读:磁盘一般会顺序向后读取一定长度的数据(页的整数倍)放入内存。
-
局部性原理:当一个数据被用到时,其附近的数据也通常会马上被使用。
-
B+Tree 节点的大小设为等于一个页,每次新建节点直接申请一个页的空间,这样就保证一个节点物理上也存储在一个页里,就实现了一个节点的载入只需一次I/O。
-
B+Tree 的度一般会超过100,因此高度非常小(一般为3到5之间)
MySQL 存储引擎
MyISAM (非聚集)
-
MyISAM 索引文件和数据文件是分离的。
-
MyISAM 引擎使用B+Tree 作为索引结构,MyISAM的索引文件仅仅保存数据记录的地址。
-
MyISAM中,主索引和辅助索引在结构上没有任何区别,只是主索引要求key是唯一的,而辅助索引的key可以重复。
MyISAM存储结构示意图
InnoDB (聚集)
-
数据文件本身就是索引文件。
-
InnoDB 引擎也使用B+Tree 作为索引结构,叶节点包含了完整的数据记录。
-
InnoDB的数据文件本身要按主键聚集,所以InnoDB要求表必须有主键(MyISAM可以没有)。
-
InnoDB中,主键索引包含了完整的数据记录,而辅助索引都引用主键作为data域。
InnoDB存储结构示意图
知道了InnoDB的索引实现后,就明白了为什么不建议使用过长的字段作为主键了,因为所有辅助索引都用主索引,过长的主索引会令辅助索引变得过大。
MyISAM和InnoDB区别
-
事物:InnoDB支持事务,可以使用 Commit 和 Rollback 语句。MyISAM不支持。
-
并发:MyISAM 只支持表级锁,而InnoDB 还支持行级锁。
-
外键:InnoDB 支持外键,MyISAM不支持。
-
备份:InnoDB 支持在线热备份,MyISAM不支持。
-
崩溃恢复:MyISAM 崩溃后发生损坏的概率比 InnoDB 高很多,而且恢复的速度也更慢。
-
其它特性:
-
InnoDB是聚集索引,MyISAM是非聚集索引。
-
InnoDB不保存表的具体行数,MyISAM用变量保存了整个表的行数。
-
InnoDB不支持全文索引,MyISAM支持全文索引。
-
MyISAM 支持压缩表和空间数据索引。
索引分类
索引分类
-
主键索引:是一种特殊的唯一索引,不允许有空值。设定为主键后数据库会自动建立索引innodb为聚集索引。
-
唯一索引:索引列中的值必须是唯一的,但是允许为空值。
-
单值索引:索引只包含单个列,但一个表中可以有多个单值索引。
-
复合索引:在表中的多个字段组合上创建的索引,只有在查询条件中使用了这些字段的左边字段时,索引才会被使用,使用组合索引时遵循最左前缀原则。
-
全文索引:只有在MyISAM引擎上才能使用,只能在CHAR、VARCHAR、TEXT类型字段上使用全文索引,在一堆文字中,通过其中的某个关键字等,就能找到该字段所属的记录行。比如有"怡红院、金凤楼"等等,可以 通过金凤楼,可能就可以找到该条记录。
-
空间索引:只有在MyISAM引擎上才能使用,创建空间索引的列,必须将其声明为NOT NULL。空间索引是对空间数据类型的字段建立的索引,MySQL中的空间数据类型有四种,GEOMETRY、POINT、LINESTRING、POLYGON。在创建空间索引时,使用SPATIAL关键字。
最左前缀原则
-
B+Tree 是按照第一个关键字进行索引,然后在叶子节点上按照第一个关键字、第二个关键字、第三个关键字…进行排序。使用复合索引的时候,会根据你的索引顺序,从左开始匹配。
-
索引只能用于查找key是否存在(相等),遇到范围查询(>、<、between、like左匹配)等就不能进一步匹配了,后续退化为线性查找。
复合索引的底层存储结构
索引的优缺点
优点
-
提高查询效率(降低IO使用率)。
-
降低CPU使用率, 例如:
...order by age desc
,因为B+Tree 索引本身就是一个排好序的结构,因此在排序时可以直接使用。
缺点
-
索引本身很大,实际上索引也是一张表,该表保存了主键与索引字段,并指向实体表的记录,所以索引列也是要占用空间。
-
索引会降低增删改的效率,MySQL不仅要保存数据,还要保存一下索引文件每次更新添加了索引列的字段, 都会调整因为更新所带来的键值变化后的索引信息。
-
索引不是所有情况均适用:A. 少量数据 B.频繁更新的字段 C.很少使用的字段
需要建立索引的情况
-
主键自动建立唯一索引。
-
频繁作为查询条件的字段应该创建索引,WHERE 后面的语句。
-
查询中与其它表关联的字段,外键关系建立索引。
-
单键/组合索引的选择问题,who?在高并发下倾向创建组合索引。
-
查询中排序的字段,排序字段若通过索引去访问将大大提高排序速度。
-
查询中统计或者分组字段。