postgresql在线创建分区表索引

postgresql在线创建分区表索引

分区表在线创建索引报错

关于在线创建索引,我们可以在create index时加上参数CONCURRENTLY

但是当在分区表上使用该参数时会报错

test_db=# create index CONCURRENTLY on test_p_t(num); ERROR: cannot create index on partitioned table "test_p_t" concurrently 
问题分析

查询官方文档发现分区表是不支持在线创建索引的,并且提供了一种影响最小的创建方式,先在分区表的每个分区上在线创建索引,再在父表上以普通方式创建索引,父表创建索引时会先检查分区上是否有等效索引,如果有等效索引,并且该索引没有其他继承关系,则只是把分区上该索引继承到父表索引下,如果没有等效索引才会在分区上创建索引。由此看来,我们已经在每个分区上在线创建好索引后,再在父表以普通方式创建索引,或者在父表创建时使用ONLY参数,再使用"ALTER INDEX parent_index_name ATTACH PARTITION index_name;"显式的去标记索引继承关系。

官方文档里对在分区表上创建索引的部分解释如下:

当前不支持在分区表上并发生成索引。 然而,你可以在每个分区上单独的并发构建索引,然后最终以非并发的方式创建分区索引,以减少对分区表的写入被锁定的时间。 在这种情况下,生成分区索引仅是元数据操作。 当在一个分区表上调用CREATE INDEX时,默认的行为是递归到所有的分区上以确保它们都具有匹配的索引。每一个分区首先会被检查是否有一个等效的索引存在, 如果有则该索引将被挂接为被创建索引的一个分区索引,而被创建的索引将成为其父索引。如果不存在匹配的索引,则会创建一个新的索引并且自动进行挂接。 如果命令中没有指定索引名称,每个分区中的新索引的名称将被自动决定。如果指定了ONLY选项,则不会进行递归, 并且该索引会被标记为无效(一旦所有的分区都得到该索引,ALTER INDEX ... ATTACH PARTITION可以把该索引标记为有效)。 不过,要注意不管是否指定这一选项,未来使用CREATE TABLE ... PARTITION OF创建的任何分区将自动有一个匹配的索引,不管有没有指定ONLY。 
方法验证
创建分区表
test_db=# \d+ test_p_t
                           Partitioned table "public.test_p_t"
 Column |  Type   | Collation | Nullable | Default | Storage | Stats target | Description
--------+---------+-----------+----------+---------+---------+--------------+-------------
 id     | integer |           |          |         | plain   |              |
 num    | integer |           |          |         | plain   |              |
Partition key: RANGE (id)
Triggers:
    test_ptt AFTER UPDATE ON test_p_t FOR EACH ROW EXECUTE FUNCTION test_ptt()
Partitions: test_p_t_1 FOR VALUES FROM (1) TO (10),
            test_p_t_2 FOR VALUES FROM (11) TO (20)

test_db=# \d+ test_p_t_1
                                Table "public.test_p_t_1"
 Column |  Type   | Collation | Nullable | Default | Storage | Stats target | Description
--------+---------+-----------+----------+---------+---------+--------------+-------------
 id     | integer |           |          |         | plain   |              |
 num    | integer |           |          |         | plain   |              |
Partition of: test_p_t FOR VALUES FROM (1) TO (10)
Partition constraint: ((id IS NOT NULL) AND (id >= 1) AND (id < 10))
Triggers:
    test_ptt AFTER UPDATE ON test_p_t_1 FOR EACH ROW EXECUTE FUNCTION test_ptt()
Access method: heap

test_db=# \d+ test_p_t_2
                                Table "public.test_p_t_2"
 Column |  Type   | Collation | Nullable | Default | Storage | Stats target | Description
--------+---------+-----------+----------+---------+---------+--------------+-------------
 id     | integer |           |          |         | plain   |              |
 num    | integer |           |          |         | plain   |              |
Partition of: test_p_t FOR VALUES FROM (11) TO (20)
Partition constraint: ((id IS NOT NULL) AND (id >= 11) AND (id < 20))
Triggers:
    test_ptt AFTER UPDATE ON test_p_t_2 FOR EACH ROW EXECUTE FUNCTION test_ptt()
Access method: heap
第一种,在父表上以普通方式创建索引
在分区上创建索引

此时两条索引都是没有继承关系的

test_db=# create index CONCURRENTLY on test_p_t_1(id);
CREATE INDEX
test_db=# create index CONCURRENTLY on test_p_t_2(id);
CREATE INDEX
test_db=# \d+ test_p_t_1
                                Table "public.test_p_t_1"
 Column |  Type   | Collation | Nullable | Default | Storage | Stats target | Description
