分表概述

数据库分表,就是把一张表分成多张表,物理上虽然分开了,逻辑上彼此仍有联系。

分表有两种方式:水平分表,即按列分开;垂直分表,即按行分开

优势

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