IT教程 ·

MySQL8.0 InnoDB并行执行

objectarx 多段线自交检查

概述

MySQL经由多年的生长已然成为最盛行的数据库,普遍用于互联网行业,并逐渐向各个传统行业渗入。之所以盛行,一方面是其优异的高并发事务处置惩罚的才能,另一方面也得益于MySQL雄厚的生态。MySQL在处置惩罚OLTP场景下的短查询效果很好,但关于庞杂大查询则才能有限。最直接一点就是,关于一个SQL语句,MySQL最多只能运用一个CPU核来处置惩罚,在这类场景下没法发挥主机CPU多核的才能。MySQL没有裹足不前,一直在生长,新推出的8.0.14版本第一次引入了并行查询特征,使得check table和select count(*)范例的语句机能成倍提拔。虽然现在运用场景还比较有限,但后续的生长值得期待。

运用体式格局

经由历程设置参数innodb_parallel_read_threads来设置并发线程数,就能够入手下手并行扫描功用,默许这个值为4。我这里做一个简朴的试验,经由历程sysbench导入2亿条数据,离别设置innodb_parallel_read_threads为1,2,4,8,16,32,64,测试并行实行的效果。测试语句为select count(*) from sbtest1;

MySQL8.0 InnoDB并行执行 IT教程 第1张

横轴是设置并发线程数,纵轴是语句实行时候。从测试效果来看,全部并行表现照样不错的,扫描2亿条纪录,从单线程的18s,下降到32线程的1s。背面并发开再多,由于数据量有限,多线程的治理斲丧超过了并发带来的机能提拔,不能再继承收缩SQL实行时候。

MySQL并行实行

实际上现在MySQL的并行实行还处于异常低级阶段,以下图所示,左侧是之前MySQL串行处置惩罚单个SQL形状;中心的是现在MySQL版本供应的并行才能,InnoDB引擎并行扫描的形状;最右侧的是未来MySQL要生长的形状,优化器依据体系负载和SQL生成并行设计,并将分区设计下发给实行器并行实行。并行实行不仅仅是并行扫描,还包含并行群集,并行衔接,并行分组,以及并行排序等。现在版本MySQL的上层的优化器以及实行器并没有配套的修正。因而,下文的议论重要集合在InnoDB引擎怎样完成并行扫描,重要包含分区,并行扫描,预读以及与实行器交互的适配器类。

MySQL8.0 InnoDB并行执行 IT教程 第2张

分区

并行扫描的一个中心步骤就是分区,将扫描的数据分别红多份,让多个线程并行扫描。InnoDB引擎是索引构造表,数据以B+tree的情势存储在磁盘上,节点的单元是页面(block/page),同时缓冲池中会对热门页面举行缓存,并经由历程LRU算法举行镌汰。分区的逻辑就是,从根节点页面动身,逐层往下扫描,当推断某一层的分支数超过了设置的线程数,则住手拆分。在完成时,实际上统共会举行两次分区,第一次是按根节点页的分支数分别分区,每一个分支的最左叶子节点的纪录为左下界,并将这个纪录记为相邻上一个分支的右上界。经由历程这类体式格局,将B+tree分别红若干子树,每一个子树就是一个扫描分区。经由第一次分区后,大概涌现分区数不能充足利用多核问题,比方设置了并行扫描线程为3,第一次分区后,产生了4个分区,那末前3个分区并行做完后,第4个分区最多只要一个线程扫描,终究效果就是不能充足利用多核资本。

二次分区

为了处理这个问题,8.0.17版本引入了二次分区,关于第4个分区,继承下探拆分,如许多个子分区又能并发扫描,InnoDB引擎并发扫描的最小粒度是页面级别。详细推断二次分区的逻辑是,一次分区后,若分区数大于线程数,则编号大于线程数的分区,须要继承举行二次分区;若分区数小于线程数且B+tree条理很深,则一切的分区都须要举行二次分区。相干代码以下:

split_point = 0;
if (ranges.size() > max_threads()) {
   //末了一批分区举行二次分区                                      
   split_point = (ranges.size() / max_threads()) * max_threads();          
 } else if (m_depth < SPLIT_THRESHOLD) {                                  
   /* If the tree is not very deep then don't split. For smaller tables    
   it is more expensive to split because we end up traversing more blocks*/
   split_point = max_threads();                                            
 } else {
   //假如B+tree的条理很深(层数大于或即是3,数据量很大),则一切分区都须要举行二次分区
 }

