PostgreSQL码农集散地

PostgreSQL 18 preview - 奇慢无比的GIN索引创建支持并行了

PostgreSQL 18 preview - 奇慢无比的GIN索引创建支持并行了

GIN 典型的应用场景是多值列索引, 例如全文检索、数组、JSON字段. gin结构很简单, element + ctid list/tree.

构建gin索引时比较慢, 索引比btree大, 因为一个值里面可能有多个element, 每个element都需要存储其对应的行号.

但是gin索引有并行构建的基础, 因为每个行分配给不同的进程, 所以不同进程的"行号list"都是独一无二的. sort完后将不同进程的相同element的"行号 list"合并即可.

https://git.postgresql.org/gitweb/?p=postgresql.git;a=commit;h=8492feb98f6df3f0f03e84ed56f0d1cbb2ac514c

Allow parallel CREATE INDEX for GIN indexes  
author Tomas Vondra <[email protected]>   
Mon, 3 Mar 2025 15:53:03 +0000 (16:53 +0100)  
committer Tomas Vondra <[email protected]>   
Mon, 3 Mar 2025 15:53:06 +0000 (16:53 +0100)  
commit 8492feb98f6df3f0f03e84ed56f0d1cbb2ac514c  
tree 8b5775ca7cdb77a61c4ada41b45579e50bc3cf35 tree  
parent 3f1db99bfabbb9d4afc41f362d9801512f4c7c65 commit | diff  
Allow parallel CREATE INDEX for GIN indexes  

Allow using parallel workers to build a GIN index, similarly to BTREE  
and BRIN. For large tables this may result in significant speedup when  
the build is CPU-bound.  

The work is divided so that each worker builds index entries on a subset  
of the table, determined by the regular parallel scan used to read the  
data. Each worker uses a local tuplesort to sort and merge the entries  
for the same key. The TID lists do not overlap (for a given key), which
means the merge sort simply concatenates the two lists. The merged  
entries are written into a shared tuplesort for the leader.  

The leader needs to merge the sorted entries again, before writing them  
into the index. But this way a significant part of the work happens in
the workers, and the leader is left with merging fewer large entries,  
which is more efficient.  

Most of the parallelism infrastructure is a simplified copy of the code  
used by BTREE indexes, omitting the parts irrelevant for GIN indexes  
(e.g. uniqueness checks).  

Original patch by me, with reviews and substantial improvements by  
Matthias van de Meent, certainly enough to make him a co-author.  

Author: Tomas Vondra, Matthias van de Meent  
Reviewed-by: Matthias van de Meent, Andy Fan, Kirill Reshke  
Discussion: https://postgr.es/m/6ab4003f-a8b8-4d75-a67f-f25ad98582dc%40enterprisedb.com  

这个 patch 的主要目的是允许在创建 GIN 索引时使用并行工作线程,从而加速大表的索引构建过程。以下是该 patch 的详细解读:


背景

GIN(Generalized Inverted Index)是 PostgreSQL 中用于支持全文搜索、数组、JSONB 等复杂数据类型的索引。在之前的版本中,GIN 索引的构建是单线程的,对于大表来说,这可能会成为性能瓶颈,尤其是在 CPU 密集型场景下。

PostgreSQL 已经支持并行构建 BTREE 和 BRIN 索引,但 GIN 索引的并行化实现较为复杂,因为 GIN 索引的结构(如 TID 列表的合并)与 BTREE 和 BRIN 索引不同。


改动内容

  1. 并行化 GIN 索引构建:

  • 该 patch 允许在创建 GIN 索引时使用并行工作线程,类似于 BTREE 和 BRIN 索引的并行构建。
  • 并行工作线程的数量由 max_parallel_workers_per_gather 参数控制。
  • 工作分配:

    • 每个工作线程负责处理表的一个子集,数据通过常规的并行扫描读取。
    • 每个工作线程使用本地的 tuplesort 对相同键的索引条目进行排序和合并。
    • 由于 TID 列表不会重叠(对于同一个键),合并操作只需简单地将两个列表连接起来。
  • 共享数据结构:

    • 合并后的条目被写入一个共享的 tuplesort,由领导者进程(leader)进一步处理。
    • 领导者进程需要再次合并排序后的条目,然后将其写入最终的 GIN 索引。
  • 性能优化:

    • 大部分工作(如排序和合并)由工作线程完成,领导者进程只需处理较少的、较大的条目,从而提高效率。
    • 这种设计减少了领导者进程的负载,使其能够更高效地完成最终索引的写入。
  • 代码复用与简化:

    • 并行化基础设施的大部分代码是从 BTREE 索引的并行化实现中复用的,但省略了与 GIN 索引无关的部分(如唯一性检查)。

    性能影响

    • 对于大表的 GIN 索引构建,尤其是在 CPU 密集型场景下,该 patch 可以显著加速索引构建过程。
    • 并行化能够充分利用多核 CPU 的计算能力,减少索引构建的总时间。

    总结

    这个 patch 通过引入并行化机制,显著提升了 GIN 索引构建的性能,特别是在大表和高 CPU 负载的场景下。其主要特点包括:

    1. 使用并行工作线程处理表的不同子集。
    2. 通过本地和共享的 tuplesort 实现高效的排序和合并。
    3. 复用 BTREE 索引的并行化基础设施,同时针对 GIN 索引的特性进行优化。

    该改动是 PostgreSQL 在索引构建性能优化方面的重要进展,进一步扩展了并行化的应用范围。