PostgreSQL11分区表的新特性

版权声明:本文为博主原创之文章,未经博主允许谢绝转载。 https://blog.csdn.net/pg_hgdb/article/details/83412129

本文主要介绍PostgreSQL11分区表的一些新的特性,实现了PostgreSQL10版本中无法实现的一些功能。

1、UPDATE操作可以跨分区移动行

PostgreSQL 10不允许执行可能导致更新结束时行会移动到其他不同分区的更新。但是在PostgreSQL 11中,是可以这样做的。

举个例子,我们创建一张表并建立两个子表:

postgres=# CREATE TABLE matches (match_date date,match_id int) PARTITION BY RANGE (match_date);
CREATE TABLE
postgres=# CREATE TABLE matches_2017 PARTITION OF matches FOR VALUES FROM ('2017-01-01') TO ('2018-01-01');
CREATE TABLE
postgres=# CREATE TABLE matches_2018 PARTITION OF matches FOR VALUES FROM ('2018-01-01') TO ('2019-01-01');
CREATE TABLE

插入一条数据:

postgres=# INSERT INTO matches VALUES ('2017-11-11', 233);
INSERT 0 1

它会进入matches_2017子表中:

postgres=# select * from matches_2018;
 match_date | match_id 
------------+----------
(0 rows)
postgres=# select * from matches_2017;
 match_date | match_id 
------------+----------
 2017-11-11 |      233
(1 row)

如果尝试更新行,但是根据分区范围,更新后的行应该存在于另一个子表中:

postgres=# UPDATE matches SET match_date='2018-07-14';
UPDATE 1
postgres=# select * from matches_2017;
 match_date | match_id 
------------+----------
(0 rows)
postgres=# select * from matches_2018;
 match_date | match_id 
------------+----------
 2018-07-14 |      233
(1 row)

可以看到,更新成功并且该行进入另一个子表。

2、创建默认分区

在PostgreSQL11中,可以创建一个“默认”分区,它可以存储不属于任何现有分区范围或列表的行。

举例如下:

postgres=# CREATE TABLE matches_default PARTITION OF matches DEFAULT; 
CREATE TABLE

使用默认分区,我们可以插入不属于任何现有分区范围/列表的行:

postgres=# INSERT INTO matches VALUES ('2019-05-10', 14);
INSERT 0 1

postgres=# select * from matches_default;
 match_date | match_id 
------------+----------
 2019-05-10 |       14
(1 row)

3、自动创建索引

在PostgreSQL10中,必须为每个分区手动创建索引,尝试在父表上创建分区会失败。在PostgreSQL11中,如果在父表上创建索引,Postgres将自动在所有子表上创建索引,创建索引后创建的任何新分区也将自动获取添加到其中的索引。

举例如下:

postgres=# CREATE INDEX idx_match_id ON matches(match_id);
CREATE INDEX
postgres=# \d matches_2017
              Table "public.matches_2017"
   Column   |  Type   | Collation | Nullable | Default 
------------+---------+-----------+----------+---------
 match_date | date    |           |          | 
 match_id   | integer |           |          | 
Partition of: matches FOR VALUES FROM ('2017-01-01') TO ('2018-01-01')
Indexes:
    "matches_2017_match_id_idx" btree (match_id)

postgres=# \d matches_2018
              Table "public.matches_2018"
   Column   |  Type   | Collation | Nullable | Default 
------------+---------+-----------+----------+---------
 match_date | date    |           |          | 
 match_id   | integer |           |          | 
Partition of: matches FOR VALUES FROM ('2018-01-01') TO ('2019-01-01')
Indexes:
    "matches_2018_match_id_idx" btree (match_id)

postgres=# \d matches_default
            Table "public.matches_default"
   Column   |  Type   | Collation | Nullable | Default 
------------+---------+-----------+----------+---------
 match_date | date    |           |          | 
 match_id   | integer |           |          | 
Partition of: matches DEFAULT
Indexes:
    "matches_default_match_id_idx" btree (match_id)

4、外键支持

在PostgreSQL10中,分区表中的列不可能是外键。但是在PostgreSQL11中,是支持外键的。

举例如下:

postgres=# CREATE TABLE classes ( class_id integer PRIMARY KEY );
CREATE TABLE
postgres=# CREATE TABLE courses (course_date date NOT NULL,course_id integer REFERENCES classes(class_id)) PARTITION BY RANGE (course_date);
CREATE TABLE

5、唯一索引

在PostgreSQL10中,必须在子表中强制执行唯一约束, 无法在主表上创建唯一索引。在PostgreSQL11中,是可以在主表上创建唯一索引的。

举例如下:

