前言
本文主要调试一下 mysql 的如下两种查询语句
我们也来深入的看一下, 究竟如下两个普通的查询, mysql 做了什么事情
1. select * from `user` where id = '991';
2. select * from `user`;
3. select * from `user` where name = 'jerry991';
环境介绍
测试表 `user` schema 如下
CREATE TABLE `user` (`id` int(11) NOT NULL AUTO_INCREMENT,`age` int(11) DEFAULT '0',`name` varchar(64) DEFAULT NULL,PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8;
`user` 表中的测试数据如下
select * from `user` where id = '991'
我们这里会着重介绍这个 case, 这里面会详细的看一下 index page, data page 的情况, 以及其具体的执行的流程
另外这个过程中我们会忽略掉一些细节, 尽量只查看这个业务查询的核心业务流程
获取 index page
index -> page 为 3, 表示第三个 page 为索引页面, pageNo 初始化为 index->page, 该 page 的地址为 block->frame : 0x1217f4000
我们 inspect 一下 0x1217f4000
可以看出 offset 为 3, 对应于上面的 pageNo
pagePrev, pageNext 为 0xffffffff, 表示只有一个索引页面
pageNDirSlots 表示当前 page 存在两个 pageDirectory
pageHeapTop 表示当前页面的 FreeSpace 位于 0xa2
pageNHeap 表示有 5 个元素, 三个索引元素 + infimum + supremum
pageNRecords 表示有三个索引元素
pageLevel 为 1 表示下面一级才是叶子节点[存储数据的节点]
再看下面的索引信息 [1, 302) -> page4, [302, 719) -> page5, [719, supremum] -> page6
(lldb) x 0x1217f4000 -c 0x100
0x1217f4000: c2 b7 03 ad 00 00 00 03 ff ff ff ff ff ff ff ff ·.�....��������
0x1217f4010: 00 00 00 00 00 1d 34 e5 45 bf 00 00 00 00 00 00 ......4�E�......
0x1217f4020: 00 00 00 00 00 06 00 02 00 a2 80 05 00 00 00 00 .........�......
0x1217f4030: 00 9a 00 02 00 02 00 03 00 00 00 00 00 00 00 00 ................
0x1217f4040: 00 01 00 00 00 00 00 00 00 16 00 00 00 06 00 00 ................
0x1217f4050: 00 02 00 f2 00 00 00 06 00 00 00 02 00 32 01 00 ...�.........2..
0x1217f4060: 02 00 1b 69 6e 66 69 6d 75 6d 00 04 00 0b 00 00 ...infimum......
0x1217f4070: 73 75 70 72 65 6d 75 6d 00 10 00 11 00 0e 80 00 supremum........
0x1217f4080: 00 01 00 00 00 04 00 00 00 19 00 0e 80 00 01 2e ................
0x1217f4090: 00 00 00 05 00 00 00 21 ff d6 80 00 02 cf 00 00 .......!��...�..
0x1217f40a0: 00 06 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
0x1217f40b0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
0x1217f40c0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
0x1217f40d0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
0x1217f40e0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
0x1217f40f0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................# FileHeader
FIL_PAGE_SPACE = c2 b7 03 ad
FIL_PAGE_OFFSET = 00 00 00 03
FIL_PAGE_PREV = ff ff ff ff
FIL_PAGE_NEXT = ff ff ff ff
FIL_PAGE_LSN = 00 00 00 00 00 1d 34 e5
FIL_PAGE_TYPE = 45 bf
FIL_PAGE_FILE_FLUSH_LSN = 00 00 00 00 00 00 00 00
FIL_PAGE_ARCH_LOG_NO = 00 00 00 06 # PageHeader
PAGE_N_DIR_SLOTS = 00 02
PAGE_HEAP_TOP = 00 a2
PAGE_N_HEAP = 80 05
PAGE_FREE = 00 00
PAGE_GARBAGE = 00 00
PAGE_LAST_INSERT = 00 9a
PAGE_DIRECTION = 00 02
PAGE_N_DIRECTION = 00 02
PAGE_N_RECS = 00 03
PAGE_MAX_TRX_ID = 00 00 00 00 00 00 00 00
PAGE_LEVEL = 00 01
PAGE_INDEX_ID = 00 00 00 00 00 00 00 16
PAGE_BTR_SEG_LEAF = 00 00 00 06 00 00 00 02 00 f2
PAGE_BTR_SEG_TOP = 00 00 00 06 00 00 00 02 00 32 # page4
0x 00 = nulls
0x 1 = delete flag
0x 0 = number of records owned by the record
0b 0000 0000 0001 0 = 2 = order number of this record
0b 001 = node pointer (inside B-tree)
0x 00 0e = next record offset -> page05
minId = 0x 80 00 00 01 = 1
pageNo = 0x 00 00 00 04# page5
0x 00 = nulls
0x 0 = delete flag
0x 0 = number of records owned by the record
0b 0000 0000 0001 1 = 3 = order number of this record
0b 001 = node pointer (inside B-tree)
0x 00 0e = next record offset -> page06
minId = 0x 80 00 01 2e = 302
pageNo = 0x 00 00 00 05# page6
0x 00 = nulls
0x 0 = delete flag
0x 0 = number of records owned by the record
0b 0000 0000 0010 0 = 4 = order number of this record
0b 001 = node pointer (inside B-tree)
0x ff d6 = next record offset -> supremum
minId = 0x 80 00 02 cf = 719
pageNo = 0x 00 00 00 06
根据 index page 确定数据所在 page
通过上面 index page 里面的索引信息, 以及我们这里查询的 991 限定来确定数据属于哪一个 page
这里的查询会有两层搜索, 通过 pageDirectory 的搜索, 通过最近的 pageDirectory 线性查询目标索引, 这里 991 属于 page6
看这里的 low_rec, 指向的记录就是上面的 [719, supremum] -> page6
获取数据 page
pageNo 为 6, 获取 page6, 该 page 的地址为 block->frame : 0x121804000
我们来 inspect 一下 0x121804000
(lldb) x 0x121804000 -c 0x100
0x121804000: e5 86 89 21 00 00 00 06 00 00 00 05 ff ff ff ff �..!........����
0x121804010: 00 00 00 00 00 1d a6 f8 45 bf 00 00 00 00 00 00 ......��E�......
0x121804020: 00 00 00 00 00 06 00 47 27 fc 81 1b 00 00 00 00 .......G'�......
0x121804030: 27 df 00 02 01 18 01 19 00 00 00 00 00 00 00 00 '�..............
0x121804040: 00 00 00 00 00 00 00 00 00 16 00 00 00 00 00 00 ................
0x121804050: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 01 00 ................
0x121804060: 02 00 1c 69 6e 66 69 6d 75 6d 00 06 00 0b 00 00 ...infimum......
0x121804070: 73 75 70 72 65 6d 75 6d 08 00 00 00 10 00 24 80 supremum......$.
0x121804080: 00 02 cf 00 00 00 00 41 75 f1 00 00 01 e7 01 10 ..�....Au�...�..
0x121804090: 80 00 00 16 6a 65 72 72 79 37 31 39 08 00 00 00 ....jerry719....
0x1218040a0: 18 00 24 80 00 02 d0 00 00 00 00 41 76 f2 00 00 ..$...�....Av�..
0x1218040b0: 01 e8 01 10 80 00 00 16 6a 65 72 72 79 37 32 30 .�......jerry720
0x1218040c0: 08 00 00 00 20 00 24 80 00 02 d1 00 00 00 00 41 .... .$...�....A
0x1218040d0: 77 f3 00 00 01 e9 01 10 80 00 00 16 6a 65 72 72 w�...�......jerr
0x1218040e0: 79 37 32 31 08 00 04 00 28 00 24 80 00 02 d2 00 y721....(.$...�.
0x1218040f0: 00 00 00 41 78 f4 00 00 01 ea 01 10 80 00 00 16 ...Ax�...�......# FileHeader
FIL_PAGE_SPACE = e5 86 89 21
FIL_PAGE_OFFSET = 00 00 00 06
FIL_PAGE_PREV = 00 00 00 05
FIL_PAGE_NEXT = ff ff ff ff
FIL_PAGE_LSN = 00 00 00 00 00 1d a6 f8
FIL_PAGE_TYPE = 45 bf
FIL_PAGE_FILE_FLUSH_LSN = 00 00 00 00 00 00 00 00
FIL_PAGE_ARCH_LOG_NO = 00 00 00 06 # PageHeader
PAGE_N_DIR_SLOTS = 00 47
PAGE_HEAP_TOP = 27 fc
PAGE_N_HEAP = 81 1b
PAGE_FREE = 00 00
PAGE_GARBAGE = 00 00
PAGE_LAST_INSERT = 27 df
PAGE_DIRECTION = 00 02
PAGE_N_DIRECTION = 01 18
PAGE_N_RECS = 01 19
PAGE_MAX_TRX_ID = 00 00 00 00 00 00 00 00
PAGE_LEVEL = 00 00
PAGE_INDEX_ID = 00 00 00 00 00 00 00 16
PAGE_BTR_SEG_LEAF = 00 00 00 00 00 00 00 00 00 00
PAGE_BTR_SEG_TOP = 00 00 00 00 00 00 00 00 00 00 # UserRecords
jerry719 -> jerry720 -> jerry721 ... -> jerry999
我们来 inspect 一下 0x121807f00
从上面元数据可以知道, pageDirectory 总共有 71 个, 合计 142 字节, 加上末尾的 8 字节 FileTailer 合计 150 字节
0x121807fff - 0x121807f6a + 1 = 0x96 = 150 字节
可以看到这里的 pageDirectory 也是逆序排列的 infimum -> 0xeb -> 0x017b -> ... -> supremum [根据 id 排序]
(lldb) x 0x121807f00 -c 0x100
0x121807f00: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
0x121807f10: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
0x121807f20: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
0x121807f30: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
0x121807f40: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
0x121807f50: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
0x121807f60: 00 00 00 00 00 00 00 00 00 00 00 70 27 2b 26 9b ...........p'+&.
0x121807f70: 26 0b 25 7b 24 eb 24 5b 23 cb 23 3b 22 ab 22 1b &.%{$�$[#�#;"�".
0x121807f80: 21 8b 20 fb 20 6b 1f db 1f 4b 1e bb 1e 2b 1d 9b !. � k.�.K.�.+..
0x121807f90: 1d 0b 1c 7b 1b eb 1b 5b 1a cb 1a 3b 19 ab 19 1b ...{.�.[.�.;.�..
0x121807fa0: 18 8b 17 fb 17 6b 16 db 16 4b 15 bb 15 2b 14 9b ...�.k.�.K.�.+..
0x121807fb0: 14 0b 13 7b 12 eb 12 5b 11 cb 11 3b 10 ab 10 1b ...{.�.[.�.;.�..
0x121807fc0: 0f 8b 0e fb 0e 6b 0d db 0d 4b 0c bb 0c 2b 0b 9b ...�.k.�.K.�.+..
0x121807fd0: 0b 0b 0a 7b 09 eb 09 5b 08 cb 08 3b 07 ab 07 1b ...{.�.[.�.;.�..
0x121807fe0: 06 8b 05 fb 05 6b 04 db 04 4b 03 bb 03 2b 02 9b ...�.k.�.K.�.+..
0x121807ff0: 02 0b 01 7b 00 eb 00 63 14 23 33 ed 00 1d a6 f8 ...{.�.c.#3�..��
根据数据page 定位 id = 991
这里的查询会有两层搜索, 通过 pageDirectory 的搜索, 通过最近的 pageDirectory 线性查询目标索引, 这里 991 对应于 up_rec
看这里的 up_rec, 指向的记录就是上面的 (991, 22, jerry991)
我们来 inspect 一下 up_rec, 正是我们这里期望查询的 id = '991' 对应的记录
(lldb) x 0x1218066bf
0x1218066bf: 80 00 03 df 00 00 00 00 42 85 81 00 00 01 32 01 ...�....B.....2.
0x1218066cf: 10 80 00 00 16 6a 65 72 72 79 39 39 31 08 00 00 .....jerry991...
外部的业务函数 row_search_for_mysql
上面这里查询到了 id = '991' 对应的记录, 封装在了 pcur->btr_cur->page_cur->rec 里面
上面的流程是在 row_search_for_mysql 的 btr_pcur_open_with_no_init 这里, 这里的作用就是初始化 pcur, 为后面的业务迭代做准备
接下来走的便是根据 pcur->btr_cur->page_cur->rec 进行迭代, 比较是否符合查询条件, 输出记录信息到客户端等等流程
所以这里会做的事情是, 当前 rec 是在 id = '991', 然后 比较是否符合查询条件, 这里是符合, 然后将记录输出到客户端
然后会进入 prebuilt->n_fetch_cached < MYSQL_FETCH_CACHE_SIZE 的条件, 继续下一个元素的迭代
迭代到 rec 是在 id = '992', 然后 比较是否符合查询条件, 这里是不匹配, 走的时候 normal_return, 退出查询
我们来 inspect 一下 0x1218066bf
(lldb) x 0x1218066bf
0x1218066bf: 80 00 03 df 00 00 00 00 42 85 81 00 00 01 32 01 ...�....B.....2.
0x1218066cf: 10 80 00 00 16 6a 65 72 72 79 39 39 31 08 00 00 .....jerry991...
迭代元素 id = '992'
我们来 inspect 一下 0x1218066e3
(lldb) x 0x1218066e3
0x1218066e3: 80 00 03 e0 00 00 00 00 42 86 82 00 00 01 35 01 ...�....B.....5.
0x1218066f3: 10 80 00 00 16 6a 65 72 72 79 39 39 32 08 00 00 .....jerry992...
explain 的结果
我们来看一下 explain 的结果, 会使用 primary key 来索引, 大约会扫描 1 行记录
select * from `user`
初始化 pcur->btr_cur->page_cur->rec
这个查询相对来说 走的流程比较简单, 查询的最小的 数据page的 infimum, 然后不断往后迭代, 直到所有元素迭代完成
这里是初始化 pcur->btr_cur->page_cur->rec 为最小的 数据page的 infimum
这里的迭代是从 index page 开始的, 从 level 最高的 index page 开始向下迭代, 如果是 fromLeft 表示最终取最小的数据页的 infimum, 如果是 !fromLeft 表示最终取最大的数据页的 supremum
根据 pcur->btr_cur->page_cur->rec 进行迭代
然后接下来就是不断地向后迭代, 输出查询的元素信息到客户端
当前 page 到达了末尾, 切换到下一个 page
btr_pcur_is_after_last_in_tree 判断当前 page 是否是最后一个 page, 判断的方式是 pageNext 是否为 0xffffffff
explain 的结果
我们来看一下 explain 的结果, 全表扫描, 大约会扫描 906 行记录
select * from `user` where name = 'jerry991'
初始化 pcur->btr_cur->page_cur->rec
这里的初始化过程 和上面的 "select * from `user`" 的初始化是一样的, 初始化 pcur->btr_cur->page_cur->rec 为 page4->infimum
然后不断向后迭代数据, 直到全表的数据迭代完成
我们来 inspect 一下 这一页的数据
(lldb) x 0x11cf1c000 -c 0x100
0x11cf1c000: 94 8c b5 2e 00 00 00 04 ff ff ff ff 00 00 00 05 ..�.....����....
0x11cf1c010: 00 00 00 00 00 1d 34 e5 45 bf 00 00 00 00 00 00 ......4�E�......
0x11cf1c020: 00 00 00 00 00 06 00 36 3b 0d 81 a3 1d b0 1d 64 .......6;..�.�.d
0x11cf1c030: 00 00 00 05 00 00 00 d0 00 00 00 00 00 00 00 00 .......�........
0x11cf1c040: 00 00 00 00 00 00 00 00 00 16 00 00 00 00 00 00 ................
0x11cf1c050: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 01 00 ................
0x11cf1c060: 02 00 1c 69 6e 66 69 6d 75 6d 00 01 00 0b 00 00 ...infimum......
0x11cf1c070: 73 75 70 72 65 6d 75 6d 05 00 00 00 10 00 21 80 supremum......!.
0x11cf1c080: 00 00 01 00 00 00 00 2b 07 04 00 00 01 56 04 7b .......+.....V.{
0x11cf1c090: 80 00 00 1c 6a 65 72 72 79 04 00 00 00 18 00 20 ....jerry......
0x11cf1c0a0: 80 00 00 02 00 00 00 00 35 04 83 00 00 01 36 01 ........5.....6.
0x11cf1c0b0: 10 80 00 00 16 6c 75 63 79 06 00 00 00 20 00 22 .....lucy.... ."
0x11cf1c0c0: 80 00 00 05 00 00 00 00 3d 14 8a 00 00 01 91 01 ........=.......
0x11cf1c0d0: 10 80 00 00 16 6a 65 72 72 79 35 06 00 04 00 28 .....jerry5....(
0x11cf1c0e0: 00 22 80 00 00 07 00 00 00 00 3d 0b 84 00 00 01 ."........=.....
0x11cf1c0f0: 37 01 10 80 00 00 16 6a 65 72 72 79 37 06 00 00 7......jerry7...
根据 pcur->btr_cur->page_cur->rec 进行迭代
然后接下来就是不断地向后迭代, 输出查询的元素信息到客户端
todo, 这里根据条件过滤, 以及数据传递, 还需要再找找
完