HGDB快速将所有原生分区转换为普通表

环境

系统平台:Linux x86-64 Red Hat Enterprise Linux 7
版本:5.6.5

文档用途

某些情况下由于业务预估过多,导致开发把所有表都建立成了分区表,而实际上并不需要分区。本文用于将原生分区表快速转换为普通表。

详细信息

背景

某些情况下由于业务预估过多,导致开发把所有表都建立成了分区表,而实际上并不需要分区。

  1. 在高并发下分区表可能引入一定的优化器消耗

  2. 分区多,会导致会话relcache的内存增加,长连接+高并发+未使用的huge page可能触发OOM

注意:本文不考虑采用serial导致sequence和table挂钩、外键约束,如果分区表包含serial字段,序列属于partition table的情况,删除old table会导致序列被删除,并且新表的默认值也会被清理。

例子

数据准备

create database db1;
\c db1



create table tbl(id int, info text) partition by hash(id);

-- MODULUS子句用于指定除数,REMAINDER子句用于指定哈希值被除后的余数。

-- REMAINDER 子句需要指定一个小于 MODULUS 子句的值。

-- 如果指定的分区数量少于 MODULES 子句的值,将会导致无法插入某些数据,因为没有用于存储这些值的分区。

create table tbl0 partition of tbl for values with (modulus 2, remainder 0);

create table tbl1 partition of tbl for values with (modulus 2, remainder 1);

insert into tbl select generate_series(1, 10000), md5(random()::text);



create table ttbl(id int, info text) partition by hash(id);

create table ttbl0 partition of ttbl for values with (modulus 2, remainder 0);

create table ttbl1 partition of ttbl for values with (modulus 2, remainder 1);

insert into ttbl select generate_series(1, 10000), md5(random()::text);



create table "TTb" (id int, info text) partition by hash (id);

create table "TTb0" partition of "TTb" for values with (modulus 2, remainder 0);

create table "TTb1" partition of "TTb" for values with (modulus 2, remainder 1);

insert into "TTb" select generate_series(1,10000), md5(random()::text);



create schema s1;

set search_path=s1;



create table tbl (id int, info text) partition by hash (id);

create table tbl0 partition of tbl for values with (modulus 2, remainder 0);

create table tbl1 partition of tbl for values with (modulus 2, remainder 1);

insert into tbl select generate_series(1,10000), md5(random()::text);



create table ttbl (id int, info text) partition by hash (id);

create table ttbl0 partition of ttbl for values with (modulus 2, remainder 0);

create table ttbl1 partition of ttbl for values with (modulus 2, remainder 1);

insert into ttbl select generate_series(1,10000), md5(random()::text);



create table "TTb" (id int, info text) partition by hash (id);

create table "TTb0" partition of "TTb" for values with (modulus 2, remainder 0);

create table "TTb1" partition of "TTb" for values with (modulus 2, remainder 1);

insert into "TTb" select generate_series(1,10000), md5(random()::text);



create schema s2;

set search_path=s2;



create table tbl (id int, info text) partition by hash (id);

create table tbl0 partition of tbl for values with (modulus 2, remainder 0);

create table tbl1 partition of tbl for values with (modulus 2, remainder 1);

insert into tbl select generate_series(1,10000), md5(random()::text);



create table ttbl (id int, info text) partition by hash (id);

create table ttbl0 partition of ttbl for values with (modulus 2, remainder 0);

create table ttbl1 partition of ttbl for values with (modulus 2, remainder 1);

insert into ttbl select generate_series(1,10000), md5(random()::text);



create table "TTb" (id int, info text) partition by hash (id);

create table "TTb0" partition of "TTb" for values with (modulus 2, remainder 0);

create table "TTb1" partition of "TTb" for values with (modulus 2, remainder 1);

insert into "TTb" select generate_series(1,10000), md5(random()::text);



db1=# \dPt *.*       List of partitioned tables Schema | Name |  Owner   | Parent name

