关于阿里云开发者:深度解析PolarDB的并行查询引擎

1次阅读

共计 5155 个字符,预计需要花费 13 分钟才能阅读完成。

简介:PolarDB 与开源 MySQL 及其它类 MySQL 的产品相比,除了计算与存储拆散的先进架构之外,另外一个最外围的技术冲破就是开发了其它类 MySQL 产品没有的并行查问引擎,通过并行查问引擎,PolarDB 除了放弃本身对 OLTP 利用的劣势之外,还对 OLAP 的反对能力有了一个质的飞越,遥遥领先于其它类 MySQL 产品。

PolarDB 与开源 MySQL 及其它类 MySQL 的产品相比,除了计算与存储拆散的先进架构之外,另外一个最外围的技术冲破就是开发了其它类 MySQL 产品没有的并行查问引擎,通过并行查问引擎,PolarDB 除了放弃本身对 OLTP 利用的劣势之外,还对 OLAP 的反对能力有了一个质的飞越,遥遥领先于其它类 MySQL 产品。

用户越来越多的剖析统计需要

        家喻户晓,MySQL 的优化器目前还不反对并行优化,也不反对并行执行。当然,MySQL 本人也在逐步摸索并行执行的可能,比方对 count(*) 的并行执行,但整体上,还没有造成一套成熟的并行优化、并行执行的机制,只是针对一些非凡的场景进行局部优化。随着类 MySQL 产品在云上的蓬勃发展,越来越多的传统用户迁徙到类 MySQL 产品上,对 MySQL 提出了一些新的挑战。很多传统用户在对 OLTP 要求的同时,还要求数据库有一些剖析、统计、报表的能力,绝对传统商业数据库来说,MySQL 在这方面有显著的劣势。为了满足用户一直晋升的 OLTP 能力,同时又能进行 OLAP 剖析的需要,PolarDB 的并行查问引擎应运而生。自诞生以来,通过弱小的并行执行能力,原来须要几百秒的查问,当初只须要几秒,为用户节俭了大量的工夫和金钱,失去大量用户的极大好评。

PolarDB 并行查问引擎应运而生

        优化器是数据库的外围,优化器的好坏简直能够决定一个数据库产品的成败。开发一个全新的优化器,对任何团队都是一个微小的挑战,技术的复杂度暂且不提,就是想做到产品的足够稳固就是一个十分难以克服的艰难。因而即便传统商业数据库,也是在现有优化器的根底上不断改进,逐步减少对并行的反对,最终成为一个成熟的并行优化器。对 PolarDB 也是如此,在设计和开发并行查问引擎时,咱们充分利用现有优化器的技术积攒和实现根底,不断改进,一直打磨,最终造成了一个继续迭代的技术计划,以保障新的优化器的稳固运行和技术革新。

        对于一个类 OLAP 的查问,不言而喻的是它通常是对大批量数据的查问,数据量大意味着数据远大于数据库的内存容量,大部分数据可能无奈缓存到数据库的 buffer 中,而必须在查问执行时才动静加载到 buffer 中,这样就会造成大量 IO 操作,而 IO 操作又是最耗时的,因而首先要思考的就是如何能减速 IO 操作。因为硬件的限度,每次 IO 的耗时根本是固定的,尽管还有程序 IO 和随机 IO 的区别,但在 SSD 曾经流行的明天,两者的差别也在逐步靠近。那么还有没有其它形式能够减速 IO 呢? 显然并行 IO 是一个简单易行的办法,如果多个线程能够同时发动 IO,每个线程只读取局部数据,这样就能够疾速的将数据读到数据库的 buffer 中。然而如果只是将数据读取到 buffer 中,而不是立刻进行后续解决,那么这些数据就会因 buffer 爆满导致数据被换出,从而失去减速 IO 的意义。

      图 1 - 并行 IO 示意图

        因而,在并行读取数据的同时,必须同时并行的解决这些数据,这是并行查问减速的根底。因为原有的优化器只能生成串行的执行打算,为了实现并行读取数据,同时并行处理数据,首先必须对现有的优化器进行革新,让优化器能够生成咱们须要的并行打算。比方哪些表能够并行读取,并且通过并行读取会带来足够的收益;或者哪些操作能够并行执行,并且能够带来足够的收益。并不是说并行化革新肯定会有收益,比方对一个数据量很小的表,可能只是几行,如果也对它进行并行读取的话,并行执行所须要的多线程构建所须要的代价可能远大于所失去的收益,总体来说,并行读取会须要更多的资源和工夫,这就得失相当了,因而并行化的革新必须是基于代价的,否则可能会导致更重大的性能褪化问题。

