PostgreSQL 分表

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');

July 22, 2020 · 3 min · Peter