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,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,'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

補充:postgresql10以上的自動分割槽分表功能

一.列分表

1.首先建立主分割槽表:

create table fenbiao( 
id int,year varchar 
) partition by list(year)

這裡設定的是根據year列進行資料分表;建立後使用navicat是看不到的;

2.建立分表:

create table fenbiao_2017 partition of fenbiao for values in ('2017')

create table fenbiao_2018 partition of fenbiao for values in ('2018')

這樣這兩天資料會依靠規則插入到不同分表中,如果插入一條不符合規則的資料,則會報錯誤:no partition of relation "fenbiao" found for row.

二.範圍分表

1.以year列為範圍進行分表

create table fenbiao2( 
id int,year varchar 
) partition by range(year)

2.建立分表

create table fenbiao2_2018_2020 partition of fenbiao2 for values from ('2018') to ('2020')

create table fenbiao2_2020_2030 partition of fenbiao2 for values from ('2020') to ('2030')

注意:此時插入year=2020會插入到下面的表;如下面表範圍為2021到2030,則會報錯;同時插入2030也會報錯;範圍相當於時a<=year<b;

以上為個人經驗,希望能給大家一個參考,也希望大家多多支援我們。如有錯誤或未考慮完全的地方,望不吝賜教。