POLARDB for MySQL 8.0重磅推出并行查询框架,当您的查询数据量到达一定阈值,就会自动启动并行查询框架,从而使查询耗时指数级下降。

在存储层将数据分片到不同的线程上,多个线程并行计算,将结果流水线汇总到总线程,最后总线程做些简单归并返回给用户,提高查询效率。

并行查询(Parallel Query)利用多核CPU的并行处理能力,以8核 32G 配置为例,示意图如下所示:


示意图

应用场景

并行查询适用于大部分SELECT语句,例如大表查询、多表连接查询、计算量较大的查询。对于非常短的查询,效果不太显著。

  • 轻分析类业务

    报表查询通常SQL复杂而且比较耗费时间,通过并行查询可以加速单次查询效率。

  • 系统资源相对空闲

    并行查询会使用更多的系统资源,只有当系统的CPU较多、IO负载不高、内存够大的时候,才可以充分使用并行查询来提高资源利用率和查询效率。

性能优势

关于并行查询的性能优势,请参见并行查询使用示例

并行查询的使用方法

  • 通过系统参数来控制并行查询

    POLARDB通过Global全局参数 max_parallel_degree 来控制每一条SQL最多使用多少个线程并行执行,默认值是4,您可以在使用过程中随时修改该参数(参见设置集群参数),无需重启数据库。

    除了Global集群级别,您也可以单独调整某Session内SQL查询的并行度。例如,通过Session级环境变量,加到JDBC的连接串配置中,则可以对某个应用程序单独设置并行度。
    set max_parallel_degree = n 
  • 使用 Hint
    使用Hint开启并行查询(n代表最大的并行度,也就是最大的worker个数):
     SELECT /*+ SET_VAR(max_parallel_degree=n) */  *  FROM ...
    说明 为了发挥最佳性能,并行查询并不是对所有的SQL都生效,POLARDB优化器会根据具体的查询SQL生成合适的执行计划。
  • 强制优化器选择并行执行
    POLARDB优化器可能不选择并行执行查询,如果希望优化器忽略代价,尽可能选择并行计划,可以设置如下参数:
    set force_parallel_mode = on
    说明 这是一个调试参数,不建议在生产环境中使用。由于并行查询使用场景的限制,有些情况下即便设置了该参数,优化器也可能不选择并行。

相关参数和变量

参数名 级别 描述
max_parallel_degree Global、Session

单个查询的最大并行度,即最多使用多少个worker进行并行执行。

  • 取值范围:[0-1024],0表示关闭并行计算。
  • 默认值:4。
说明 POLARDB优化器可能会对主查询和子查询分别并行执行,如果同时并行执行,它们的最大worker数不能超过max_parallel_degree,整个查询使用的worker数将会是主查询和子查询使用的worker数之和。
force_parallel_mode Session

强制POLARDB优化器忽略代价,尽可能的使用并行查询。

  • 取值范围:[ON, OFF]
  • 默认值:OFF
注意 这是个调试参数,打开后,POLARDB优化器会尽可能的选择并行查询,但不能保证一定使用并行查询。
Parallel_workers_created Session、Global 从Session启动开始,生成Parallel Worker的个数。
Gather_records Session、Global Gather记录总数。
PQ_refused_over_memory_soft_limit Session、Global 由于内存限制没有启用并行的查询及子查询数。
PQ_refused_over_total_workers Session、Global 由于总Worker数限制没有启用并行的查询及子查询数。
Total_used_query_memory Global 当前总的已使用的查询内存(Virtual Memory)。
Total_running_parallel_workers Global 当前正在运行的Parallel Worker的数目。

并行执行计划

