分表概述
數據庫分表,就是把一張表分紅多張表,物理上雖然分開了,邏輯上彼此仍有聯繫。html
分表有兩種方式:水平分表,即按列分開;垂直分表,即按行分開sql
優點數據庫
1. 查詢速度大幅提高服務器
2. 刪除數據速度更快函數
3. 能夠將使用率低的數據經過表空間技術轉移到低成本的存儲介質上性能
場景測試
官方建議:當數據表大小超過數據庫服務器內存時應該使用分表。spa
兩種分表方式大體相同,下面以垂直分表爲例進行介紹。.net
垂直分表
基本過程
1. 建立父表code
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