PostgreSQL 分表
继承实现更灵活,可以直接在已有数据的表上实现,不用重新迁移。
-- https://www.postgresql.org/docs/current/ddl-partitioning.html
-- zh
-- http://postgres.cn/docs/11/ddl-partitioning.html
-- keyword: further redirect
-- 已有数据分表,因为主表不能有数据,所以需要先备份,创建分表和规则完毕后重新插入
-- 或者用新的表名,之后再分批读取插入
-- 创建主表
CREATE TABLE measurement (
city_id int not null,
logdate date not null,
peaktemp int,
unitsales int
) PARTITION BY RANGE (logdate);
-- 创建分表及规则
-- 还可以通过partition by 再次创建sub-partitioning,对插入measurement_y2006m02的数据再次重定向
-- CREATE TABLE measurement_y2006m02 PARTITION OF measurement
-- FOR VALUES FROM ('2006-02-01') TO ('2006-03-01')
-- PARTITION BY RANGE (peaktemp);
CREATE TABLE measurement_y2006m02 PARTITION OF measurement
FOR VALUES FROM ('2006-02-01') TO ('2006-03-01');
CREATE TABLE measurement_y2006m03 PARTITION OF measurement
FOR VALUES FROM ('2006-03-01') TO ('2006-04-01');
-- 创建索引,自动再每个分区上创建索引
CREATE INDEX ON measurement (logdate);
-- Ensure that the enable_partition_pruning configuration parameter is not disabled in postgresql.conf. If it is, queries will not be optimized as desired.
-- test
INSERT INTO measurement (city_id, logdate, peaktemp, unitsales) VALUES (1, '2008-2-1', 1, 1);
INSERT INTO measurement (city_id, logdate, peaktemp, unitsales) VALUES (1, '2006-2-1', 1, 1);
---- 维护 ----
-- 删除分表,可以快速删除百万数据,但是需要父表的 ACCESS EXCLUSIVE 锁
DROP TABLE measurement_y2006m02;
-- 更好的处理方式,将分表从主表中分离,以单独的形式存在
-- This allows further operations to be performed on the data before it is dropped. For example, this is often a useful time to back up the data using COPY, pg_dump, or similar tools. It might also be a useful time to aggregate data into smaller formats, perform other data manipulations, or run reports.
ALTER TABLE measurement DETACH PARTITION measurement_y2006m02;
-- 在新的自盘空间申明,一般是用于把重要数据放在可靠快速磁盘,将日志型等数据放于普通磁盘
-- CREATE TABLE measurement_y2008m02 PARTITION OF measurement
-- FOR VALUES FROM ('2008-02-01') TO ('2008-03-01')
-- TABLESPACE fasttablespace;
-- 分区表有下列限制:
--
-- 没有办法创建跨越所有分区的排除约束,只可能单个约束每个叶子分区。
--
-- 虽然在分区表上支持主键,但引用分区表的外键不受支持(但支持从分区表到某个其他表的外键引用)。
--
-- 当一个UPDATE导致一行从一个分区移动到另一个分区时,另一个并发的UPDATE或DELETE可能会产生一个串行化错误。假设会话1正在执行一个分区键上的UPDATE,同时一个并发的能看见这个行的会话2执行了对该行的UPDATE或者DELETE操作。在这种情况下,会话2的UPDATE或者DELETE会检测到行的移动,并抛出一个串行化的错误(将总是会返回一个SQLSTATE '40001')。 如果发生这种情况,应用程序可能希望重试该事务。 在没有分区表或没有行移动的通常情况下, 会话2将识别新更新的行并在新行上执行UPDATE/DELETE。
--
-- 如果必要,必须在个体分区上定义BEFORE ROW触发器,分区表上不需要。
--
-- 不允许在同一个分区树中混杂临时关系和持久关系。因此,如果分区表是持久的,则其分区也必须是持久的,反之亦然。在使用临时关系时,分区数的所有成员都必须来自于同一个会话。
-- 使用继承实现
-- 虽然内建的声明式分区适合于大部分常见的用例,但还是有一些场景需要更加灵活的方法。分区可以使用表继承来实现,这能够带来一些声明式分区不支持的特性,例如:
--
-- 对声明式分区来说,分区必须具有和分区表正好相同的列集合,而在表继承中,子表可以有父表中没有出现过的额外列。
--
-- 表继承允许多继承。
--
-- 声明式分区仅支持范围、列表以及哈希分区,而表继承允许数据按照用户的选择来划分(不过注意,如果约束排除不能有效地剪枝子表,查询性能可能会很差)。
--
-- 在使用声明式分区时,一些操作比使用表继承时要求更长的持锁时间。例如,向分区表中增加分区或者从分区表移除分区要求在父表上取得一个ACCESS EXCLUSIVE锁,而在常规继承的情况下一个SHARE UPDATE EXCLUSIVE锁就足够了。
--继承实现可以在已有数据中实现吗?
create table measurement3 (
id int not null,
name char not null
);
insert into measurement3 (id, name) values (1, 'a');
insert into measurement3 (id, name) values (2, 'b');
insert into measurement3 (id, name) values (3, 'c');
insert into measurement3 (id, name) values (4, 'd');
create table measurement3_y1 (check ( id >= 10 and id < 20 )) inherits (measurement3);
create table measurement3_y2 (check ( id >= 20 and id < 30 )) inherits (measurement3);
create function measurement_insert_trigger() returns trigger
language plpgsql
as
$$
BEGIN
IF (NEW.id >= 10 and NEW.id < 20) then
INSERT INTO measurement3_y1 VALUES (NEW.*);
ELSIF (NEW.id >= 20 and NEW.id < 30) then
INSERT INTO measurement3_y2 VALUES (NEW.*);
ELSE
RAISE EXCEPTION 'id out of range. Fix the measurement_insert_trigger function!';
END IF;
RETURN NULL;
END;
$$;
alter function measurement_insert_trigger() owner to develop;
CREATE TRIGGER insert_measurement_trigger
BEFORE INSERT ON measurement3
FOR EACH ROW EXECUTE FUNCTION measurement_insert_trigger();
insert into measurement3 (id, name)
values (100, 'd');