共计 1973 个字符,预计需要花费 5 分钟才能阅读完成。
最近的 MySQL8.0.14 版本增加了其第一个并行查询特性,可以支持在聚集索引上做 SELECT COUNT() 和 check table 操作。本文简单的介绍下这个特性。
用法
增加了一个 session 级别参数: innodb_parallel_read_threads
要执行并行查询,需要满足如下条件 (ref: row_scan_index_for_mysql)
无锁查询
聚集索引
不是 Insert…select
需要参数设置为 >1
相关代码
入口函数:
row_scan_index_for_mysql
parallel_select_count_star // for select count(*)
parallel_check_table // for check table
InnoDB 里实现了两种查询方式,一种是基于 key 的 (key reader),根据叶子节点上的值做分区,需要判断可见性;另外一种是基于 page 的 (physical read),根据 page no 来做分区,无需判断可见性。目前支持的两种查询都是 key reader 的方式。
使用如下代码创建一个 reader,并调用接口函数,read() 函数里的回调函数包含了如何对获取到的行数据进行处理:
Key_reader reader(prebuilt->table, trx, index, prebuilt, n_threads);
reader.read(func), 其中 func 是回调函数,用于告诉线程怎么处理得到的每一行
分区并计算线程数
分区入口:
template <typename T, typename R>
typename Reader<T, R>::Ranges Reader<T, R>::partition()
流程:
搜集 btree 的最左节点 page no
从 root page 开始向下,尝试构建子树:
如果该 level 的 page 个数不足线程数,继续往下走
否则,使用该 level, 搜集该 level 的每个 page 的最左记录向下直到叶子节点的最左链表
如上搜集到的是多条代表自上而下的 page no 数组,需要根据这些数组创建分区 range,这里有两种创建方式:
Key_reader::Ranges Key_reader::create_ranges: 基于键值创建分区
找到每个链表的叶子节点的第一条记录,存储其 cursor 作为当前 range 的起点和上一个 range 的终点
Phy_reader::Ranges Phy_reader::create_ranges:基于物理页创建分区
找到每个链表的叶子节点,相邻链表的叶子节点组成一个 range
线程数取分区数和配置线程数的最小值
启动线程
启动线程各自扫描: start_parallel_load
为每个分区创建 context(class Reader::Ctx),加入到队列中实现了一个 Lock-free 的队列模型,多线程可以并发的从队列中取 context: 实现细节在文件 include/ut0mpmcbq.h 中,对应类 class mpmc_bq, 实现思路见链接
线程函数:
dberr_t Reader<T, R>::worker(size_t id, Queue &ctxq, Function &f)
每取一个分区,调用处理函数去遍历分区:
Key_reader::traverse 对于获得的每条记录,判断其可见性(共享事务对象 trx_t),调用回调函数处理记录(在 Key_reader::read() 作为参数传递), 对于 select count(*), 就是累加记录的计数器
Phy_reader::traverse 读取每条非标记删除的记录并调用回调函数处理,无需判断可见性
对于异常情况,只返回最后一个 context 的错误码。
该特性只是 MySQL 在并行查询的第一步,甚至定义了一些接口还没有使用,例如接口函数 pread_adapter_scan_get_num_threads, 估计是给未来 server 层做并行查询使用的。代码里对应两个适配类:
Parallel_reader_adapter
Parallel_partition_reader_adapter
另外一个可以用到的地方是创建二级索引,我们知道 InnoDB 创建二级索引,是先从聚集索引读取记录,生成多个 merge file,然后再做归并排序,但无论是生成 merge file,还是排序,都可以做到并行化。官方也提到这是未来的一个优化点,相信不久的将来,我们就能看到 MySQL 更为强大的并行查询功能。
Reference
WL#11720: InnoDB: Parallel read of indexMySQL 8.0.14: A Road to Parallel Query Execution is Wide Open!
本文作者:zhaiwx_yinfeng. 阅读原文
本文为云栖社区原创内容,未经允许不得转载。