MySQL学习一:深入理解MySQL底层数据结构
1、索引数据结构红黑树,Hash,B+树详解
2、千万级数据表如何用B+树索引快速查找
3、聚集索引&聚簇索引&稀疏索引到底是什么
4、为什么DBA总推荐使用自增主键做索引
5、联合索引底层数据结构又是怎样的
6、Mysql最左前缀优化原则是怎么回事?
MySQL5.5版本之后默认的就是innodb存储引擎,所以我们接下来说的都是基于innodb存储引擎来说的。
对于存储引擎来说,数据结构有二叉树,红黑树,hash,B树,B+树,为什么MySQL的innodb使用了B+树呢?
针对查询,首先要明确的一点就是IO操作是非常消耗性能的,是占用时间最久的操作之一;我们说的优化绝大部分就是压缩IO的时长,例如压缩单次IO时长,或者就是减少IO的次数。
对于二叉树来说,最好的结构就是平衡二叉树,此时查询效率是比较高的;但是如果数据量比较多的情况下,会令树很高,此时IO就要进行多次,耗时就增长了很多;还有一个情况就是插入有序的数据,二叉树会退化为链表,此时查询数据类似遍历链表,效率也不高。
但是也要考虑插入的性能,如果插入的数据无序,那么可能会破坏平衡结构,令树重新构建平衡,此时插入性能差。
对于红黑树来说,其红黑结构是必须要满足的,所以就导致了插入删除数据之后要频繁进行重建树结构,这样是很消耗性能的。还有一点就是如果数据很多的情况下,红黑树的深度也会很深,就要进行多次IO之后才能获取到目标数据,查询效率低。
对于hash来说,其hash算法是很高效的,通过hash进行精确单个定位效率是最高的。但是hash也有缺点,一个是可能会导致hash冲突,冲突之后就会令数据拉链构成链表,该节点数据要遍历查询;还有一个就是hash是精确定位的,可以使用=和in,但是对于范围查询,大小查询的支持是很差的;实际情况下范围查询才是使用频率最高的。
B树,B树的效率已经比二叉树和红黑树要高了。因为B树比较矮胖,提出了页的概念,默认一个页是16KB,整个页都来存储数据,进行分层存储;B树的特点是每个节点都是存储的完整数据,叶子节点之间没有联系,都是要经过根节点进行向下定位。同时没有节点数据是冗余的,都是独一份的,并且每层从左到右都是有序存储的。
备注:innodb和myisam都是使用的B+树吗?
B+树,正常B+树的叶子节点是单向链表;但是MySQL的innodb存储引擎索引的B+树使用的是变种的B+树,也就是叶子节点是双向链表。
优化的B+树,其非叶子节点存储的是索引数据,并且非叶子节点都进行了数据的冗余,叶子节点之间是双向链表;
针对innodb来说,如果是主键索引,那么叶子节点存储的就是整行数据;非主键索引,其叶子节点存储的是主键的值,如果有需要再通过主键的值去主键索引查询数据,这就是回表。回表针对innodb来说只发生在非主键索引上。
针对myisam来说,不论是否是主键索引结构,其叶子节点存储的都不是整行数据,而是该行数据的地址值。
备注:innodb的非主键索引的叶子节点存储的是主键的值,还是地址值?如何通过主键值去定位到整行数据?
innodb和myisam的区别
存储引擎是针对表来说的,一个库的表可以有多个存储引擎。
innodb引擎结构的表有两个文件,一个是表结构文件,一个就是值文件,所以可以针对主键索引设置成叶子节点存储整行数据。
myisam引擎结构的表有三个文件,一个是表结构文件,一个是索引文件,一个是真正数据文件。所以其索引结构的叶子节点存储的都是数据的地址值。
别的区别:innodb支持行锁,支持事务,支持外键,支持崩溃之后快速恢复数据;myisam则不支持这些。
约束
主键约束:非空且唯一;
唯一约束:全表要是唯一值,可以为空null;
非空约束:数据必须不能是null,全表可以有重复值;
外键约束:强制关联约束,不太建议物理外键,可以使用逻辑外键。
为什么建议每张表加一个主键索引?并且推荐使用自增的整数类型当主键?
因为每张表在创建的时候,innodb会去扫描有没有显式的指定主键列。如果没有,会去看有没有设置的唯一且非空的列,有就把这个列当主键;如果没有,就隐式的创建一个rowid当主键。注意后面两种都是要进行额外操作的,远离了MySQL数据库存储数据查询数据的初衷,故最好建立一个索引。innodb会指定通过主键来构建主键索引数据结构。
因为我们查询数据很多情况下是比较大小,相等的情况,此时整数类型进行这些数学运算是最快的。所以建议主键使用整数类型。又因为索引是排好序的一个数据结构,所以使用自增的整数,就无序考虑和之前的数据比较大小,只需要顺序的往后写,写满了再开一页就好了,这样的效率是很高的。如果是无序的,那么每次插入数据,就要遍历全部叶子节点,可能还需要进行重构,这样是很耗时的。
数据量
针对innodb来说,正常主键使用bigint充当主键索引,bigint是8B(byte),索引值之间也有键连接,大概是6B,那么总共是14B,又因为一页默认大小是16KB,表中每行数据一般不超过1kb;简单计算可以得到一张表大概有千万级数据;但是这个是大概计算,不是精确的值。
为什么只有主键才带有整行的数据呢?
第一是为了节省存储空间,数据库磁盘也是比较昂贵的,要节省使用;同时也是为了保证数据一致性,让修改数据时更快;如果每个索引都维护一份整行数据,那么每次修改都要去遍历查询所有的索引结构,工作量较大。
聚簇索引,回表
通常说的聚集索引,聚簇索引,我们可以大致认为等同于主键索引,因为所谓的聚就是直接有整行的数据,在innodb中,只有主键索引才符合这个标准。
通常说的二级索引,就是非主键索引;二级索引可以有一列,也可以有多列,一般二级索引建议使用多个列一起使用,这样可以节省存储空间。索引结构并不是越多越好,索引的创建维护都是要消耗性能的。
所谓回表,就是针对的非主键索引,并且查询的列多于主键列加上该二级索引的列,或者所查列有一或多个不在此二级索引上;此时找到主键值之后,要再去主键索引去查询值,这就是所谓的回表;在查询的二级索引结构中无法拿到所查的全部数据就要去主键结构中查。
联合索引底层数据结构又是怎样的
联合索引一般都是多个列一起构成的,将常见的列统一使用索引,这样可以节省空间。
拿ABC三个列当联合索引,此时相当于有三个索引,分别是A,AB,ABC;由此可见在整个索引结构中,A是有序排列的;在A相等的情况下,此时B是有序的(不考虑C);在AB都相等的情况下,C是有序的。也就是说BC是局部有序的,全局无序。这个要注意的。所以使用BC来查询,是无法使用到索引的,是要全表扫描的。
这也可以说是最左前缀优化,就是针对联合索引的查询,首先要保证最左边的也在查询条件中,否则该索引无效。