分表概述
數據庫分表,就是把一張表分成多張表,物理上雖然分開了,邏輯上彼此仍有聯系。
分表有兩種方式:水平分表,即按列分開;垂直分表,即按行分開
優勢
1. 查詢速度大幅提升
2. 刪除數據速度更快
3. 可以將使用率低的數據通過表空間技術轉移到低成本的存儲介質上
場景
官方建議:當數據表大小超過數據庫服務器內存時應該使用分表。
兩種分表方式大致相同,下面以垂直分表為例進行介紹。
垂直分表
基本過程
1. 創建父表
2. 創建子表,子表必須繼承父表,最好不要新加字段 【加了以后如何,沒試過】
// 可以給每個子表創建索引
3. 定義一個規則(rule) 或者觸發器(trigger),把對父表的寫入重定向到對應的分表
創建父表
父表無數據,無約束,無索引
CREATE TABLE tbl_partition ( date_key date, hour_key smallint, client_key integer, item_key integer, account integer, expense numeric );
創建分表
分表必須繼承父表
CREATE TABLE tbl_partition_2016_01() inherits (tbl_partition); CREATE TABLE tbl_partition_2016_02() inherits (tbl_partition); CREATE TABLE tbl_partition_2016_03() inherits (tbl_partition);
分表需要添加限制,這些限制決定了每張表允許保存的數據范圍,每張表的限制范圍不能有重疊。
ALTER TABLE tbl_partition_2016_01 ADD CONSTRAINT tbl_partition_2016_01_check_date_key CHECK (date_Key >= '2016-01-01'::date AND date_Key < '2016-02-01'::date); ALTER TABLE tbl_partition_2016_02 ADD CONSTRAINT tbl_partition_2016_02_check_date_key CHECK (date_Key >= '2016-02-01'::date AND date_Key < '2016-03-01'::date); ALTER TABLE tbl_partition_2016_03 ADD CONSTRAINT tbl_partition_2016_03_check_date_key CHECK (date_Key >= '2016-03-01'::date AND date_Key < '2016-04-01'::date);
也可以建表和限制寫在一起
create table t_sys_log_y2016m09 (CHECK (operation_time >= DATE '2016-09-01' AND operation_time< DATE '2016-10-01')) INHERITS (t_sys_log_main);
給分表添加索引
CREATE INDEX tbl_partition_date_key_2016_01 ON tbl_partition_2016_01 (date_key,client_key); CREATE INDEX tbl_partition_date_key_2016_02 ON tbl_partition_2016_02 (date_key,client_key); CREATE INDEX tbl_partition_date_key_2016_03 ON tbl_partition_2016_03 (date_key,client_key);
創建觸發器
表建立完成后,就是寫入的工作,如何將數據自動寫入對應的分表呢?兩種方法,分別是規則(rule)和觸發器(trigger),相比 trigger,rule 開銷更大,這里使用觸發器。
trigger 通常會結合自定義函數來實現分區插入:Function 負責根據條件選擇插入,trigger 負責自動調用 Function
首先定義 Function
CREATE OR REPLACE FUNCTION tbl_partition_trigger() RETURNS TRIGGER AS $$ BEGIN IF NEW.date_key >= DATE '2016-01-01' AND NEW.date_Key < DATE '2016-02-01' THEN INSERT INTO tbl_partition_2016_01 VALUES (NEW.*); ELSIF NEW.date_key >= DATE '2016-02-01' AND NEW.date_Key < DATE '2016-03-01' THEN INSERT INTO tbl_partition_2016_02 VALUES (NEW.*); ELSIF NEW.date_key >= DATE '2016-03-01' AND NEW.date_Key < DATE '2016-04-01' THEN INSERT INTO tbl_partition_2016_03 VALUES (NEW.*); END IF; RETURN NULL; END; $$ LANGUAGE plpgsql;
對父表創建觸發器
CREATE TRIGGER insert_tbl_partition_trigger BEFORE INSERT ON tbl_partition FOR EACH ROW EXECUTE PROCEDURE tbl_partition_trigger();
至此分表成功

性能對比
為了對比分表與不分表的性能,我創建了一個全表。
CREATE TABLE tbl_partition_all ( date_key date, hour_key smallint, client_key integer, item_key integer, account integer, expense numeric );
把數據先全部寫到全表后,遷移到分表
INSERT INTO tbl_partition SELECT * FROM tbl_partition_all;
全表9w條數據,分表每個3w條,測試如下
1. 分表 - 查詢單個分表內的數據
EXPLAIN ANALYZE select count(account) ,client_key from tbl_partition v where v.date_key >='2016-03-02' and v.date_key <='2016-03-07' group by client_key ;
3月份的數據在一個表內,耗時約 18s
全表查同樣的數據
EXPLAIN ANALYZE select count(account) ,client_key from tbl_partition_all v where v.date_key >='2016-03-02' and v.date_key <='2016-03-07' group by client_key ;
耗時約 30s
2. 分表 - 查詢跨分表的數據
EXPLAIN ANALYZE select count(account) ,client_key from tbl_partition v where v.date_key >='2016-01-02' and v.date_key <='2016-03-07' group by client_key ;
跨3個表,耗時約 65s
全表查同樣的數據
EXPLAIN ANALYZE select count(account) ,client_key from tbl_partition_all v where v.date_key >='2016-01-02' and v.date_key <='2016-03-07' group by client_key ;
耗時約 87s
3. 有同事問為什么不直接分呢?不繼承單純按數據建多個表
對此我也進行了測試,單獨建立3個表,分別存放之前每個分表的數據,分別建立索引,然后查詢同樣的數據
EXPLAIN ANALYZE select count(account) ,client_key from test1 v where v.date_key >='2016-01-02' and v.date_key <='2016-01-28' group by client_key union select count(account) ,client_key from test2 v where v.date_key >='2016-02-01' and v.date_key <='2016-02-28' group by client_key union select count(account) ,client_key from test3 v where v.date_key >='2016-03-01' and v.date_key <='2016-03-07' group by client_key ;
耗時約 180s,效率更低
總結:分表效率很高,優於全表和多個單表,我這里只是用了少量的數據,性能並沒有提升很大,如果數據量很大,性能應該提升明顯。
分表其他操作
刪除繼承關系
ALTER TABLE tbl_partition_2016_01 NO INHERIT tbl_partition;
添加繼承關系
ALTER TABLE test1 INHERIT tbl_partition;
參考資料:
https://www.cnblogs.com/winkey4986/p/6824747.html
https://www.jb51.net/article/97937.htm
https://blog.csdn.net/imthemostshuaiin626/article/details/77318911
https://hacpai.com/article/1536655962119
