簡介
PostgreSQL屬於關系型數據庫,雖然在排行一直在MySql之下,但是筆者覺得PostgreSQL功能比MySql更加豐富強大,而且使用極爽。特別是近幾年推出的jsonb、分區表等功能,如果合理利用PostgreSQL這些功能,那么使用關系數據庫處理億級數據量將不再是夢想。本篇文章將給大家介紹如何使用PostgreSQL進行分區管理實現億級數據庫優化。
開始
接下來會以銷售單為案例創建兩種銷售表,分別使用分區表和常規表方式進行對比。
創建分區表
在postgre中創建一張sale表並按照銷售日期進行分區
名稱 | 類型 | 是否主鍵 | 描述 |
---|---|---|---|
id | guid | 是 | id |
sale_date | date | 否 | 銷售日期 |
country_code | text | 否 | 地區編碼 |
product_sku | text | 否 | 商品sku |
units | int4 | 否 | 單位 |
創建sale主表
CREATE TABLE sale (
Id uuid,
sale_date date not null,
country_code text,
product_sku text,
units integer
) PARTITION BY RANGE (sale_date);
創建sale分表
CREATE TABLE sale_202001 PARTITION OF sale FOR VALUES FROM ('2020-01-01') TO ('2020-02-01');
CREATE TABLE sale_202002 PARTITION OF sale FOR VALUES FROM ('2020-02-01') TO ('2020-03-01');
CREATE TABLE sale_202003 PARTITION OF sale FOR VALUES FROM ('2020-03-01') TO ('2020-04-01');
CREATE TABLE sale_202004 PARTITION OF sale FOR VALUES FROM ('2020-04-01') TO ('2020-05-01');
CREATE TABLE sale_202005 PARTITION OF sale FOR VALUES FROM ('2020-05-01') TO ('2020-06-01');
CREATE TABLE sale_202006 PARTITION OF sale FOR VALUES FROM ('2020-06-01') TO ('2020-07-01');
CREATE TABLE sale_202007 PARTITION OF sale FOR VALUES FROM ('2020-07-01') TO ('2020-08-01');
CREATE TABLE sale_202008 PARTITION OF sale FOR VALUES FROM ('2020-08-01') TO ('2020-09-01');
CREATE TABLE sale_202009 PARTITION OF sale FOR VALUES FROM ('2020-09-01') TO ('2020-10-01');
CREATE TABLE sale_202010 PARTITION OF sale FOR VALUES FROM ('2020-10-01') TO ('2020-11-01');
CREATE TABLE sale_202011 PARTITION OF sale FOR VALUES FROM ('2020-11-01') TO ('2020-12-01');
CREATE TABLE sale_202012 PARTITION OF sale FOR VALUES FROM ('2020-12-01') TO ('2021-01-01');
給分表件索引
postgre分區表中實際數據都是存在區表上的,所以只有給區表建索引才有作用。
ALTER TABLE sale_202001 ADD PRIMARY KEY(id);
ALTER TABLE sale_202002 ADD PRIMARY KEY(id);
ALTER TABLE sale_202003 ADD PRIMARY KEY(id);
ALTER TABLE sale_202004 ADD PRIMARY KEY(id);
ALTER TABLE sale_202005 ADD PRIMARY KEY(id);
ALTER TABLE sale_202006 ADD PRIMARY KEY(id);
ALTER TABLE sale_202007 ADD PRIMARY KEY(id);
ALTER TABLE sale_202008 ADD PRIMARY KEY(id);
ALTER TABLE sale_202009 ADD PRIMARY KEY(id);
ALTER TABLE sale_202010 ADD PRIMARY KEY(id);
ALTER TABLE sale_202011 ADD PRIMARY KEY(id);
ALTER TABLE sale_202012 ADD PRIMARY KEY(id);
創建常規表
在postgre中創建一直saleAll常規表不做分區
CREATE TABLE saleAll (
Id uuid,
sale_date date not null,
country_code text,
product_sku text,
units integer
);
ALTER TABLE saleAll ADD PRIMARY KEY(id);
插入海量數據
通過5000萬條數據對比分區表和常規表查詢性能
sale分區表批量插入數據
-- 隨機數 --
CREATE OR REPLACE FUNCTION random_between(low INT ,high INT)
RETURNS INT AS
$$
BEGIN
RETURN floor(random()* (high-low + 1) + low);
END;
$$ language 'plpgsql' STRICT;
-- sale批量新增 --
create or replace function insertSale() returns void as
$$
declare
i int :=0;
p TEXT :='';
tempTime timestamp;
j int;
begin
while i < 50000000 loop
j:=round(random()*80);
p:=concat('P-', random_between(1000,2000)::TEXT);
tempTime:=date '2020-01-01' + j;
insert into sale(id, sale_date, country_code, product_sku, units)
values(uuid_generate_v4(),tempTime, 'CN', p ,round(random() * 100));
i:= i+1;
raise notice 'holy shit%', i;
end loop;
end;
$$ language plpgsql;
saleAll常規表批量插入數據
-- saleAll批量新增 --
create or replace function insertSaleAll() returns void as
$$
declare
i int :=0;
p TEXT :='';
tempTime timestamp;
j int;
begin
while i < 50000000 loop
j:=round(random()*80);
p:=concat('P-', random_between(1000,2000)::TEXT);
tempTime:=date '2020-01-01' + j;
insert into saleAll(id, sale_date, country_code, product_sku, units)
values(uuid_generate_v4(),tempTime, 'CN', p ,round(random() * 100));
i:= i+1;
raise notice 'holy shit%', i;
end loop;
end;
$$ language plpgsql;
執行批量插入
SELECT insertSale();
SELECT insertSaleAll();
批量插入海量數據需要等待很久,建議在單獨的測試庫中進行,否則影響數據庫性能。
查詢性能對比
首先對分區表和常規表進行查詢性能對比
全表掃描
使用count(*)
對分區表進行全表掃描
使用 count(*)
對常規表進行全部掃描
可以看出分區表和常規表全表掃描沒有明顯差距
日期條件查詢
分區表與常規表分表使用sale_date進行查詢,耗時對比如下:
查詢 | 日期范圍 | sale分區表數據量 | saleAll常規表數據量 | 分區表耗時(s) | 常規表耗時(s) |
---|---|---|---|---|---|
count(*) | 2020-01-01至2020-02-01 | 11113634 | 12991066 | 1.355 | 3.042 |
count(*) | 2020-02-01至2020-03-01 | 10576517 | 12371957 | 1.224 | 3.030 |
count(*) | 2020-03-01至2020-04-01 | 7506640 | 8541846 | 0.980 | 2.848 |
count(*) | 2020-04-01至2020-05-01 | 5154498 | 4956280 | 0.706 | 2.716 |
count(*) | 2020-05-01至2020-06-01 | 5321426 | 5199780 | 0.607 | 2.760 |
count(*) | 2020-06-01至2020-07-01 | 3395589 | 3181768 | 0.495 | 2.657 |
count(*) | 2020-07-01至2020-08-01 | 2561833 | 788134 | 0.415 | 2.634 |
count(*) | 2020-08-01至2020-09-01 | 2524290 | 798648 | 0.326 | 2.601 |
count(*) | 2020-09-01至2020-10-01 | 1591223 | 481197 | 0.246 | 2.654 |
count(*) | 2020-10-01至2020-11-01 | 986697 | 1181387 | 0.240 | 2.617 |
count(*) | 2020-11-01至2020-12-01 | 897223 | 1162590 | 0.206 | 2.662 |
count(*) | 2020-12-01至2020-12-31 | 488046 | 730686 | 0.095 | 2.582 |
從上面對比可以分析出使用sale_date字段進行查詢時sale主表自動導航到區別進行查詢,所以查詢耗時回根據區表的大小有所差異。常規表使用sale_date進行查詢時會掃描全表,所以查詢時間非常平均,而且數據量較大時查詢速度會非常緩慢。
索引查詢
使用索引對分區表和常規表進行查詢比較
sale分區表使用索引查詢如下:
saleAll常規表使用索引查詢如下:
通過索引查詢結果顯示分區表和常規表的速度都是毫秒內的,但是如果細算一下分區表的查詢速度比常規表快4倍左右。
普通字段查詢
下面使用product_sku普通字段進行查詢對比
分區表查詢:
常規表查詢:
總結
通過以上操作對比可以看出,在單表數據過大時查詢速度會明細變慢,使用分區表對大數據量進行分表存儲而所有增刪改只需對主表進行操作會大大提高查詢效率。Postgre中還要更多的配置用來提高性能,或許我們在系統架構中無需搭建大數據平台,直接使用Postgre處理大數據也會成為一種選擇。