COPY 中的自动分区

来自 PostgreSQL 维基
跳转到导航跳转到搜索

历史

COPY 中的自动分区是 Aster Data 在 PostgreSQL 9.0 代码库上开发的一个提议功能。它已提交并经过审查 (1 2),但尚未被接受到核心产品中,无论是在该版本还是任何其他版本中。

架构

自动层次结构加载代码已集成到 Postgres 9.0 (当时仍编号为 8.5) 的 COPY 命令代码中。可以通过在 COPY 命令中添加一个选项来启用它,如下所示

COPY parent_table FROM file (PARTITIONING);

我们假设 COPY 是在父表上执行的,并且子约束定义正确 (没有重叠约束)。如果启用了 PARTITIONING 并且表有子表,我们将扫描子表列表,并检查每个子表元组是否使用 Postgres 内部 ExecRelCheck 方法满足约束 (我们只是修改了代码以导出该方法)。元组使用 heap_insert 移动到第一个匹配的子表中,索引将更新,并执行触发器。如果元组与任何子表约束不匹配,则会生成错误。

错误处理机制定义了在 COPY 期间如何处理错误 (有关更多详细信息,请参阅 COPY 中的错误日志记录 )。

详细设计

路由代码替换了 copy.c @ void CopyFrom(CopyState cstate) 中 COPY 命令的标准插入代码。它集成在错误日志记录的第二个 TRY/CATCH 块中,以处理会违反所有子表约束的元组。

静态 bool route_tuple_to_child(Relation parent_relation, HeapTuple tuple) 方法将元组路由到第一个满足其约束的子表中。我们扫描 InheritsRelationId 以查找子表,并使用 bool check_constraints_and_insert_tuple(Relation child_table_relation, HeapTuple tuple) 方法逐个尝试它们。约束使用 PostgreSQL 内部 ExecRelCheck 方法检查,如果满足约束,则返回 true。如果这是正确的子表,我们将执行 BEFORE ROW 触发器,使用 heap_insert 插入元组,更新索引并触发 AFTER ROW 触发器。

请注意,如果任何触发器修改了用于路由的键的值,元组将保留在错误的子表中,从而导致进一步访问时出现不可预测的行为。

作为优化,route_tuple_to_child 方法保留了最近使用过的子表 oid 的 LRU 排序列表。此缓存表会系统地在最前面尝试,如果连续的元组必须进入同一个子表 (这是一种可能的加载场景),这将防止任何子表查找。如果出现不匹配,将执行新的子表查找。

我们修改了 PostgreSQL 的 GUC 部分,以启用其他会话级参数。我们正在添加以下配置选项

   * copy_partitioning_cache_size: 0 is disable, any value greater than 0 defines how many child tables oids are kept in the LRU cache

假设、限制和错误输出

以下情况将生成错误

  • 子表没有定义任何约束
  • 元组不满足任何子表约束
  • 在执行路由后修改元组值的 ROW 和 STATEMENT 触发器会导致不可预测的错误。

如果子表有重叠,元组将插入找到的第一个子表中 (无论它是缓存表还是查找中出现的第一个表)。

示例和单元测试

这是一个用作回归测试的示例,展示了功能。我们首先创建一个具有 3 个子表的母表

	CREATE TABLE y2008 (
	  id     int not null,
	  date   date not null,
	  value  int
	);
	 
	CREATE TABLE jan2008 (
	    CHECK ( date >= DATE '2008-01-01' AND date < DATE '2008-02-01' )
	) INHERITS (y2008);
	
	CREATE TABLE feb2008 (
	    CHECK ( date >= DATE '2008-02-01' AND date < DATE '2008-03-01' )
	) INHERITS (y2008);
	
	CREATE TABLE mar2008 (
	    CHECK ( date >= DATE '2008-03-01' AND date < DATE '2008-04-01' )
	) INHERITS (y2008);

我们将以下数据 (每个子表 1 行) 复制到母表中

copy_input.data content:
11	'2008-01-10'	11
12	'2008-02-15'	12
13	'2008-03-15'	13
21	'2008-01-10'	11
31	'2008-01-10'	11
41	'2008-01-10'	11
22	'2008-02-15'	12
23	'2008-03-15'	13
32	'2008-02-15'	12
33	'2008-03-15'	13
42	'2008-02-15'	12
43	'2008-03-15'	13 

让我们尝试在未启用分区的条件下进行 COPY

COPY y2008 FROM '/root/workspace/Postgres-8.3.6-Aster/src/test/regress/data/copy_input.data';

所有行都插入母表中。

SELECT COUNT(*) FROM y2008;
 count 
-------
    12
(1 row)

SELECT COUNT(*) FROM jan2008;
 count 
-------
     0
(1 row)

SELECT COUNT(*) FROM feb2008;
 count 
-------
     0
(1 row)

SELECT COUNT(*) FROM mar2008;
 count 
-------
     0
(1 row)

DELETE FROM y2008;

我们现在启用自动层次结构加载,行将自动加载到相应的子表中。

	COPY y2008 FROM '/root/workspace/Postgres-8.5-Aster/src/test/regress/data/copy_input.data' (PARTITIONING);
	
SELECT * FROM y2008;
 id |    date    | value 
----+------------+-------
 11 | 01-10-2008 |    11
 21 | 01-10-2008 |    11
 31 | 01-10-2008 |    11
 41 | 01-10-2008 |    11
 12 | 02-15-2008 |    12
 22 | 02-15-2008 |    12
 32 | 02-15-2008 |    12
 42 | 02-15-2008 |    12
 13 | 03-15-2008 |    13
 23 | 03-15-2008 |    13
 33 | 03-15-2008 |    13
 43 | 03-15-2008 |    13
(12 rows) 

SELECT * FROM jan2008;
 id |    date    | value 
----+------------+-------
 11 | 01-10-2008 |    11
 21 | 01-10-2008 |    11
 31 | 01-10-2008 |    11
 41 | 01-10-2008 |    11
(4 rows)  

SELECT * FROM feb2008;
 id |    date    | value 
----+------------+-------
 12 | 02-15-2008 |    12
 22 | 02-15-2008 |    12
 32 | 02-15-2008 |    12
 42 | 02-15-2008 |    12
(4 rows)

SELECT * FROM mar2008;
 id |    date    | value 
----+------------+-------
 13 | 03-15-2008 |    13
 23 | 03-15-2008 |    13
 33 | 03-15-2008 |    13
 43 | 03-15-2008 |    13
(4 rows) 

可以使用以下方法调整缓存大小

set copy_partitioning_cache_size = 3;

现在重复 COPY 命令将更快

	COPY y2008 FROM '/root/workspace/Postgres-8.5-Aster/src/test/regress/data/copy_input.data' (PARTITIONING);

最后,我们删除表。

DROP TABLE y2008 CASCADE;
NOTICE:  drop cascades to table mar2008
NOTICE:  drop cascades to constraint mar2008_date_check on table mar2008
NOTICE:  drop cascades to table feb2008
NOTICE:  drop cascades to constraint feb2008_date_check on table feb2008
NOTICE:  drop cascades to table jan2008
NOTICE:  drop cascades to constraint jan2008_date_check on table jan2008