索引的定义
索引是帮助MySQL高效获取数据的排好序的数据结构
索引数据结构有:二叉树,红黑树,Hash表,B-Tree,B+Tree
对于数据库表的InndB索引来说,底层即是B+Tree数据结构
B-Tree
(1) 叶子节点具有相同的深度,叶子节点的指针为空
(2) 所有的索引元素不重复
(3) 节点中的数据索引从左到右递增排列
B+Tree(B-Tree变种)
(1) 非叶子节点不存储data数据,只进行存储索引(这个索引是冗余的索引,多余的),可以放更多的索引
分析:
首先我们需要知道:一个数据页的大小MySQL原则规定为16KB(16384字节)。
1.如果以现在的B+树为例,非叶子节点只进行存储索引,一个索引大小为(8+6)个字节大小,一个索引包含指向下一个数据页的地址值和整形主键值。
那么非叶子节点的一个数据页可以存储(16384除以14==1170)1170条记录的索引。
假如说B+树为三层,那么第一层只有一个数据页,这一个数据页可以存储1170条记录的索引。
第一层这一个数据页中的每一条索引对应第二层一个数据页,所以第二层又对应有1170条数据页,一个数据页存储1170个索引,所以第二层可以存储1170X1170条索引。
第三层存储真实的data数据,一条数据记录假设为1KB,那么一个数据页可以存储16条data数据记录。第二层有1170X1170条索引,对应第三层可以存储1170X1170X16=千万级别data真实数据量。
2.如果以之前的B树数据结构为例,非叶子节点的数据页中也进行存储的是data数据,一条数据记录大概为1KB,那么一条数据页仅仅存储16条记录。那么要达到千万级别的数据量存储需要16的n次方,这个层数将会远远大于3。
(2) 叶子节点包含所有的索引字段
分析:
对于B+树结构而言,非叶子节点存储的是冗余索引,这个冗余索引的作用就是确定真正的data数据是位于叶子节点中的哪一个位置的,通过的是二分法查找。所有真实的data数据是存储在叶子节点中的。
(3) 叶子节点用指针相互连接,提高区间访问的性能
分析:
相邻的叶子节点之间是用指针相互连接的,指针的具体实现是通过让相邻的节点划分一块内存空间用来互相存储对方节点的地址。
为什么要搞一个指针相互连接?
提高区间访问的性能。
举一个例子,假设说我们要查询一个区间范围的数据量(20~1000)。
1.如果是之前的B树结构,虽然叶子节点的数据也是从小到大进行排序的,但是相邻节点之间没有指针指向,当我们锁定了20后也无法直接一路向右锁定大于20的数据,我们还是要重新进行查询。
2.但如果是现在的B+树结构,当我们通过冗余索引锁定了叶子节点的元素20,那么通过这个相邻指针,我们可以一路向右查找,一直锁定到1000为止。因为叶子节点的元素是按照大小顺序从左往右排布的。
Hash
(1) 对索引的key进行一次hash计算就可以定位出数据存储的位置
分析:十分快速方便,但是MySQL底层使用的是B+树作为存储数据的存储结构
(2) 很多时候Hash索引要比B+树索引更加高效
分析:同(1)分析即可
(3) 仅能满足"=","IN",不支持范围查询
分析:相比B+树,该Hash结构不支持范围查询,因为Hash表是根据key键值生成哈希值,根据哈希值确定在哈希表哪一个索引,是随机的。
可能说一个key键值为"李四1",一个key键值为"李四2",这两个key键值生成的哈希值相差甚大,最终索引位置相差甚远!
所以不支持范围查询。
但是B+树的叶子节点中是把所有的数据记录从小到大依次存储的,并且相邻的叶子节点之间使用指针指向,适合范围查询。
(4) hash冲突问题会发生
分析:不同的key键值生成的哈希值相同,最终生成的索引值相同,不同的对象竞争想要存储到同一个索引位置,发生Hash冲突!
MyISAM存储引擎索引实现
MyISAM索引文件和数据文件是分离的(非聚集)
为什么Myisam引擎对应索引及数据为非聚集?InnoDB是聚集?
一张图告诉你结果:
InnoDB索引实现(聚集)
(1) 表数据文件本身就是按B+Tree组织的一个索引结构文件
(2) 聚集索引-叶子节点包含了完整的数据记录
(3) 为什么建议InnoDB表必须建主键,并且推荐使用整型的自增主键?
分析:
为什么建议InnoDB表必须建主键?
主键查询是极其高效的。
如果我们不主动建主键,InnoDB底层也会默认帮您在众多字段中,去选择一个字段作为主键(该字段必须满足字段对应的所有记录值都是不相同的,保证主键字段值的唯一性)。若没有满足该条件的字段作为主键的话,InnoDB底层会模拟一个rowid字段作为主键。但是InnoDB帮我们建主键所消耗的性能远超过自主建索引所消耗的性能,所以最好主动创建一个主键,一般选择的就是一个自增的id字段作为主键。
为什么推荐使用整型的自增主键?
使用整形:
是因为数据类型为整形的主键字段在我们使用主键查询操作的时候更加便于进行比对操作,如果使用数据类型为字符串的字段作为主键的话,我们根据主键索引进行查询操作时,比对是根据ASCII码表进行的,效率远不及整形直接比对!
使用自增:
数据底层存储使用的数据结构为B+树,B+树存储数据记录是在叶子节点中的。但是数据记录存储是从小到大依次进行排列存储的。
如果不使用自增字段插入,随意进行插入一个大小的字段,这个随意的大小字段会插入到任意的数据页中,那么很可能会造成页分裂等大消耗性能的情景。
如果使用自增字段插入,根据叶子节点中所有存储的记录是从小到大排列存储的原则:每一次新插入一个自增的记录是插入到最后面的叶子节点,当发现该叶子节点存储的数据达到一定时,下一个数据就不会再存储到这个叶子节点了,会新开辟一个叶子节点。因此页分裂的情况就不会再出现了,这样极大的节省性能消耗。
(4) 为什么非主键索引结构叶子节点存储的是主键值?(一致性和节省存储空间)
分析:
一个数据库表可能会有十几个,甚至二十几个字段,除了主键字段设置为主键索引外。其余的字段也可以进行设置索引,即是非主键索引,也称为二级索引。
为什么非主键索引结构叶子节点存储的是主键值?
(1)保证一致性
当通过非主键索引(二级索引)进行查询一条记录,我们需要先根据非主键索引结构叶子节点中存储的主键值进行一次回表操作,回表操作即是找到主键索引对应的B+树存储结构表,找到回表到的B+树存储结构的叶子节点,找到对应的数据记录。这样进行查询,即使数据库表记录更改,也可以保持数据一致性。
(2)节省存储空间
如果一新建一个非主键索引,叶子节点就进行存储所有的记录数据,这是十分浪费磁盘空间的。
联合索引的底层存储结构长什么样?
为什么使用联合索引?
-
"一个顶三个"。建了一个(a,b,c)的复合索引,那么实际等于建了(a),(a,b),(a,b,c)三个索引,因为每多一个索引,都会增加写操作的开销和磁盘空间的开销。对于大量数据的表,这可是不小的开销!
-
覆盖索引。同样的有复合索引(a,b,c),如果有如下的sql: select a,b,c from table where a=1 and b = 1。那么MySQL可以直接通过遍历索引取得数据,而无需回表,这减少了很多的随机io操作。减少io操作,特别的随机io其实是dba主要的优化策略。所以,在真正的实际应用中,覆盖索引是主要的提升性能的优化手段之一
-
索引列越多,通过索引筛选出的数据越少。有1000W条数据的表,有如下sql:select from table where a = 1 and b =2 and c = 3,假设假设每个条件可以筛选出10%的数据,如果只有单值索引,那么通过该索引能筛选出1000W10%=100w 条数据,然后再回表从100w条数据中找到符合b=2 and c= 3的数据,然后再排序,再分页;如果是复合索引,通过索引筛选出1000w 10% 10% *10%=1w,然后再排序、分页,哪个更高效,一眼便知
通过分析下图这个案例说明索引最左前缀原理:
分析:首先要明白索引的定义:索引是帮助MySQL高效获取数据的排好序的数据结构
对于联合索引而言:只有当name字段的值大小一致相同时的那些记录,那些记录对应的age字段大小才是从小到大依次排好序的。(如图:name都是Bill的数据记录,对应的age才是从小到大依次排序的。对于HanMeiMei,Jeff这些不同name对应的age压根不是依次排序的)
所以对于最左前缀原理的作用就在于此,因此对于联合索引而言:如果相对最左的字段不走索引,那么相对较右边的字段走索引也走不成。