postgres=# CREATE TABLE employee (enrollment_date date,employee_id int,UNIQUE(enrollment_date,employee_id)) PARTITION BY RANGE (enrollment_date);
CREATE TABLE
postgres=# CREATE TABLE employee_2017 PARTITION OF employee FOR VALUES FROM ('2017-01-01') TO ('2018-01-01');
CREATE TABLE
postgres=# CREATE TABLE employee_2018 PARTITION OF employee FOR VALUES FROM ('2018-01-01') TO ('2019-01-01');
CREATE TABLE
postgres=# \d employee_2017
                Table "public.employee_2017"
     Column      |  Type   | Collation | Nullable | Default 
-----------------+---------+-----------+----------+---------
 enrollment_date | date    |           |          | 
 employee_id     | integer |           |          | 
Partition of: employee FOR VALUES FROM ('2017-01-01') TO ('2018-01-01')
Indexes:
    "employee_2017_enrollment_date_employee_id_key" UNIQUE CONSTRAINT, btree (enrollment_date, employee_id)

postgres=# \d employee_2018
                Table "public.employee_2018"
     Column      |  Type   | Collation | Nullable | Default 
-----------------+---------+-----------+----------+---------
 enrollment_date | date    |           |          | 
 employee_id     | integer |           |          | 
Partition of: employee FOR VALUES FROM ('2018-01-01') TO ('2019-01-01')
Indexes:
    "employee_2018_enrollment_date_employee_id_key" UNIQUE CONSTRAINT, btree (enrollment_date, employee_id)

6、分区级别的聚合

PostgreSQL 11附带了一个名为enable_partitionwise_aggregate的新选项,可以打开该选项以使查询计划程序将聚合推送到分区级别。 默认情况下,此选项已关闭。

举个例子,默认关闭时,得到的查询计划会类似于:

postgres=# EXPLAIN SELECT match_date, count(*) FROM matches GROUP BY match_date;
                                QUERY PLAN                                 
---------------------------------------------------------------------------
 HashAggregate  (cost=3.05..3.08 rows=3 width=12)
   Group Key: matches_2017.match_date
   ->  Append  (cost=0.00..3.03 rows=3 width=4)
         ->  Seq Scan on matches_2017  (cost=0.00..1.00 rows=1 width=4)
         ->  Seq Scan on matches_2018  (cost=0.00..1.01 rows=1 width=4)
         ->  Seq Scan on matches_default  (cost=0.00..1.01 rows=1 width=4)
(6 rows)

这表示分组发生在所有单独的每分区扫描的超集上。

打开该选项并查看更新后的计划:

postgres=# SET enable_partitionwise_aggregate=on;
SET
postgres=# EXPLAIN SELECT match_date, count(*) FROM matches GROUP BY match_date;
                                QUERY PLAN                                 
---------------------------------------------------------------------------
 Append  (cost=1.00..3.08 rows=3 width=12)
   ->  HashAggregate  (cost=1.00..1.01 rows=1 width=12)
         Group Key: matches_2017.match_date
         ->  Seq Scan on matches_2017  (cost=0.00..1.00 rows=1 width=4)
   ->  HashAggregate  (cost=1.01..1.02 rows=1 width=12)
         Group Key: matches_2018.match_date
         ->  Seq Scan on matches_2018  (cost=0.00..1.01 rows=1 width=4)
   ->  HashAggregate  (cost=1.01..1.02 rows=1 width=12)
         Group Key: matches_default.match_date
         ->  Seq Scan on matches_default  (cost=0.00..1.01 rows=1 width=4)
(10 rows)

现在每个分区发生一次分组,结果将连接起来(我们在这里按分区键分组)。

7、Hash分区

在PostgreSQL11中,加入了HASH类型的分区。 散列类型分区根据分区键的散列值分配行。 

举个说明如何创建一个散列分区,在本例中是一个text类型的分区键:

postgres=# CREATE TABLE student ( name text ) PARTITION BY HASH (name);
CREATE TABLE
postgres=# CREATE TABLE student_0 PARTITION OF student FOR VALUES WITH (MODULUS 3, REMAINDER 0);
CREATE TABLE
postgres=# CREATE TABLE student_1 PARTITION OF student FOR VALUES WITH (MODULUS 3, REMAINDER 1);
CREATE TABLE
postgres=# CREATE TABLE student_2 PARTITION OF student FOR VALUES WITH (MODULUS 3, REMAINDER 2);
CREATE TABLE
postgres=# INSERT INTO student SELECT md5(n::text) FROM generate_series(0,10000) n;
INSERT 0 10001
postgres=# SELECT count(*) FROM student_0;
 count 
-------
  3402
(1 row)

postgres=# SELECT count(*) FROM student_1;
 count 
-------
  3335
(1 row)

postgres=# SELECT count(*) FROM student_2;
 count 
-------
  3264
(1 row)

By Kalath

猜你喜欢

转载自blog.csdn.net/pg_hgdb/article/details/83412129