无论是一次分区,照样二次分区,分区边境的逻辑都一样,以每一个分区的最左叶子节点的纪录为左下界,而且将这个纪录记为相邻上一个分支的右上界。如许确保分区充足多,粒度充足细,充足并行。下图展现了设置为3的并发线程,扫描举行二次分区的状况。

相干代码以下:

create_ranges(size_t depth, size_t level)
一次分区:
parallel_check_table
 add_scan
   partition(scan_range, level=0)  /* start at root-page */
     create_ranges(scan_range, depth=0, level=0)
   create_contexts(range, index >= split_point)
二次分区:                                                      
split()
 partition(scan_range, level=1)
   create_ranges(depth=0,level)

并行扫描

在一次分区后,将每一个分区扫描使命放入到一个lock-free行列中,并行的worker线程从行列中猎取使命,实行扫描使命,假如猎取的使命带有split属性,这个时刻worker会将使命举行二次拆分,并投入到行列中。这个历程重要包含两个中心接口,一个是事情线程接口,别的一个是遍历纪录接口,前者从行列中猎取使命并实行,并保护统计计数;后者依据可见性猎取适宜的纪录,并经由历程上层注入的回调函数处置惩罚,比方计数等。

Parallel_reader::worker(size_t thread_id)

{

 1.从ctx-queue提取ctx使命

 2.依据ctx的split属性,肯定是不是须要进一步拆分分区(split())

 3.遍历分区一切纪录(traverse())

 4.一个分区使命完毕后,保护m_n_completed计数

 5.假如m_n_compeleted计数到达ctx数量,叫醒一切worker线程完毕

 6.依据traverse接口,返回err信息。

}

Parallel_reader::Ctx::traverse()

{

 1.依据range设置pcursor

 2.找到btree,将游标定位到range的肇端位置

 3.推断可见性(check_visibility)

 4.假如可见,依据回调函数盘算(比方统计)

 5.向后遍历,若到达了页面的末了一条纪录,启动预读机制(submit_read_ahead)

 6.超出范围后完毕

}

同时在8.0.17版本还引入了预读机制,防止由于IO瓶颈致使并行效果不佳的问题。现在预读的线程数不能设置,在代码中硬编码为2个线程。每次预读的单元是一个簇(InnoDB文件经由历程段,簇,页三级构造治理,一个簇是一组一连的页),依据页面设置的大小,大概为1M或许2M。关于罕见的16k页面设置,每次预读1M,也就是64个页面。worker线程在举行扫描时,会先推断相邻的下一个页面是不是为簇的第一个页面,假如是,则提议预读使命。预读使命一样经由历程lock-free 行列缓存,worker线程是生产者,read-ahead-worker是消费者。由于一切分区页面没有堆叠,因而预读使命也不会反复。

实行器交互(适配器)

实际上,MySQL已封装了一个适配器类Parallel_reader_adapter来供上层运用,为后续的更雄厚的并行实行做准备。起首这个类须要处理纪录花样的问题,将引擎层扫描的纪录转换成MySQL花样,如许做到上基层解耦,实行器不必感知引擎层花样,统一按MySQL花样处置惩罚。全部历程是一个流水线,经由历程一个buffer批量存储MySQL纪录,worker线程不断的将纪录从引擎层上读上来,同时有纪录不断的被上层处置惩罚,经由历程buffer能够均衡读取和处置惩罚速率的差别,确保全部历程活动起来。缓存大小默许是2M,依据表的纪录行长来肯定buffer能够缓存若干个MySQL纪录。中心流程重要在process_rows接口中,流程以下

process_rows

{

 1.将引擎纪录转换成MySQL纪录

 2.猎取本线程的buffer信息(转换了若干mysql纪录,发送了若干给上层)

 3.将MySQL纪录添补进buffer,自增统计m_n_read

 4.调用回调函数处置惩罚(比方统计,聚合,排序等),自增统计m_n_send

}

关于调用者来讲,须要设置表的元信息,以及注入处置惩罚纪录回调函数,比方处置惩罚群集,排序,分组的事情。回调函数经由历程设置m_init_fn,m_load_fn和m_end_fn来掌握。

总结

MySQL8.0引入了并行查询虽然还比较低级,但已让我们看到了MySQL并行查询的潜力,从试验中我们也看到了开启并行实行后,SQL语句实行充足发挥了多核才能,相应时候急剧下降。置信在不久的未来,8.0的会支撑更多并行算子,包含并行群集,并行衔接,并行分组以及并行排序等。

 

SpringProfile轻松切换多环境配置文件

参与评论