PostgreSQL 11 新特性之預設分割槽
阿新 • • 發佈:2019-01-09
文章目錄
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
人生本來短暫,你又何必匆匆!點個贊再走吧!