--------+------+----------+------------- public | TTb  | highgo   | public | tbl  | highgo   | public | ttbl | highgo   | s1     | TTb  | highgo   | s1     | tbl  | highgo   | s1     | ttbl | highgo   | s2     | TTb  | highgo   | s2     | tbl  | highgo   | s2     | ttbl | highgo   | (9 rows)

  使用下面的方法来转换表:

begin;

create table new_tbl (like tbl including all);

insert into new_tbl select * from tbl;

alter table tbl rename to old_tbl;

alter table new_tbl rename to tbl;  -- 如果有依赖,这个表有被依赖的对象时,会导致drop失败

end;

  一次性转换所有分区表的操作如下:

-- 防止锁等待,等待直接回滚

-- 建议断开业务后连接操作

set lock_timeout='5s';



do language plpgsql $$

declare

   v_nsp name;    v_tbl name;    v_exchange1 name := 'new'||md5(random()::text);    v_exchange2 name := 'old'||md5(random()::text);begin    for v_nsp, v_tbl in        SELECT n.nspname as "Schema",            c.relname as "Name"
       FROM pg_catalog.pg_class c

           LEFT JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace

           LEFT JOIN pg_catalog.pg_inherits inh ON c.oid = inh.inhrelid

       WHERE c.relkind IN ('p','')

           AND n.nspname !~ '^pg_toast'

       ORDER BY "Schema", "Name"    LOOP        execute format('create table %I.%I (like %I.%I including all)', v_nsp, v_exchange1, v_nsp, v_tbl);        execute format('insert into %I.%I select * from %I.%I', v_nsp, v_exchange1, v_nsp, v_tbl);        execute format('alter table %I.%I rename to %I', v_nsp, v_tbl, v_exchange2);        execute format('alter table %I.%I rename to %I', v_nsp, v_exchange1, v_tbl);        -- 如果有依赖,以下语句将报错,导致整个事务回滚。        execute format('drop table %I.%I', v_nsp, v_exchange2);    END loop;  end;  
$$;



-- 再查询分区表

db1=# \dPt *.*     List of partitioned tables Schema | Name | Owner | Parent name

--------+------+-------+-------------

(0 rows)

如果分区表内的数据很多,这个可能会执行很久。

如果有依赖问题怎么办

先解决依赖,再执行如上

例如解决sequence的依赖问题,对每个分区表相关的sequence执行去除owner,改成新表:

alter sequecve 序列 owned by none;
以上do脚本改成如下:

do language plpgsql $$

declare

 v_tbl_oid oid;  v_nsp name;  v_tbl name;  v_exchange1 name := 'new'||md5(random()::text);  v_exchange2 name := 'old'||md5(random()::text);  v_seq_nsp name;
 v_seq_name name;

begin

   for v_tbl_oid,v_nsp,v_tbl in    SELECT c.oid, n.nspname as "Schema",        c.relname as "Name"    FROM pg_catalog.pg_class c        LEFT JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace        LEFT JOIN pg_catalog.pg_inherits inh ON c.oid = inh.inhrelid    WHERE c.relkind IN ('p','')        AND n.nspname !~ '^pg_toast'    ORDER BY "Schema",  "Name"    LOOP        execute format('create table %I.%I (like %I.%I including all)', v_nsp, v_exchange1, v_nsp, v_tbl);        execute format('insert into %I.%I select * from %I.%I', v_nsp, v_exchange1, v_nsp, v_tbl);        execute format('alter table %I.%I rename to %I', v_nsp, v_tbl, v_exchange2);        execute format('alter table %I.%I rename to %I', v_nsp, v_exchange1, v_tbl);        -- 如果有依赖,以下将报错,导致整个事务回滚.        -- 处理serial类型依赖, 设置owned by为none
       for v_seq_nsp, v_seq_name in

           select n.nspname as "Schema", c.relname as "Name" from pg_class c

           LEFT JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace            

           where c.oid in
           (select objid from pg_depend where refobjid=v_tbl_oid)

           and relkind='S'

           AND n.nspname !~ '^pg_toast'        loop            execute format('alter sequence %I.%I owned by none', v_seq_nsp, v_seq_name);
       end loop;

       execute format('drop table %I.%I', v_nsp, v_exchange2);    

   END loop;

end; $$;
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值