--------+---------+-----------+----------+---------+---------+--------------+-------------
 id     | integer |           |          |         | plain   |              |
 num    | integer |           |          |         | plain   |              |
Partition of: test_p_t FOR VALUES FROM (1) TO (10)
Partition constraint: ((id IS NOT NULL) AND (id >= 1) AND (id < 10))
Indexes:
    "idx_testpt1_id" btree (id)
Triggers:
    test_ptt AFTER UPDATE ON test_p_t_1 FOR EACH ROW EXECUTE FUNCTION test_ptt()
Access method: heap

test_db=# \d+ test_p_t_2
                                Table "public.test_p_t_2"
 Column |  Type   | Collation | Nullable | Default | Storage | Stats target | Description
--------+---------+-----------+----------+---------+---------+--------------+-------------
 id     | integer |           |          |         | plain   |              |
 num    | integer |           |          |         | plain   |              |
Partition of: test_p_t FOR VALUES FROM (11) TO (20)
Partition constraint: ((id IS NOT NULL) AND (id >= 11) AND (id < 20))
Indexes:
    "idx_testpt2_id" btree (id)
Triggers:
    test_ptt AFTER UPDATE ON test_p_t_2 FOR EACH ROW EXECUTE FUNCTION test_ptt()
Access method: heap

test_db=# select inhrelid::regclass,inhparent::regclass from pg_inherits where inhrelid in ('idx_testpt1_id'::regclass,'idx_testpt2_id'::regclass);
 inhrelid | inhparent
----------+-----------
(0 rows)
在父表创建索引

可以看到分区上的索引已经继承到了父表的索引下

test_db=# create index on test_p_t(id);
CREATE INDEX
test_db=# \d+ test_p_t
                           Partitioned table "public.test_p_t"
 Column |  Type   | Collation | Nullable | Default | Storage | Stats target | Description
--------+---------+-----------+----------+---------+---------+--------------+-------------
 id     | integer |           |          |         | plain   |              |
 num    | integer |           |          |         | plain   |              |
Partition key: RANGE (id)
Indexes:
    "test_p_t_id_idx" btree (id)
Triggers:
    test_ptt AFTER UPDATE ON test_p_t FOR EACH ROW EXECUTE FUNCTION test_ptt()
Partitions: test_p_t_1 FOR VALUES FROM (1) TO (10),
            test_p_t_2 FOR VALUES FROM (11) TO (20)

test_db=# select inhrelid::regclass,inhparent::regclass from pg_inherits where inhrelid in ('idx_testpt1_id'::regclass,'idx_testpt2_id'::regclass);
    inhrelid    |    inhparent
----------------+-----------------
 idx_testpt1_id | test_p_t_id_idx
 idx_testpt2_id | test_p_t_id_idx
(2 rows)
另一种方式,在父表创建索引时使用ONLY参数
父表和分区表分别创建索引

可以看到父表上索引test_p_t_num_idx状态是无效的,分区上两条索引也是没有继承关系的

test_db=# create index CONCURRENTLY idx_testpt2_num on test_p_t_2(num);
CREATE INDEX
test_db=# create index CONCURRENTLY idx_testpt1_num on test_p_t_1(num);
CREATE INDEX
test_db=# create index on only test_p_t(num);
CREATE INDEX
test_db=# \d+ test_p_t
                           Partitioned table "public.test_p_t"
 Column |  Type   | Collation | Nullable | Default | Storage | Stats target | Description
--------+---------+-----------+----------+---------+---------+--------------+-------------
 id     | integer |           |          |         | plain   |              |
 num    | integer |           |          |         | plain   |              |
Partition key: RANGE (id)
Indexes:
    "test_p_t_id_idx" btree (id)
    "test_p_t_num_idx" btree (num) INVALID
Triggers:
    test_ptt AFTER UPDATE ON test_p_t FOR EACH ROW EXECUTE FUNCTION test_ptt()
Partitions: test_p_t_1 FOR VALUES FROM (1) TO (10),
            test_p_t_2 FOR VALUES FROM (11) TO (20)

test_db=# \d+ test_p_t_1
                                Table "public.test_p_t_1"
 Column |  Type   | Collation | Nullable | Default | Storage | Stats target | Description
--------+---------+-----------+----------+---------+---------+--------------+-------------
 id     | integer |           |          |         | plain   |              |
 num    | integer |           |          |         | plain   |              |
