絮叨
我们继续来探索mysql。前面我们了解了mysql的索引的一些基础知识,今天我们来康康B+树索引
- Mysql从入门到入神之(一)Schema 数据类型优化 和索引基础
- Mysql从入门到入神之(二)Select 和Update的执行过程
- Mysql从入门到入神之(三)InnoDB的存储结构
- Mysql从入门到入神之(四)B+树索引
前面把页 ,页中的槽。槽中的行,基本上一一聊清楚了,那么我们想呀,我们的页是放到哪里的呢?它又有怎么的结构呢?这个就是我们今天要来讨论的东西了。
表空间
表空间是一个抽象的概念,对于系统表空间来说,对应着文件系统中一个或多个实际文件;对于每个独立表空间来说,对应着文件系统中一个名为表名.ibd的实际文件。大家可以把表空间想象成被切分为许许多多个页的池子,当我们想为某个表插入一条记录的时候,就从池子中捞出一个对应的页来把数据写进去
区(extent)的概念
表空间中的页实在是太多了,为了更好的管理这些页面,设计InnoDB的大叔们提出了区(英文名:extent)的概念。对于16KB的页来说,连续的64个页就是一个区,也就是说一个区默认占用1MB空间大小。不论是系统表空间还是独立表空间,都可以看成是由若干个区组成的,每256个区被划分成一组。画个图表示就是这样:
表空间被划分为许多连续的区,每个区默认由64个页组成,每256个区划分为一组
表空间的东西,太复杂。这边就不先班门弄斧,如有有需要的就大家去看掘金小册 MySQL 是怎样运行的:从根儿上理解 MySQL
访问方法(access method)的概念
什么意思呢?就是好比你要做一件事情,做这个事情的方法有很多种,比如我们去美国,可以坐飞机,做轮船,当然你也可以走过去,哈哈地球毕竟是园的。
回到MySQL中来,我们平时所写的那些查询语句本质上只是一种声明式的语法,只是告诉MySQL我们要获取的数据符合哪些规则,至于MySQL背地里是怎么把查询结果搞出来的那是MySQL自己的事儿。对于单个表的查询来说,设计MySQL的大叔把查询的执行方式大致分为下边两种:
- 使用全表扫描进行查询
- 这种执行方式很好理解,就是把表的每一行记录都扫一遍嘛,把符合搜索条件的记录加入到结果集就完了。不管是啥查询都可以使用这种方式执行,当然,这种也是最笨的执行方式
- 使用索引进行查询
- 因为直接使用全表扫描的方式执行查询要遍历好多记录,所以代价可能太大了。如果查询语句中的搜索条件可以使用到某个索引,那直接使用索引来执行查询可能会加快查询执行的时间。使用索引来执行查询的方式五花八门,又可以细分为许多种类:
- 针对主键或唯一二级索引的等值查询
- 针对普通二级索引的等值查询
- 针对索引列的范围查询
- 直接扫描整个索引
把MySQL执行查询语句的方式称之为访问方法或者访问类型。同一个查询语句可能可以使用多种不同的访问方法来执行,虽然最后的查询结果都是一样的,但是执行的时间可能差老鼻子远了,就像是从钟楼到大雁塔,你可以坐火箭去,也可以坐飞机去,当然也可以坐乌龟去。下边细细道来各种访问方法的具体内容。
const
有的时候我们可以通过主键列来定位一条记录,比方说这个查询:
SELECT * FROM liumaishenjian WHERE id = 1438;
对于liumaishenjian表的聚簇索引来说,展示的就是id列。我们想突出的重点就是:B+树叶子节点中的记录是按照索引列排序的,对于的聚簇索引来说,它对应的B+树叶子节点中的记录就是按照id列排序的。B+树本来就是一个矮矮的大胖子,所以这样根据主键值定位一条记录的速度贼快。类似的,我们根据唯一二级索引列来定位一条记录的速度也是贼快的,比如下边这个查询:
SELECT * FROM liumaishenjian WHERE key2 = 3841;
其实总结来说就是 通过主键索引 和 唯一二级索引的等值查询就是非常快的,把这种通过主键或者唯一二级索引列来定位一条记录的访问方法定义为:const,意思是常数级别的,代价是可以忽略不计的。
ref
有时候我们对某个普通的二级索引列与常数进行等值比较,比如这样:
SELECT * FROM liumaishenjian WHERE key1 = 'abc';
把这种搜索条件为二级索引列与常数等值比较,采用二级索引来执行查询的访问方法称为:ref。我们看一下采用ref访问方法执行查询的图示:
从图示中可以看出,对于普通的二级索引来说,通过索引列进行等值比较后可能匹配到多条连续的记录,而不是像主键或者唯一二级索引那样最多只能匹配1条记录,所以这种ref访问方法比const差了那么一丢丢,但是在二级索引等值比较时匹配的记录数较少时的效率还是很高的(如果匹配的二级索引记录太多那么回表的成本就太大了),跟坐高铁差不多。不过需要注意下边两种情况:
- 二级索引列值为NULL的情况
不论是普通的二级索引,还是唯一二级索引,它们的索引列对包含NULL值的数量并不限制,所以我们采用key IS NULL这种形式的搜索条件最多只能使用ref的访问方法,而不是const的访问方法。 - 对于某个包含多个索引列的二级索引来说,只要是最左边的连续索引列是与常数的等值比较就可能采用ref的访问方法
range
我们之前介绍的几种访问方法都是在对索引列与某一个常数进行等值比较的时候才可能使用到(ref_or_null比较奇特,还计算了值为NULL的情况),但是有时候我们面对的搜索条件更复杂,比如下边这个查询:
SELECT * FROM liumaishenjian WHERE key2 IN (1438, 6328) OR (key2 >= 38 AND key2 <= 79); 复制代码
我们当然还可以使用全表扫描的方式来执行这个查询,不过也可以使用二级索引 + 回表的方式执行,如果采用二级索引 + 回表的方式来执行的话,那么此时的搜索条件就不只是要求索引列与常数的等值匹配了,而是索引列需要匹配某个或某些范围的值,在本查询中key2列的值只要匹配下列3个范围中的任何一个就算是匹配成功了:
- key2的值是1438
- key2的值是6328
- key2的值在38和79之间。
这种访问方式 我们称之为range
如果把这几个所谓的key2列的值需要满足的范围在数轴上体现出来的话,那应该是这个样子:
index
SELECT key_part1, key_part2, key_part3 FROM liumaishenjian WHERE key_part2 = 'abc'; 复制代码
前提条件 key_part1, key_part2, key_part3 是一个组合索引
由于key_part2并不是联合索引idx_key_part最左索引列,所以我们无法使用ref或者range访问方法来执行这个语句。但是呢他满足以下2个、条件
- 它的查询列表只有3个列:key_part1, key_part2, key_part3,而索引idx_key_part又包含这三个列。
- 搜索条件中只有key_part2列。这个列也包含在索引idx_key_part中。
也就是说我们可以直接通过遍历idx_key_part索引的叶子节点的记录来比较key_part2 = 'abc'这个条件是否成立,把匹配成功的二级索引记录的key_part1, key_part2, key_part3列的值直接加到结果集中就行了。由于二级索引记录比聚簇索记录小的多(聚簇索引记录要存储所有用户定义的列以及所谓的隐藏列,而二级索引记录只需要存放索引列和主键),而且这个过程也不用进行回表操作,所以直接遍历二级索引比直接遍历聚簇索引的成本要小很多,设计MySQL的大叔就把这种采用遍历二级索引记录的执行方式称之为:index。
all
最直接的查询执行方式就是我们已经提了无数遍的全表扫描,对于InnoDB表来说也就是直接扫描聚簇索引,设计MySQL的大叔把这种使用全表扫描执行查询的方式称之为:
all。
结尾
我们下章继续再战。