MySQL8.0 InnoDB并行执行[亲测有效]

MySQL8.0 InnoDB并行执行[亲测有效]概述 MySQL经过多年的发展已然成为最流行的数据库,广泛用于互联网行业,并逐步向各个传统行业渗透。之所以流行,一方面是其优秀的高并发事务处理的能力,另一方面也得益于MySQL丰富的生态。MySQL在

MySQL8.0 InnoDB并行执行

概述

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并行执行[亲测有效]

横轴是配置并发线程数,纵轴是语句执行时间。从测试结果来看,整个并行表现还是不错的,扫描2亿条记录,从单线程的18s,下降到32线程的1s。后面并发开再多,由于数据量有限,多线程的管理消耗超过了并发带来的性能提升,不能再继续缩短SQL执行时间。

MySQL并行执行

实际上目前MySQL的并行执行还处于非常初级阶段,如下图所示,左边是之前MySQL串行处理单个SQL形态;中间的是目前MySQL版本提供的并行能力,InnoDB引擎并行扫描的形态;最右边的是未来MySQL要发展的形态,优化器根据系统负载和SQL生成并行计划,并将分区计划下发给执行器并行执行。并行执行不仅仅是并行扫描,还包括并行聚集,并行连接,并行分组,以及并行排序等。目前版本MySQL的上层的优化器以及执行器并没有配套的修改。因此,下文的讨论主要集中在InnoDB引擎如何实现并行扫描,主要包括分区,并行扫描,预读以及与执行器交互的适配器类。

MySQL8.0 InnoDB并行执行[亲测有效]

分区

并行扫描的一个核心步骤就是分区,将扫描的数据划分成多份,让多个线程并行扫描。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,数据量很大),则所有分区都需要进行二次分区
 }

代码100分

无论是一次分区,还是二次分区,分区边界的逻辑都一样,以每个分区的最左叶子节点的记录为左下界,并且将这个记录记为相邻上一个分支的右上界。这样确保分区足够多,粒度足够细,充分并行。下图展示了配置为3的并发线程,扫描进行二次分区的情况。

MySQL8.0 InnoDB并行执行[亲测有效]

相关代码如下:

代码100分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的会支持更多并行算子,包括并行聚集,并行连接,并行分组以及并行排序等。

参考文档

https://dev.mysql.com/worklog/task/?id=11720

https://dev.mysql.com/worklog/task/?id=12978

https://yq.aliyun.com/articles/691516?utm_content=g_1000045831

http://mysql.taobao.org/monthly/2019/10/02/

https://www.percona.com/blog/2019/01/17/using-parallel-query-with-amazon-aurora-for-mysql/

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。
转载请注明出处: https://daima100.com/9867.html

(0)
上一篇 2023-01-30
下一篇 2023-01-30

相关推荐

  • PHP使用Redis的事务命令「建议收藏」

    PHP使用Redis的事务命令「建议收藏」1.redis事务命令: 命令作用返回值 watch 监视一个或多个key 总是OK multi 声明事务开始,后续命令将排队按顺序等待exec执行 总是OK exec 顺序执行multi之后的命令,

    2023-02-23
    158
  • Python菜单按钮:提高网站用户交互

    Python菜单按钮:提高网站用户交互随着互联网的普及和发展,网站已经成为人们生活中不可或缺的一部分。而在众多网站中,如何提高网站的用户交互性成为了许多企业和个人关注的重点。良好的用户交互性能够吸引更多的用户,提高网站的访问量和留存率,进而提升企业的形象和品牌价值。而Python菜单按钮是提高网站用户交互的一种重要方式。

    2024-02-25
    93
  • 从 Exadata 到 TiDB,中通快递 HTAP 实践[通俗易懂]

    从 Exadata 到 TiDB,中通快递 HTAP 实践[通俗易懂]作者介绍:朱志友,中通快递大数据架构师。 中通快递背景介绍 中通快递业务的规模目前是世界第一,是第一个达成年百亿业务量的快递企业,在 2019 年的双十一更是完成了订单量超过 2 亿的佳绩。中通科技…

    2023-03-28
    145
  • 【Doris Weekly】2021.03.22 ~ 2021.04.11

    【Doris Weekly】2021.03.22 ~ 2021.04.11观众朋友们: 下午好! 今天是2021年04月13日星期二,农历三月初二。 欢迎收看【Doris近日要闻】~本次为您带来的是2021年03月22日~2021年04月11日的三周总结。 ▼ 01 统计…

    2023-04-10
    131
  • Redis入门(4) – 排序[亲测有效]

    Redis入门(4) – 排序[亲测有效]SORT命令 LIMIT参数 BY参数 GET参数 STORE参数 排序性能优化 很多场合需要对元素进行排序,这时除了使用有序集合外,还可以借助Redis提供的SORT命令来排序。 SORT命令 SO

    2023-03-06
    142
  • mysql学习笔记第二节 日志系统 redolog与binlog「建议收藏」

    mysql学习笔记第二节 日志系统 redolog与binlog「建议收藏」第二节 日志系统 一条update语句的更新过程 首先让与这个表有关的所有缓存全都失效 其余与查询流程基本相似,但是还涉及到两个重要的日志模块-redo log和binlog redo log(重做日

    2023-04-17
    137
  • Redis入门(1) – 使用Cli[亲测有效]

    Redis入门(1) – 使用Cli[亲测有效][在Docker中使用Redis] [Redis Cli] [命令的返回值类型] [Redis中的多数据库] [基础命令] Redis是REmote DIctionary Server(远程字典服务器

    2023-02-28
    148
  • Oracle中关于视图-序列-同义词-索引的详解

    Oracle中关于视图-序列-同义词-索引的详解一.视图 1.什么是视图 简单来说 视图就是一个虚拟表,实体表的映射 什么时候使用视图 在开发中,有一些表结构是不希望过多的人去接触,就把实体表映射为一个视图。(表结构简化) 在项目过程中,程序人员…

    2023-03-07
    124

发表回复

您的电子邮箱地址不会被公开。 必填项已用*标注