关于在线创建索引,我们可以在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)
可以看到父表上索引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)
可以看到,两个分区索引都继承到父表索引下后,父表索引由无效状态变为正常
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、父表索引创建成功后,新增分区时会继承父表属性,自动创建索引。