PostgreSQL-優化之分表


分表概述

數據庫分表,就是把一張表分成多張表,物理上雖然分開了,邏輯上彼此仍有聯系。

分表有兩種方式:水平分表,即按列分開;垂直分表,即按行分開

優勢

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


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM