1. 程式人生 > >PostgreSQL 11 新特性之預設分割槽

PostgreSQL 11 新特性之預設分割槽

文章目錄

PosgtreSQL 11 支援為分割槽表建立一個預設(DEFAULT)的分割槽,用於儲存無法匹配其他任何分割槽的資料。顯然,只有 RANGE 分割槽表和 LIST 分割槽表需要預設分割槽。

CREATE TABLE measurement (
    city_id         int not null,
    logdate         date not null,
    peaktemp        int,
    unitsales       int
) PARTITION BY RANGE (logdate);

CREATE
TABLE measurement_y2018 PARTITION OF measurement FOR VALUES FROM ('2018-01-01') TO ('2019-01-01');

以上示例只建立了 2018 年的分割槽,如果插入 2017 年的資料,系統將會無法找到相應的分割槽:

INSERT INTO measurement(city_id,logdate,peaktemp,unitsales)
VALUES (1, '2017-10-01', 50, 200);
ERROR:  no partition of relation "measurement" found for row
DETAIL: Partition key of the failing row contains (logdate) = (2017-10-01).

使用預設分割槽可以解決這類問題。建立預設分割槽時使用 DEFAULT 子句替代 FOR VALUES 子句。

CREATE TABLE measurement_default PARTITION OF measurement DEFAULT;
\d+ measurement
                                 Table "public.measurement"
  Column   |  Type   |
Collation | Nullable | Default | Storage | Stats target | Description -----------+---------+-----------+----------+---------+---------+--------------+------------- city_id | integer | | not null | | plain | | logdate | date | | not null | | plain | | peaktemp | integer | | | | plain | | unitsales | integer | | | | plain | | Partition key: RANGE (logdate) Partitions: measurement_y2018 FOR VALUES FROM ('2018-01-01') TO ('2019-01-01'), measurement_default DEFAULT

有了預設分割槽之後,未定義分割槽的資料將會插入到預設分割槽中:

INSERT INTO measurement(city_id,logdate,peaktemp,unitsales)
VALUES (1, '2017-10-01', 50, 200);
INSERT 0 1

select * from measurement_default;
 city_id |  logdate   | peaktemp | unitsales 
---------+------------+----------+-----------
       1 | 2017-10-01 |       50 |       200
(1 row)

預設分割槽存在以下限制:

  • 一個分割槽表只能擁有一個 DEFAULT 分割槽;
  • 對於已經儲存在 DEFAULT 分割槽中的資料,不能再建立相應的分割槽;參見下文示例;
  • 如果將已有的表掛載為 DEFAULT 分割槽,將會檢查該表中的所有資料;如果在已有的分割槽中存在相同的資料,將會產生一個錯誤;
  • 雜湊分割槽表不支援 DEFAULT 分割槽,實際上也不需要支援。

使用預設分割槽也可能導致一些不可預見的問題。例如,往 measurement 表中插入一條 2019 年的資料,由於沒有建立相應的分割槽,該記錄同樣會分配到預設分割槽:

INSERT INTO measurement(city_id,logdate,peaktemp,unitsales)
VALUES (1, '2019-03-25', 66, 100);
INSERT 0 1

select * from measurement_default;
 city_id |  logdate   | peaktemp | unitsales 
---------+------------+----------+-----------
       1 | 2017-10-01 |       50 |       200
       1 | 2019-03-25 |       66 |       100
(2 rows)

此時,如果再建立 2019 年的分割槽,操作將會失敗。因為新增新的分割槽需要修改預設分割槽的範圍(不再包含 2019 年的資料),但是預設分割槽中已經存在 2019 年的資料。

CREATE TABLE measurement_y2019 PARTITION OF measurement
    FOR VALUES FROM ('2019-01-01') TO ('2020-01-01');
ERROR:  updated partition constraint for default partition "measurement_default" would be violated by some row

為了解決這個問題,可以先將預設分割槽從分割槽表中解除安裝(DETACH PARTITION),建立新的分割槽,將預設分割槽中的相應的資料移動到新的分割槽,最後重新掛載預設分割槽。

ALTER TABLE measurement DETACH PARTITION measurement_default;

CREATE TABLE measurement_y2019 PARTITION OF measurement
    FOR VALUES FROM ('2019-01-01') TO ('2020-01-01');

INSERT INTO measurement_y2019
SELECT * FROM measurement_default WHERE logdate >= '2019-01-01' AND logdate < '2020-01-01';
INSERT 0 1

DELETE FROM measurement_default WHERE logdate >= '2019-01-01' AND logdate < '2020-01-01';
DELETE 1

ALTER TABLE measurement ATTACH PARTITION measurement_default DEFAULT;

CREATE TABLE measurement_y2020 PARTITION OF measurement
    FOR VALUES FROM ('2020-01-01') TO ('2021-01-01');

\d+ measurement
                                 Table "public.measurement"
  Column   |  Type   | Collation | Nullable | Default | Storage | Stats target | Description 
-----------+---------+-----------+----------+---------+---------+--------------+-------------
 city_id   | integer |           | not null |         | plain   |              | 
 logdate   | date    |           | not null |         | plain   |              | 
 peaktemp  | integer |           |          |         | plain   |              | 
 unitsales | integer |           |          |         | plain   |              | 
Partition key: RANGE (logdate)
Partitions: measurement_y2018 FOR VALUES FROM ('2018-01-01') TO ('2019-01-01'),
            measurement_y2019 FOR VALUES FROM ('2019-01-01') TO ('2020-01-01'),
            measurement_y2020 FOR VALUES FROM ('2020-01-01') TO ('2021-01-01'),
            measurement_default DEFAULT

官方文件:Table Partitioning

人生本來短暫,你又何必匆匆!點個贊再走吧!