声明:本文只是个人笔记,如果有人觉得好的话,上掘金小册查看作者的书
索引的代价:
- 空间上的代价:占用的存储空间大
- 时间上的代价:每次对表中的数据进行增、删、该操作时,都需要去修改各个B+树索引。增、删、改操作可能会对节点和记录的顺序造成破坏,所以存储引擎需要额外的时间进行一些记录移位、页面分裂、页面回收等操作来维护节点和记录的顺序。给性能拖后腿,
B+树索引适用的条件
首先创建一个表
CREATE TABLE person_info(
id INT NOT NULL auto_increment,
name VARCHAR(100) NOT NULL,
birthday DATE NOT NULL,
phone_number CHAR(11) NOT NULL,
country varchar(100) NOT NULL,
PRIMARY KEY (id),
KEY idx_name_birthday_phone_number (name, birthday, phone_number)
);
表中存在的索引:
聚簇索引:表中的主键是id列,它存储一个自动递增的整数。所以InnoDB存储引擎会自动为id列建立聚簇索引。
二级索引:idx_name_birthday_phone_number,它是由3个列组成的联合索引。所以在这个索引对应的B+树的叶子节点处存储的用户记录只保留name、birthday、phone_number这三个列的值以及主键id的值,并不会保存country列的值。
二级索引的B+树简略图如下
该B+树的页面和记录的排序方式:
- 先按name列的值进行排序
- 如果name列的值相同,则按照birthday列的值进行排序
- 如果birthday列的值也想通,则按照phone_number的值进行排序
什么时候回用到B+树索引呢?
1. 全值匹配—如果我们的搜索条件中的列和索引列一致
比如:
SELECT * FROM person_info WHERE name = 'Ashburn' AND birthday = '1990-09-27' AND phone_number = '15123983239';
查询过程:
- 因为B+树的数据页和记录先是按照name列的值进行排序的,所以先可以很快定位name列的值是Ashburn的记录位置。
- 在name列相同的记录里又是按照birthday列的值进行排序的,所以在name列的值是Ashburn的记录里又可以快速定位birthday列的值是’1990-09-27’的记录。
- 如果很不幸,name和birthday列的值都是相同的,那记录是按照phone_number列的值排序的,所以联合索引中的三个列都可能被用到。
那么Where子句中几个搜索条件的顺序对查询结果有影响吗?也就是说如果我们调换name、birthday、phone_number这几个搜索列的顺序对查询的执行过程有影响么?
SELECT * FROM person_info WHERE birthday = '1990-09-27' AND phone_number = '15123983239' AND name = 'Ashburn';
答案:没有影响。ySQL有一个叫查询优化器的东东,会分析这些搜索条件并且按照可以使用的索引中列的顺序来决定先使用哪个搜索条件,后使用哪个搜索条件。
2. 匹配左边的列
搜索语句中也可以不用包含全部联合索引的列,只包含左边的列就行。
比如:
SELECT * FROM person_info WHERE name = 'Ashburn';
或者包含多个左边的列
SELECT * FROM person_info WHERE name = 'Ashburn' AND birthday = '1990-09-27';
为什么搜索条件中必须出现左边的列才可以使用到这个B+树索引呢?比如下边的语句就用不到这个B+树索引么?
SELECT * FROM person_info WHERE birthday = '1990-09-27';
答案:是的,的确用不到,因为B+树的数据页和记录先是按照name列的值排序的,在name列的值相同的情况下才使用birthday列进行排序,也就是说name列的值不同的记录中birthday的值可能是无序的。而现在你跳过name列直接根据birthday的值去查找,臣妾做不到呀~ 那如果我就想在只使用birthday的值去通过B+树索引进行查找咋办呢?这好办,你再对birthday列建一个B+树索引就行了,创建索引的语法不用我唠叨了吧。
但是需要特别注意的一点是,如果我们想使用联合索引中尽可能多的列,搜索条件中的各个列必须是联合索引中从最左边连续的列。
比方说联合索引idx_name_birthday_phone_number中列的定义顺序是name、birthday、phone_number,如果我们的搜索条件中只有name和phone_number,而没有中间的birthday,比方说这样:
SELECT * FROM person_info WHERE name = 'Ashburn' AND phone_number = '15123983239';
这样只能用