Partition of: test_p_t FOR VALUES FROM (1) TO (10)
Partition constraint: ((id IS NOT NULL) AND (id >= 1) AND (id < 10))
Indexes:
    "idx_testpt1_id" btree (id)
    "idx_testpt1_num" btree (num)
Triggers:
    test_ptt AFTER UPDATE ON test_p_t_1 FOR EACH ROW EXECUTE FUNCTION test_ptt()
Access method: heap

test_db=# \d+ test_p_t_2
                                Table "public.test_p_t_2"
 Column |  Type   | Collation | Nullable | Default | Storage | Stats target | Description
--------+---------+-----------+----------+---------+---------+--------------+-------------
 id     | integer |           |          |         | plain   |              |
 num    | integer |           |          |         | plain   |              |
Partition of: test_p_t FOR VALUES FROM (11) TO (20)
Partition constraint: ((id IS NOT NULL) AND (id >= 11) AND (id < 20))
Indexes:
    "idx_testpt2_id" btree (id)
    "idx_testpt2_num" btree (num)
Triggers:
    test_ptt AFTER UPDATE ON test_p_t_2 FOR EACH ROW EXECUTE FUNCTION test_ptt()
Access method: heap
test_db=# select inhrelid::regclass,inhparent::regclass from pg_inherits where inhrelid in ('idx_testpt1_num'::regclass,'idx_testpt2_num'::regclass);
 inhrelid | inhparent
----------+-----------
(0 rows)

使用“ALTER INDEX parent_index_name ATTACH PARTITION index_name;”方式标记索引继承关系

可以看到,两个分区索引都继承到父表索引下后,父表索引由无效状态变为正常

test_db=# alter index test_p_t_num_idx attach partition idx_testpt1_num;
ALTER INDEX
test_db=# \d+ test_p_t
                           Partitioned table "public.test_p_t"
 Column |  Type   | Collation | Nullable | Default | Storage | Stats target | Description
--------+---------+-----------+----------+---------+---------+--------------+-------------
 id     | integer |           |          |         | plain   |              |
 num    | integer |           |          |         | plain   |              |
Partition key: RANGE (id)
Indexes:
    "test_p_t_id_idx" btree (id)
    "test_p_t_num_idx" btree (num) INVALID
Triggers:
    test_ptt AFTER UPDATE ON test_p_t FOR EACH ROW EXECUTE FUNCTION test_ptt()
Partitions: test_p_t_1 FOR VALUES FROM (1) TO (10),
            test_p_t_2 FOR VALUES FROM (11) TO (20)

test_db=# select inhrelid::regclass,inhparent::regclass from pg_inherits where inhrelid in ('idx_testpt1_num'::regclass,'idx_testpt2_num'::regclass);
    inhrelid     |    inhparent
-----------------+------------------
 idx_testpt1_num | test_p_t_num_idx
(1 row)

test_db=# alter index test_p_t_num_idx attach partition idx_testpt2_num;
ALTER INDEX
test_db=# \d+ test_p_t
                           Partitioned table "public.test_p_t"
 Column |  Type   | Collation | Nullable | Default | Storage | Stats target | Description
--------+---------+-----------+----------+---------+---------+--------------+-------------
 id     | integer |           |          |         | plain   |              |
 num    | integer |           |          |         | plain   |              |
Partition key: RANGE (id)
Indexes:
    "test_p_t_id_idx" btree (id)
    "test_p_t_num_idx" btree (num)
Triggers:
    test_ptt AFTER UPDATE ON test_p_t FOR EACH ROW EXECUTE FUNCTION test_ptt()
Partitions: test_p_t_1 FOR VALUES FROM (1) TO (10),
            test_p_t_2 FOR VALUES FROM (11) TO (20)

test_db=# select inhrelid::regclass,inhparent::regclass from pg_inherits where inhrelid in ('idx_testpt1_num'::regclass,'idx_testpt2_num'::regclass);
    inhrelid     |    inhparent
-----------------+------------------
 idx_testpt1_num | test_p_t_num_idx
 idx_testpt2_num | test_p_t_num_idx
(2 rows)
注意事项

1、使用参数CONCURRENTLY在线创建索引时,要确保数据库中没有长事务,否则会一直等待事务结束。

2、使用第一种方式创建索引时,要保证所有分区都已经单独创建索引后,才可以在父表创建索引,否则会延长锁表时间。

3、父表索引创建成功后,新增分区时会继承父表属性,自动创建索引。

你可能感兴趣的:(postgresql,postgresql,数据库)