Postgres并行查询

图片引自:https://www.percona.com/blog/2019/07/30/parallelism-in-postgresql/

Notes on PostgreSQL Parallel Query.
__________________________________________________________________________________
standard_ExecutorStart
      |
      |
     \|/
InitPlan—>
     |
    \|/
ExecInitNode
    |
    |
   \|/
ExecGather—》 ExecInitParallelPlan—》 ExecSerializePlan
            |           |
            |           |
            |           |InitializeParallelDSM DSM, dynamic shared memory, dynamic shared data.
            |                在开始执行的时候, 首先使用shm_toc_estimate_chunk,shm_toc_estimate_keys来
            |                进行DSA(dsa是属于什么?)空间进行估算。 其中会保存,fixed-size的状态信息。
            |                query string,serailized plannedstmt, serialized paramlistInfo,每个worker
            |                中的BufferUsage信息。Walusage, tuple queue由workers对其中进行写入的信息。
            |                有写入就有需要读取的,reader信息。 (segment table of contents)。
            |
            |  在初始化完相应的状态信息后,可以进行launch workers—LaunchParallelWorkers的动作。 即:开启相应的workers.
            |
            |
            |   BecomeLockGroupLeader 首先要构建一个成为leader的worker,如果还没有一个leader出现的话。
            |    BackgroundWorker类型描述了后台的各个worker的情况。
            |
            | —》  LaunchParallelWorkers 该函数中启动相应的worker进程。
            |        |
                     |
                     |
                     |RegisterDynamicBackgroundWorker 注册相应的backgroud worker.
                     |
                     |
            |
            |
            |—》  ExecParallelCreateReaders 构建相应的result tuple reader. TupleQueueReader数据类型;
            |
            |
            |—》  gather_getnext 获取下一条记录.
            |         |
            |         |
            |         | —> gather_getnext 读取下一条记录.
            |         |          |
            |                    |—> gather_readnext 读取下一条tuple记录。
            |                    |       |
            |                    |       | —》  TupleQueueReaderNext
            |                                        |
            |                                        |
            |                                        |—》 shm_mq_receive 从mq中读取一条tuple。
            |
            |—》  ExecProject 返回结果集;
            |
            |
            |—》
ParallelWorkerMain
数据结构:InternalParallelWorkers, 数组描述了并行查询的worker的入口点。其中涉及了三个入口点。
static const struct
{
const char *fn_name;
parallel_worker_main_type fn_addr;
} InternalParallelWorkers[] =
{
{
“ParallelQueryMain”, ParallelQueryMain
},
{
“_bt_parallel_build_main”, _bt_parallel_build_main
},
{
“parallel_vacuum_main”, parallel_vacuum_main
}
};
通过查询概数组 InternalParallelWorkers 中的worker的入口点等信息,来获取具体并行信息。
ParallelQueryMain: 并行查询的工作入口点,在该函数中我们会使用worker进行具体的工作。
ParallelWorkerMain: 并行查询的worker的入口。
为啥我们需要根据函数名字进行查询出worker的地址? 因为有可能worker的函数地址在不同进程
的不同的地址空间内,因此,为了防止直接传递函数地址导致的core,我们使用函数的名字进行查找。
ExecInitGather/ExecGatherMerge
         |
         |
        \|/
  LaunchParallelWorkers
           |
           |
          \|/
     ParallelWorkerMain
            |
            |
           \|/
       LookupParallelWorkerFunction
BackgroundWorkerArray, BackgroundWorkerSlot 这些后台work槽中已经包括已经创建好的
后台worker进程。
数组InternalBGWorkers中给出了后台的工作worker的入口点。
BackgroundWorkerData数组中包含了类型BackgroundWorkerArray的数组数据。数组中包含了
worker信息。
BackgroundWorkerShmemInit
    |
    |
    | BackgroundWorkerData = ShmemInitStruct()对该
RegisterBackgroundWorker , 在BackgroundWorkerList 链表中注册一个新的后台worker。

1:并行查询相关参数

1)创建索引,create table as, select into 并行参数。

[show/set] max_parallel_maintenance_workers = N;

2) 并行分区表的jion

[show/set] enable_partitionwise_join=[ON/OFF]

3) 并行分区表聚集

enable_partitionwise_aggregate=ON;

4) 并行hash计算

enable_parallel_hash=ON;

5) leader获取worker的信息