Fact 表的并行扫描

        通过基于并行 cost 的计算和比拟,抉择能够并行读取的表作为候选,是并行执行打算的第一步。基于新的并行 cost,兴许会有更优的 JOIN 的程序抉择,但这须要更多的迭代空间,为避免优化过程耗费太多的工夫,放弃原有打算的 JOIN 程序是一个不错的抉择。另外,对于参加 JOIN 的每张表,因为表的拜访办法不同,比方全表扫描、ref 索引扫描,range 索引扫描等,这些都会影响到最终并行扫描的 cost。

        通常咱们抉择最大的那张表作为并行表,这样并行扫描的收益最大,当然也能够抉择多个表同时做并行扫描,前面会持续探讨更简单的状况。

上面以查问年度生产 TOP 10 的用户为例:

SELECT c.c_name, sum(o.o_totalprice) as s FROM customer c, orders o WHERE c.c_custkey = o.o_custkey AND o_orderdate >= '1996-01-01' AND o_orderdate <= '1996-12-31' GROUP BY c.c_name ORDER BY s DESC LIMIT 10;

其中 orders 表为订单表,数据很多,这类表称之为 Fact 事实表,customer 表为客户表,数据绝对较少,这类表称之为 dimension 维度表。那么此 SQL 的并行执行打算如下图所示:

从打算中能够看出 orders 表会做并行扫描,由 32 个 workers 线程来执行,每个 worker 只扫描 orders 表的某些分片,而后与 customer 表按 o\_custkey 做 eq\_ref 进行 JOIN,JOIN 的后果发送到用户 session 中一个 collector 组件,而后由 collector 组件持续做后续的 GROUP BY、ORDER BY 及 LIMIT 操作。

多表并行 JOIN

        将一张表做并行扫描之后,就会想为什么只能抉择一张表?如果 SQL 中有 2 张或更多的 FACT 表,能不能能够将 FACT 表都做并行扫描呢?答案是当然能够。以上面 SQL 为例:

SELECT o.o_custkey, sum(l.l_extendedprice) as s FROM orders o, lineitem l WHERE o.o_custkey = l.l_orderkey GROUP BY o.o_custkey ORDER BY s LIMIT 10;

其中 orders 表和 lineitem 表都是数据量很大的 FACT 表,此 SQL 的并行执行打算如下图所示:

        从打算中能够看到 orders 表和 lineitem 表都会做并行扫描,都由 32 个 workers 线程来执行。那么多个表的并行是如何实现的呢?咱们以 2 个表为例,当 2 个表执行 JOIN 时,通常的 JOIN 形式有 Nested Loop JOIN、HASH JOIN 等,对于不同的 JOIN 形式,为保障后果的正确性,必须抉择正当的表扫描形式。以 HASH JOIN 为例,对于串行执行的 HASH JOIN 来说,首先抉择一个表创立 HASH 表称之谓 Build 表,而后读取另一个 Probe 表,计算 HASH,并在 Build 表中进行 HASH 匹配,若匹配胜利,输入后果,否则持续读取。如果改为并行 HASH JOIN,并行优化器会对串行执行的 HASH JOIN 进行并行化革新,使之成为并行 HASH JOIN,并行化革新的计划能够有以下两种解决方案。计划一是将 2 个表都按 HASH key 进行分区,雷同 HASH 值的数据处于同一个分区内,由同一个线程执行 HASH JOIN。计划二是创立一个共享的 Build 表,由所有执行 HASH JOIN 的线程共享,而后每个线程并行读取属于本人线程的另外一个表的分片,再执行 HASH JOIN。

图 2 - 并行 HASH JOIN 示意图

  • 对于计划一,须要读取表中的所有数据,依据选中的 HASH key,对数据进行分区,并将数据发送到不同的解决线程中,这须要额定减少一个 Repartition 算子,负责依据分区规定将数据发送到不同的解决线程。为了提高效率,这里通常会采纳 message queue 队列来实现。
  • 对于计划二,须要并行创立共享的 HASH build 表,当 build 表创立胜利后,每个线程读取 Probe 表的一个分片,别离执行 HASH JOIN,这里的分片并不需要依照 HASH key 进行分片,每个线程别离读取互不相交的分片即可。

