You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

PostgreSQL 16/PolarDB-PG 16中如何在分区表父表上并发创建索引?

PostgreSQL 16/PolarDB-PG 16中如何在分区表父表上并发创建索引?

你遇到的这个问题其实是PostgreSQL 16之后官方特意调整的行为——PG14及之前的版本因为实现逻辑的原因,允许直接在分区表父表上用CREATE INDEX CONCURRENTLY,但PG16开始官方收紧了这个限制。毕竟分区表的索引本质是每个子分区独立维护的,父表只是逻辑上的聚合,在父表层面做并发建索引很难协调所有分区的并发流程,容易出现一致性问题,所以直接禁止了这种操作。

不过别担心,我们可以用「分而治之」的方式达到同样的无锁(低锁)建索引效果,下面是具体的解决方案:

方案一:手动给每个分区并发建索引,再关联父表

这是最直接的方式,思路是先给每个子分区单独执行CREATE INDEX CONCURRENTLY(这样不会阻塞分区的读写),然后在父表上创建分区索引(这个操作只是元数据层面的关联,速度极快,几乎不锁表)。

具体代码步骤:

-- 1. 逐个给分区创建并发索引
CREATE INDEX CONCURRENTLY measurement_y2020_logdate_idx ON measurement_y2020 (logdate);
CREATE INDEX CONCURRENTLY measurement_y2021_logdate_idx ON measurement_y2021 (logdate);

-- 2. 在父表上创建分区索引(无需加CONCURRENTLY,因为只是关联已有的分区索引)
CREATE INDEX measurement_logdate_idx ON measurement (logdate);

执行完成后,你可以用\d+ measurement查看,父表的measurement_logdate_idx会自动关联所有分区的对应索引,查询时会正常使用这个索引,而且整个过程中分区的读写操作不会被长时间阻塞。

方案二:自动遍历所有分区并发建索引(适合分区较多的场景)

如果你的分区表有很多子分区,手动逐个执行太麻烦,可以用PL/pgSQL写个小脚本自动处理:

DO $$
DECLARE
    partition_rec RECORD;
BEGIN
    -- 遍历所有属于measurement父表的子分区
    FOR partition_rec IN (
        SELECT inhrelid::regclass AS partition_name
        FROM pg_inherits
        JOIN pg_class child ON pg_inherits.inhrelid = child.oid
        JOIN pg_class parent ON pg_inherits.inhparent = parent.oid
        WHERE parent.relname = 'measurement'
    ) LOOP
        -- 给每个分区动态创建并发索引
        EXECUTE format(
            'CREATE INDEX CONCURRENTLY %I ON %I (logdate)',
            partition_rec.partition_name || '_logdate_idx',
            partition_rec.partition_name
        );
    END LOOP;
END $$;

-- 最后关联父表索引
CREATE INDEX measurement_logdate_idx ON measurement (logdate);

这个脚本会自动找出所有属于measurement的子分区,逐个创建并发索引,之后再关联父表,全程无需手动干预。

注意事项

  1. 父表创建索引时不要加CONCURRENTLY:因为这个操作只是把已有的分区索引关联到父表,不需要扫描数据,速度极快,也不会锁表。
  2. 新增分区的后续处理:如果之后给父表新增了子分区,需要手动给新分区创建对应的并发索引,然后执行ALTER INDEX measurement_logdate_idx ATTACH PARTITION measurement_y2022_logdate_idx; 把新分区的索引关联到父表;或者在创建新分区时用INDEXES子句自动创建索引(但如果要并发的话还是得单独执行CREATE INDEX CONCURRENTLY)。
  3. 唯一索引的情况:如果要创建唯一分区索引,需要确保索引包含分区键(你的例子中logdate就是分区键,刚好符合要求),同样按照上述步骤,先给分区建唯一并发索引,再在父表创建唯一分区索引即可。

为什么PG16禁止直接在父表用CONCURRENTLY?

简单来说,分区表的索引是「分区级」的,每个分区的索引都是独立的数据库对象。在父表上执行CREATE INDEX CONCURRENTLY需要协调所有分区的并发建索引流程,这个过程逻辑复杂,容易出现死锁、数据不一致等问题,所以PG16官方直接禁止了这种操作,转而推荐「先分区并发建索引,再关联父表」的更安全的方式。

火山引擎 最新活动