parallel_leader_participation=ON;

6)parallel append功能

enable_parallel_append=ON;

7) 总的worker的数量

max_worker_processes;

8) 所以session同时最大可并行度

max_parallel_workers

9) 单个job里最大的并行度

max_parallel_workers_per_gather;

10) 表顺序扫描或者索引扫描

min_parallel_table_scan=0;

min_parallel_index_scan=0;

11) 并行时优化器cost设置

parallel_tuple_cost=0;

parallel_setup_cost=0;

12)设置表级别并行

alter table table_name set (parallel_workers=N)

 

GCC ARM内联汇编介绍(一)

GNU C编译器支持ARM RISC处理器,使得我们可以在C/C++中进行 ARM汇编语句的编程,从而实现操作较为底层ARM行为。

下面首先介绍GCC ASM的语句格式;

对于汇编语句中所涉及C语句中所引用到的寄存器和常量,会已相应的特定形式进行描述;通常,汇编语句的形式如下:

asm(code:output list:input list:clobber list)

汇编语言和C语言之间的关联是通过汇编语句中code中的第二,三部分和output list、input list进行关联。他们之间是如何发生关联关系?下面我们就给出详细的实例进行介绍。

下面我们看一个bit rotation的实例,该函数的作用是将一个integer的每个bit进行右转操作,然后保存到第二个Integer中。

asm(“move %[result] %[value], ror $1”: [result] “=r”(y):[value] “r”(x))

从上面的语句中我们可以看到,其涉及了result, value, “=r” “r” (x) (y) 这些代码元素;那么这些代码元素的具体含义是什么? 从上面我们知道一条汇编语句分为四个部分:(1)asm指令部分;(2)output list部分;(3)input list部分;(4)clobber部分;其中的第一个部分:asm指令为必选项,即这部分必须出现在asm语句中,即使我们asm指令为空;例如:asm(“”)。 此外,另外三个部分为可选项,可以出现在asm语句中,也不不出现。

下面我们就以bit rotation为实例对这个四个部分进行说明。上述四个部分使用冒号 “:”进行分隔。

下面我们首先看第一个部分。

汇编指令部分,在这部分中,汇编指令被包含在一对双引号中 “”。就像一个字符串一样。例如:上述例子中汇编指令 “move %[result], %[value], ror #1″。

output list部分:该部分由三个成分构成。符合名字,约束说明,C语句中的表达式这三个成分构成。其中符合名字又是以:[符合名] 这样的形式进行描述;约束说明以 “约束说明” 这样的形式进行描述; C语句中的表达式以 (C语句表达式) 这样的形式进行描述;例如:对于上述的例子 [result],描述了一个符合名称为result的对象,该对象的约束条件为:“=r”, 该符合名字所表述的对象在C语句中的对应的变量描述是以(y)这些形式给出,即在该asm语句操作的结果会保存在C语言语句中的变量y中;

input list部分:该部分与output list部分相似,这里不再赘述;

Clobber list部分:在本例中,并未给出。职业Clobber list的具体作用在下面文章中介绍;

这里需要说明的是,如果中间的某个部分没有具体内容,那么用于分割的冒号不能省略; 例如:asm(“msr cpsr, %[ps]”:: [ps] “r”(status));

为了代码的可读性,我们可以在asm语句中使用空格,换行,或者是 c style的注释来提高代码的可读性。

asm(” move %[result], %[value], ror #1″

:[result] “=r” (y) /*roration result*/

:[value] “r” (x) /*rotation value*/

:/*clobber list*/

)

在ASM语句中的代码部分(code section),操作数具有如下的形式: %[符合名]。符合名与C语句中的变量的名字属于不同的名字空间,两者没有关联关系,互不受影响。但是对于ASM语句中的符号名需要满足不重复性。这些符合名将会出现在操作符列表中。例如在output list和input list中。

事实上,GCC早期的版本并不支持符号名这一写法,这一写法是直到3.1版本后才支持。例如:早期的写法如下:

asm(“mov %0, %1, ror #1”: “=r”(result): “r”(value));

在该种写法下,操作数以%N的形式给出,其中N为0-9的数字。例如本例中的%0描述了第一个操作数,%1描述了第二个操作数;该种方式下,对于代码的可读性将是一个噩梦。因此在3.1版本后,GNU使用了更加人性化的描述方式来给出哥哥操作数的形式化,从而提高了代码的可读性。