剖析统计算子的并行

        对于一个剖析统计的需要,GROUP BY 操作是绕不开的操作,尤其对大量的 JOIN 后果再做 GROUP BY 操作,是整个 SQL 中最费时的一个过程,因而 GROUP BY 的并行也是并行查问引擎必须优先解决的问题。

以年度生产 TOP10 客户的 SQL 为例,对 GROUP BY 并行化后的并行执行打算如下图所示:

        与之前的执行打算相比,新的执行打算中多了一个 collector 组件,总共有 2 个 collector 组件。首先咱们看第二行的 collector 组件,它的 extra 信息中有 2 条 ”Using temporary; Using filesort”,这示意它是对从 workers 接管到的数据执行 GROUP BY,而后再按 ORDER 排序,因为只有第一个 collector 组件在用户的 session 中,所以这个 collector 也是在 worker 中并行执行,也就是说并行的做 Group by 和 Order by 以及 Limit;而后看第一行的 collector 组件,它的 extra 信息中只有一条 ”Merge sort”,示意 session 线程对从 workers 接管到的数据执行一次 merge sort,而后将后果返回给用户。这里可能就有人会提出疑难,为什么 session 线程只做 merge sort 就能够实现 GROUP BY 操作呢?另外 LIMIT 在哪里呢?

        首先答复第 2 个问题,因为 explain 打算显示的问题,在惯例模式下不显示 LIMIT 操作,但在 Tree 模式下会显示 LIMIT 操作。如下所示:

        从 Tree 型打算树上能够分明的看到 LIMIT 操作有 2 处,一处在打算的顶端,也就是在 session 上,做完 limit 后将数据返回给用户;另外一处在打算树的两头地位,它其实是在 worker 线程的执行打算上,在每个 worker 线程中在排序实现后也会做一次 limit,这样就能够极大缩小 worker 返回给 session 线程的数据量,从而晋升整体性能。

        上面来答复第一个问题,为什么 GROUP BY 只须要在 worker 线程上执行一次就能够保障后果的正确性。通常来说,每个 worker 只有所有数据的一个分片,只在一个数据分片上做 GROUP BY 是有极大的危险失去谬误的 GROUP BY 后果的,因为同一 GROUP 分组的数据可能不只是在本 WORKER 的数据分片上,也可能在其它 WORKER 的数据分片中,被其它 WORKER 所持有。然而如果咱们能够保障同一 GROUP 分组的数据肯定位于同一个数据分片,并且这个数据分片只被一个 WORKER 线程所持有,那么就能够保障 GROUP BY 后果的正确性。通过 Tree 型执行打算能够看到,在并行 JOIN 之后,将 JOIN 的后果按 GROUP 分组的 KEY 值: c.c\_name 进行 Repartition 操作,将雷同分组的数据散发到雷同的 WORKER,从而保障每个 WORKER 领有的数据分片互不穿插,保障 GROUP BY 后果的正确性。

        因为每个 WORKER 的 GROUP BY 操作曾经是最终后果,所以还能够将 ORDER BY 和 LIMIT 也下推到 WORKER 来执行,进一步晋升了并行执行的效率。

并行查问引擎对 TPCH 的线性减速

        附图是一个并行查问引擎对 TPCH 的减速成果,TPC- H 中 100% 的 SQL 能够被减速,70% 的 SQL 减速比超过 8 倍,总和减速近 13 倍,Q6 和 Q12 减速甚至超过 32 倍。

一直迭代翻新的并行查问引擎

        总之,通过对并行查问引擎的反对,PolarDB 不仅在放弃查问引擎稳固的同时,还极大的晋升了简单 SQL,尤其是剖析统计类型查问的性能。通过有打算的一直迭代,PolarDB 在并行查问引擎的路线上越走越远,也越来越弱小,为了满足客户日益一直增长的性能需求,为了更多企业用户的数字化降级,PolarDB 为您提供革命性的数据引擎,助您减速拥抱万物互联的将来。

版权申明: 本文内容由阿里云实名注册用户自发奉献,版权归原作者所有,阿里云开发者社区不领有其著作权,亦不承当相应法律责任。具体规定请查看《阿里云开发者社区用户服务协定》和《阿里云开发者社区知识产权爱护指引》。如果您发现本社区中有涉嫌剽窃的内容,填写侵权投诉表单进行举报,一经查实,本社区将立即删除涉嫌侵权内容。

正文完
 0