前言
在学习PG表空间时,发现一个独特的现象,创建表时指定了自定义表空间,但是表的主键还是使用的默认表空间。
tbl_tbs表的表空间为mytbs,主键tbl_tbs_pkey为默认表空间pg_default,可以看到他们所处的物理目录是不同的。
主键的表空间不应该是跟随表吗?这是PG的BUG?
原理
查看官方文档,在创建表时,对于Primary key、unique、exclude这些ixconstraints可以使用USING INDEX TABLESPACE tablespace_name语法来指定表空间。不指定则选择默认表空间。
CREATE [ [ GLOBAL | LOCAL ] { TEMPORARY | TEMP } | UNLOGGED ] TABLE [ IF NOT EXISTS ] table_name ( [
{ column_name data_type [ STORAGE { PLAIN | EXTERNAL | EXTENDED | MAIN | DEFAULT } ] [ COMPRESSION compression_method ] [ COLLATE collation ] [ column_constraint [ ... ] ]
| table_constraint
| LIKE source_table [ like_option ... ] }
[, ... ]
] )
[ INHERITS ( parent_table [, ... ] ) ]
[ PARTITION BY { RANGE | LIST | HASH } ( { column_name | ( expression ) } [ COLLATE collation ] [ opclass ] [, ... ] ) ]
[ USING method ]
[ WITH ( storage_parameter [= value] [, ... ] ) | WITHOUT OIDS ]
[ ON COMMIT { PRESERVE ROWS | DELETE ROWS | DROP } ]
[ TABLESPACE tablespace_name ]
where column_constraint is:
[ CONSTRAINT constraint_name ]
{ NOT NULL |
NULL |
CHECK ( expression ) [ NO INHERIT ] |
DEFAULT default_expr |
GENERATED ALWAYS AS ( generation_expr ) STORED |
GENERATED { ALWAYS | BY DEFAULT } AS IDENTITY [ ( sequence_options ) ] |
UNIQUE [ NULLS [ NOT ] DISTINCT ] index_parameters |
PRIMARY KEY index_parameters |
REFERENCES reftable [ ( refcolumn ) ] [ MATCH FULL | MATCH PARTIAL | MATCH SIMPLE ]
[ ON DELETE referential_action ] [ ON UPDATE referential_action ] }
[ DEFERRABLE | NOT DEFERRABLE ] [ INITIALLY DEFERRED | INITIALLY IMMEDIATE ]
and table_constraint is:
[ CONSTRAINT constraint_name ]
{ CHECK ( expression ) [ NO INHERIT ] |
UNIQUE [ NULLS [ NOT ] DISTINCT ] ( column_name [, ... ] ) index_parameters |
PRIMARY KEY ( column_name [, ... ] ) index_parameters |
EXCLUDE [ USING index_method ] ( exclude_element WITH operator [, ... ] ) index_parameters [ WHERE ( predicate ) ] |
FOREIGN KEY ( column_name [, ... ] ) REFERENCES reftable [ ( refcolumn [, ... ] ) ]
[ MATCH FULL | MATCH PARTIAL | MATCH SIMPLE ] [ ON DELETE referential_action ] [ ON UPDATE referential_action ] }
[ DEFERRABLE | NOT DEFERRABLE ] [ INITIALLY DEFERRED | INITIALLY IMMEDIATE ]
index_parameters in UNIQUE, PRIMARY KEY, and EXCLUDE constraints are:
[ INCLUDE ( column_name [, ... ] ) ]
[ WITH ( storage_parameter [= value] [, ... ] ) ]
[ USING INDEX TABLESPACE tablespace_name ]
复制
经过验证,使用USING INDEX TABLESPACE的语法是可以指定和表相同的表空间的。
对于这个语法,没有什么异议,可以灵活指定ixconstraints的表空间。但是对于它的缺省值,不管表是否指定了表空间ixconstraints都放到默认表空间,这个不太合理吧。假如我将表定义到特定表空间,就是为了分散io,或者存储隔离,主键还在默认表空间,就达不到我们的预期。
分析
那么可以修改下默认逻辑,当未使用USING INDEX TABLESPACE语法指定ixconstraints的表空间时,默认跟随表的表空间。
首先我们要debug下create table的过程,看下整体逻辑确定下修改的位置。
在transformCreateStmt函数中,两个ixconstraints的tablespace为NULL
所以T_IndexStmt在DefineIndex时tablespace为NULL
因此可以在transformCreateStmt函数中,获取到ixconstraints信息后,copy指定的表空间。
方案
当建表语句中指定了表空间并创建ixconstraints,并且ixconstraint的表空间为NULL时(并没有使用USING INDEX TABLESPACE语法指定表空间)。遍历ixconstraints链表,将表空间赋值为表指定的表空间。
验证
1、建表时ixconstraints未指定表空间,默认跟随表的表空间,符合预期。
2、建表时USING INDEX TABLESPACE语句指定ixconstraints的表空间,也是符合预期。