B+树索引(一)

        InnoDB存储引擎会自动为主键建立聚簇索引,聚簇索引的叶子节点包含完整的用户记录;还有对应的索引称为二级索引,若想要通过二级索引查找完整的用户记录,则需要进行回表操作。

        创建的主键索引和二级索引默认都是使用 B+ 树作为索引。

        通过索引查找记录时,都是自上而下查找的,每个页面中的记录都被划分为很多组,每个组中索引列值最大的记录在页内的偏移量会被当作槽记录在页目录中。因此可以在页目录中使用二分法快速定位到索引列等于某个值的记录。

聚簇索引图:

B+树索引(一)_第1张图片

二级索引图:

        图中叶子节点之间我画了单向链表,但是实际上是双向链表B+树索引(一)_第2张图片

        在二级索引中,B+树的叶子节点包括索引列和id列,这些记录是按照索引列的值从小到大的顺序排序的,如果索引列值相同,则按照主键id进行排序。

索引的代价

空间代价:

        我们每建立一个索引,都要为它建立一颗B+树,每一颗B+树的每一个节点都是一个大小为16kb的数据页,这将占用很大的存储空间。

时间代价:

        我们知道,各个数据页根据索引大小组成一个双向链表(内节点页里面包含的二级索引+主键值+子节点的页号组成),而数据页里面各个记录按照索引从小到大排列成一个单向链表。所以增删改操作会对节点的排序造成破坏,因此存储引擎需要额外的页分裂和页面回收操作;此外一条查询语句在执行前需要计算使用不同索引查询的成本,使用太多索引需要计算多次成本,浪费性能。

扫描区间

CREATE TABLE single table (
id INT NOT NULL AUTO INCREMENTkey1 VARCHAR(100)key2 INT,
key3 VARCHAR(100),
key_part1 VARCHAR(100)
key_part2 VARCHAR(100)
key_part3 VARCHAR(100),
common field VARCHAR(100)
PRIMARY KEY (id),
KEY idx keyl (key1),
UNIQUE KEY uk_key2 (key2),
KEY idx_key3 (key3),
KEY idx_key_part(key_part1, key_part2, key_part3))Engine=InnoDB CHARSET=utf8;

        对于select * from table where id>=2 and id<=100这条查询语句,他会生成[2,100]这个区间,我们可以通过聚簇索引对应的B+树快速定位到id=2的那条记录,然后沿着记录所在的单向链表向后扫描,直到离开这个区间位为止。

        对于select * from table where key2 in (1438,6328) or (key2>=38 and key2<=79);而我们正好又为key2建立了索引uk_key2。则会生成两个单点扫描区间[1438,1438],[6328,6328]。

有些搜索条件不能生成合适的扫描区间

        对于select * from table where key2>100 and common_field='abc',如果使用uk_key2进行查询的话,可以生成(100,+∞)扫描区间,但是uk_key2并不按照common_field排列,所以common_field根本起不到减小区间的作用;

        对于select * from table where key2>100 or common_field='abc',如果用uk_key2索引查询,等同于select * from table where key2>100 or true,等同于扫描uk_key2全部二级索引记录,每个还要回表查询,太慢了。

有些联合索引不能生成合适的扫描区间

        对于select * from table where key_part2='a',二级索引的记录不是直接按照key_part2排序的,key_part2='a'的记录可能并不相邻,所以不适用idx_key_part执行查询。

        对于select * from table where key_part1='a' and key_part3='c' ,对于符合key_part1='a'的记录来说,他们并不是按照key_part3排列的,因此用不上。虽然搜索条件key_part3='c'不能作为形成扫描区间的边界条件,但是idx_key_part的二级索引记录是包含key_part3列的,因此每当从idx_key_part索引的扫描区间['a','a']中获取一条二级索引记录,我们先判断这条二级索引记录是否符合key_part3='c',如果符合,再执行回表操作,如果不符合直接跳到下一条二级索引记录,这样可以减少回表操作的性能损耗。这种方式称为索引下推

        对于select * from table where key_part1<'b' and key_part2='a';对于key_part1<'b'的二级索引记录肯定是相岭的,但是key_part1<'b'的记录不是按照key_part2排列的,因此形成该扫描区间的边界条件是key_part1<'b',与key_part2='a'无关。

你可能感兴趣的:(b树,数据库,数据结构)