• PG-分区表


    分区表

    早在 10 版本之前 PostgreSQL 分区表一般通过继承加触发器方式实现,称为传统分区表。

    PostgreSQL 10 版本提供的分区表称为内置分区表。

    传统分区表

    传统分区表是通过继承和触发器方式实现的, 其实现过程步骤多, 非常复杂,需要定义父表、定义子表、 定义子表约束 、 创建子表索引 、 创建分区插入、 删除 、 修改函数和触发器等, 可以说是在普通表基础上手工实现的分区表。继承是传统分区表的重要组成部分。

    继承表

    首先定义一张父表,然后创建子表并继承父表

    示例
    创建一张日志模型表 tbl_log
    create table tbl_log(id int4, create_date date, log_type text);
    
    创建一张子表tbl_log_sql用于存储 SQL 日志
    create table tbl_log_sql(sql text) INHERITS(tbl_log);
    -- 通过INHERITS(tbl_log)关键字表示tbl_log_sql继承表tbl_log,子表可以定义额外的字段
    
    查看表结构
    d tbl_log_sql
    
    DML操作
    • 父表和子表分别插入记录
    INSERT INTO tbl_log VALUES (1,'2021-10-10', null);
    INSERT INTO tbl_log_sql VALUES (2,'2021-10-10', null,'select 1;');
    
    • 查询

      select * from tbl_log; -- 查询父表 tbl_log 会显示两条的记录,但子表的字段不会显示
      select * from tbl_log_sql;
      
      select p.relname, c.* 
        from tbl_log c, pg_class p
      where c.tableoid = p.oid;
      
      • 若只查询父表记录,需要在父表名称前加上ONLY关键字

        select * from only tbl_log;
        
    • 注意:对于使用了继承表的场景,对父表的update, delete的操作需谨慎,它会对父表和子表的数据进行DML操作

    创建分区表

    步骤
    1. 创建父表
    2. 通过 INHERITS 方式创建继承表,也称之为子表或分区
    3. 给子表创建约束
    4. 给子表创建索引,继承操作不会继承父表上的索引
    5. 在父表上定义 INSERT 、 DELETE 、 UPDATE 触发器,将 SQL 分发到对应分区(可选)
    6. 启用 constraint_exclusion 参数
    具体实现

    创建一张范围分区表,并且定义年月子表存储月数据

    创建父表
    CREATE TABLE log_ins (id serial,
    user_id int4,
    create_time timestamp(O) without time zone);
    
    创建子表
    CREATE TABLE log_ins_history(CHECK ( create_time < '2021-01-01')) INHERITS (log_ins);
    CREATE TABLE log_ins_202101(CHECK ( create_time >= '2021-01-01' and create_time < '2021-02-01')) INHERITS(log_ins);
    CREATE TABLE log_ins_202102(CHECK ( create_time >= '2021-02-01' and create_time < '2021-03-01')) INHERITS(log_ins);
    CREATE TABLE log_ins_202103(CHECK ( create_time >= '2021-03-01' and create_time < '2021-04-01')) INHERITS(log_ins);
    CREATE TABLE log_ins_202104(CHECK ( create_time >= '2021-04-01' and create_time < '2021-05-01')) INHERITS(log_ins);
    CREATE TABLE log_ins_202105(CHECK ( create_time >= '2021-05-01' and create_time < '2021-06-01')) INHERITS(log_ins);
    CREATE TABLE log_ins_202106(CHECK ( create_time >= '2021-06-01' and create_time < '2021-07-01')) INHERITS(log_ins);
    CREATE TABLE log_ins_202107(CHECK ( create_time >= '2021-07-01' and create_time < '2021-08-01')) INHERITS(log_ins);
    CREATE TABLE log_ins_202108(CHECK ( create_time >= '2021-08-01' and create_time < '2021-09-01')) INHERITS(log_ins);
    CREATE TABLE log_ins_202109(CHECK ( create_time >= '2021-09-01' and create_time < '2021-10-01')) INHERITS(log_ins);
    CREATE TABLE log_ins_202110(CHECK ( create_time >= '2021-10-01' and create_time < '2021-11-01')) INHERITS(log_ins);
    CREATE TABLE log_ins_202111(CHECK ( create_time >= '2021-11-01' and create_time < '2021-12-01')) INHERITS(log_ins);
    CREATE TABLE log_ins_202112(CHECK ( create_time >= '2021-12-01' and create_time < '2022-01-01')) INHERITS(log_ins);
    
    创建子表索引
    CREATE INDEX idx_his_ctime ON log_ins_history USING btree (create_time);
    CREATE INDEX idx_log_his_202101_ctime ON log_ins_202101 USING btree (create_time);
    CREATE INDEX idx_log_his_202102_ctime ON log_ins_202102 USING btree (create_time);
    CREATE INDEX idx_log_his_202103_ctime ON log_ins_202103 USING btree (create_time);
    CREATE INDEX idx_log_his_202104_ctime ON log_ins_202104 USING btree (create_time);
    CREATE INDEX idx_log_his_202105_ctime ON log_ins_202105 USING btree (create_time);
    CREATE INDEX idx_log_his_202106_ctime ON log_ins_202106 USING btree (create_time);
    CREATE INDEX idx_log_his_202107_ctime ON log_ins_202107 USING btree (create_time);
    CREATE INDEX idx_log_his_202108_ctime ON log_ins_202108 USING btree (create_time);
    CREATE INDEX idx_log_his_202109_ctime ON log_ins_202109 USING btree (create_time);
    CREATE INDEX idx_log_his_202110_ctime ON log_ins_202110 USING btree (create_time);
    CREATE INDEX idx_log_his_202111_ctime ON log_ins_202111 USING btree (create_time);
    CREATE INDEX idx_log_his_202112_ctime ON log_ins_202112 USING btree (create_time);
    
    创建触发器
    • insert
    -- 创建触发器函数,设置数据插入父表时的路由规则
    create or replace function log_hit_insert_trigger()
      returns trigger
      language plpgsql
    as $function$
    begin
      if (NEW.create_time < '2021-01-01') then
        insert into log_ins_history values (NEW.*);
      elsif (NEW.create_time >= '2021-01-01' and NEW.create_time < '2021-02-01') then
        insert into log_ins_202101 values (NEW.*);
      elsif (NEW.create_time >= '2021-02-01' and NEW.create_time < '2021-03-01') then
        insert into log_ins_202102 values (NEW.*);
      elsif (NEW.create_time >= '2021-03-01' and NEW.create_time < '2021-04-01') then
        insert into log_ins_202103 values (NEW.*);
      elsif (NEW.create_time >= '2021-04-01' and NEW.create_time < '2021-05-01') then
        insert into log_ins_202104 values (NEW.*);
      elsif (NEW.create_time >= '2021-05-01' and NEW.create_time < '2021-06-01') then
        insert into log_ins_202105 values (NEW.*);
      elsif (NEW.create_time >= '2021-06-01' and NEW.create_time < '2021-07-01') then
        insert into log_ins_202106 values (NEW.*);
      elsif (NEW.create_time >= '2021-07-01' and NEW.create_time < '2021-08-01') then
        insert into log_ins_202107 values (NEW.*);
      elsif (NEW.create_time >= '2021-08-01' and NEW.create_time < '2021-09-01') then
        insert into log_ins_202108 values (NEW.*);
      elsif (NEW.create_time >= '2021-09-01' and NEW.create_time < '2021-10-01') then
        insert into log_ins_202109 values (NEW.*);
      elsif (NEW.create_time >= '2021-10-01' and NEW.create_time < '2021-11-01') then
        insert into log_ins_202110 values (NEW.*);
      elsif (NEW.create_time >= '2021-11-01' and NEW.create_time < '2021-12-01') then
        insert into log_ins_202111 values (NEW.*);
      elsif (NEW.create_time >= '2021-12-01' and NEW.create_time < '2022-01-01') then
        insert into log_ins_202112 values (NEW.*);
      else
        raise exception 'create_time out of range.';
      end if;
      return NULL;
    END;
    $function$;
    
    -- 创建触发器
    CREATE TRIGGER insert_log_ins_trigger BEFORE INSERT ON log_ins FOR EACH ROW
    EXECUTE PROCEDURE log_hit_insert_trigger();
    
    • delete
    • update

    使用分区表

    插入数据

    往父表 log_ins 插入测试数据,并验证数据是否插入对应分区

    INSERT INTO log_ins(user_id,create_time)
    SELECT round(100000000*random()),generate_series('2020-12-01'::date,
    '2021-12-01'::date , '1 minute');
    
    查询数据
    SELECT count(*) FROM log_ins;
    SELECT count(*) FROM ONLY log_ins;
    
    SELECT * FROM ONLY log_ins limit 2;
    
    SELECT min(create_time),max(create_time) FROM log_ins_202101;
    -- 查看子表大小
    dt+ log_ins*
    
    EXPLAIN ANALYZE SELECT * FROM log_ins_202101 WHERE create_time > '2021-01-01' AND create time < '2021-02-01';
    

    constraint_exclusion 参数

    用来控制优化器是否根据表上的约束来优化查询,参数值:

    • on :所有表都通过约束优化查询
    • off:所有表都不通过约束优化查询
    • partition :只对继承表和 UNION ALL 子查询通过检索约束来优化查询

    如果设置成 on 或 partition ,查询父表时优化器会根据子表上的约束判断检索哪些子表,而不需要扫描所有子表,从而提升查询性能。

    -- 会话级别设置参数
    set constraint_exclusion = off;
    EXPLAIN ANALYZE SELECT * FROM log_ins_202101 WHERE create_time > '2021-01-01' AND create time < '2021-02-01';
    

    添加分区

    创建分区
    CREATE TABLE log_ins_202201(LIKE log_ins INCLUDING ALL);
    
    添加约束
    ALTER TABLE log_ins_202201 ADD CONSTRAINT log_ins_202201_create_time check
    CHECK ( create_time >= '2022-01-01' AND create_time < '2022-02-01');
    
    刷新触发器函数
    
    
    新分区 log_ins_202101 继承到父表 log_ins
    ALTER TABLE log_ins_202201 INHERIT log_ins;
    

    删除分区

    1. 直接删除分区方式
    DROP TABLE log_ins_202201;
    
    2. 先将分区的继承关系去掉后删除
    ALTER TABLE log_ins_202201 NO INHERIT log_ins;
    DROP TABLE log_ins_202201;
    

    查看分区信息

    d+ log_ins
    
    SELECT
      nmsp_parent.nspname as parent_schema,
      parent.relname as parent,
      nmsp_child.nspname as child_schema,
      child.relname as child
    from
      pg_inherits join pg_class parent on pg_inherits.inhparent = parent.oid 
      join pg_class child on pg_inherits.inhrelid = child.oid 
      join pg_namespace nmsp_parent on nmsp_parent.oid = parent.relnamespace 
      join pg_namespace nmsp_child on nmsp_clild.oid = child.relnamespace
    where parent.relname = 'log_ins';
    
    -- 分区表的分区数量
    select 
      nspname,
      relname,
      count(*) as partition_num
    from
      pg_class c,
      pg_namespace n,
      pg_inherits i
    where c.oid = i.inhparent
      and c.relnamespace = n.oid
      and c.relhassubclass
      and c.relkind in ('r','p')
    group by 1,2 
    order by partition_num desc;
    

    内置分区表

    PostgreSQL10 一个重量级新特性是支持内置分区表,用户不需要预先在父表上定义INSERT 、 DELETE 、 UPDATE 触发器,对父表的 DML 操作会自动路由到相应分区,相比传统分区表大幅度降低了维护成本,PostgreSQL10目前仅支持范围分区和列表分区。

    内置分区表实际与传统分区表一样,都是用继承的方式实现。

    语法

    创建分区
    
    

    创建内置分区表步骤

    • 创建父表,指定分区键和分区策略 。
    • 创建分区,创建分区时须指定分区表的父表和分区键的取值范围,注意分区键的范围不要有重叠,否则会报错 。
    • 在分区上创建相应索引,通常情况下分区键上的索引 是必须的,非分区键的索引可根据实际应用场景选择是否创建 。

    示例

    创建内置分区表过程
    -- 1. 创建范围分区表
    CREATE TABLE log_par (
    	id serial,
        user_id int4,
        create_time timestamp(0) without time zone
    ) PARTITION BY RANGE(create_time);
    
    -- 2. 创建分区
    create table log_par_his partition of log_par for values from ('1970-01-01') to ('2020-01-01');
    create table log_par_202001 partition of log_par for values from ('2020-01-01') to ('2020-02-01');
    create table log_par_202002 partition of log_par for values from ('2020-02-01') to ('2020-03-01');
    
    -- 3. 创建分区索引
    create index idx_log_par_his_ctime on log_par_his using btree(create_time);
    create index idx_log_par_202001_ctime on log_par_202001 using btree(create_time);
    create index idx_log_par_202002_ctime on log_par_202002 using btree(create_time);
    
    
    使用分区
    -- 1. 插入数据
    insert into log_par(user_id, create_time)
    select round(1000000*random()), generate_series('2019-12-01'::date, '2020-02-28'::date, '1 minute');
    
    -- 2. 查看数据
    select count(*) from log_par;
    select count(*) from only log_par;
    
    15:53:30 [local]:5432 dev@devdb=> dt+ log_par*
                                  List of relations
     Schema |      Name      |       Type        | Owner |  Size   | Description 
    --------+----------------+-------------------+-------+---------+-------------
     dev    | log_par        | partitioned table | dev   | 0 bytes | 
     dev    | log_par_202001 | table             | dev   | 1960 kB | 
     dev    | log_par_202002 | table             | dev   | 1712 kB | 
     dev    | log_par_his    | table             | dev   | 1960 kB | 
    (4 rows)
    
    15:53:30 [local]:5432 dev@devdb=>
    
    内置分区表与其分区的继承关系
    select 
        nmsp_parent.nspname as parent_schema, 
        parent.relname as parent,
        nmsp_child.nspname as child_schema,
        child.relname as child_schema
    from pg_inherits join pg_class parent
        on pg_inherits.inhparent = parent.oid join pg_class child
        on pg_inherits.inhrelid = child.oid join pg_namespace nmsp_parent
        on nmsp_parent.oid = parent.relnamespace join pg_namespace nmsp_child
        on nmsp_child.oid = child.relnamespace
    where 
        parent.relname = 'log_par';
        
     parent_schema | parent  | child_schema |  child_schema  
    ---------------+---------+--------------+----------------
     dev           | log_par | dev          | log_par_202001
     dev           | log_par | dev          | log_par_202002
     dev           | log_par | dev          | log_par_his
    (3 rows)
    
    

    添加分区

    添加分区
    CREATE TABLE log_par_202101 PARTITION OF log_par FOR VALUES FROM ('2021-01-01') TO ('2021-02-01');
    
    创建索引
    CREATE INDEX idx_log_par_202101_ctime ON log_par_202101 USING btree(create_time);
    

    删除分区

    1. 直接删分区方式
    DROP TABLE log_par_202101;
    
    2. 解绑分区方式
    -- 解绑分区,分区和数据仍然保留
    ALTER TABLE log_par DETACH PARTITION log_par_202101;
      
    -- 恢复分区
    ALTER TABLE log_par ATTACH PARTITION log_par_202101 FOR VALUES FROM ('2021-01-01') TO ('2021-02-01');
    
  • 相关阅读:
    hdu 5648 DZY Loves Math 组合数+深搜(子集法)
    hdu 5647 DZY Loves Connecting 树形DP
    hdu 4550 卡片游戏 贪心
    hdu 5646 DZY Loves Partition 二分+数学分析+递推
    hdu 2196 Computer 树形DP
    poj 2342 Anniversary party 树形DP入门
    Vijos P1003 等价表达式 随机数+单调栈
    【BZOJ】1044: [HAOI2008]木棍分割 二分+区间DP
    【BZOJ】1925: [Sdoi2010]地精部落 DP+滚动数组
    【BZOJ】1012: [JSOI2008]最大数maxnumber 树状数组求区间最值
  • 原文地址:https://www.cnblogs.com/binliubiao/p/15399595.html
Copyright © 2020-2023  润新知