1. 程式人生 > >PostgreSQL11分割槽表的新特性

PostgreSQL11分割槽表的新特性

本文主要介紹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