以下为您介绍EXPLAIN执行计划输出中与并行查询相关的内容。

  • 并行扫描

    在并行扫描中,每个Worker并行独立扫描数据表中的数据。Worker扫描产生的中间结果集将会返回给Leader线程,Leader线程通过Gather操作收集产生的中间结果,并将所有结果汇返回到客户端。

  • 多表并行连接

    并行查询会将多表连接操作完整的下推到Worker上去执行。POLARDB优化器只会选择一个自认为最优的表进行并行扫描,而除了该表外,其他表都是一般扫描。每个Worker会将连接结果集返回给Leader线程,Leader线程通过Gather操作进行汇总,最后将结果返回给客户端。

  • 并行排序

    POLARDB优化器会根据查询情况,将ORDER BY下推到每个Worker里执行,每个Worker将排序后的结果返回给Leader,Leader通过Gather Merge Sort操作进行归并排序,最后将排序结果返回到客户端。

  • 并行分组

    POLARDB优化器会根据查询情况,将GROUP BY下推到Worker上去并行执行。每个Worker负责部分数据的GROUP BY。Worker会将GROUP BY的中间结果返回给Leader,Leader通过Gather操作汇总所有数据。这里POLARDB优化器会根据查询计划情况来自动识别是否需要再次在Leader上进行GROUP BY。例如,如果GROUP BY使用了Loose Index Scan,Leader上将不会进行再次GROUP BY;否则Leader会再次进行GROUP BY操作,然后把最终结果返回到客户端。

  • 并行聚集

    并行查询执行聚集函数下推到Worker上并行执行。并行聚集是通过两次聚集来完成的。第一次,参与并行查询部分的每个Worker执行聚集步骤。第二次,Gather或Gather Merge节点将每个Worker产生的结果汇总到Leader。最后,Leader会将所有Worker的结果再次进行聚集得到最终结果。

并行执行计划示例

以下以使用pq_test表来测试并行查询为例。

表结构如下:

mysql> SHOW CREATE TABLE pq_test\G
*************************** 1. row ***************************
       Table: pq_test
Create Table: CREATE TABLE `pq_test` (
  `id` BIGINT(20) NOT NULL AUTO_INCREMENT,
  `help_topic_id` INT(10) UNSIGNED NOT NULL,
  `name` CHAR(64) NOT NULL,
  `help_category_id` SMALLINT(5) UNSIGNED NOT NULL,
  `description` TEXT NOT NULL,
  `example` TEXT NOT NULL,
  `url` TEXT NOT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=21495809 DEFAULT CHARSET=utf8
1 row in set (0.00 sec)

表大小如下:

mysql> SHOW TABLE STATUS\G
*************************** 1. row ***************************
           Name: pq_test
         Engine: InnoDB
        Version: 10
     Row_format: Dynamic
           Rows: 20064988
 Avg_row_length: 1898
    Data_length: 38085328896
Max_data_length: 0
   Index_length: 0
      Data_free: 4194304
 Auto_increment: 21495809
    Create_time: 2019-07-30 01:35:27
    Update_time: NULL
     Check_time: NULL
      Collation: utf8_general_ci
       Checksum: NULL
 Create_options:
        Comment:
1 row in set (0.02 sec)

查询SQL:

SELECT COUNT(*) FROM pq_test;
  • 不使用并行查询的EXPLAIN输出如下:
    mysql> SET max_parallel_degree=0; EXPLAIN SELECT COUNT(*) FROM pq_test\G
    Query OK, 0 rows affected (0.02 sec)
    *************************** 1. row ***************************
               Id: 1
      Select_type: SIMPLE
            Table: pq_test
      Partitions: NULL
             Type: index
    Possible_keys: NULL
              Key: PRIMARY
          Key_len: 8
              Ref: NULL
             Rows: 20064988
         Filtered: 100.00
            Extra: Using index
    1 row in set, 1 warning (0.03 sec)
  • 使用并行查询的EXPLAIN输出如下:
    mysql> EXPLAIN SELECT COUNT(*) FROM pq_test\G
    *************************** 1. row ***************************
               Id: 1
      Select_type: SIMPLE
            Table: <gather2>
       Partitions: NULL
             Type: ALL
    Possible_keys: NULL
              Key: NULL
          Key_len: NULL
              Ref: NULL
             Rows: 20064988
         Filtered: 100.00
            Extra: NULL
    *************************** 2. row ***************************
               Id: 2
      Select_type: SIMPLE
            Table: pq_test
       Partitions: NULL
             Type: index
    Possible_keys: NULL
              Key: PRIMARY
          Key_len: 8
              Ref: NULL
             Rows: 10032494
         Filtered: 100.00
            Extra: Parallel scan (2 workers); Using index
    2 rows in set, 1 warning (0.00 sec)

从该EXPLAIN可以看到,并行计划中包含Gather操作,该操作负责汇总所有Worker返回的中间结果。另外,从执行计划输出的Extra信息中看到:pq_test表使用了Parallel scan(并行扫描),期望用2个Workers来并行执行。