Database 支持数据库复制的数据结构设计
我正在开发一个小型的即席复制框架(针对一家reatil公司),它只复制特定表的特定记录(大约200条),具体取决于特定的域级逻辑 为了了解每个目标主机的每个记录的复制状态,我有一个repStatus字符(主机的数量)类型列;其中主机始终代表相同的位置 此列每个位置的值可以是0(无操作),1(复制记录),2(复制记录),3(确认后重新发送),A(第一次错误),B(第二次错误)。。。等等 例如:012A表示:Database 支持数据库复制的数据结构设计,database,postgresql,database-design,data-structures,database-replication,Database,Postgresql,Database Design,Data Structures,Database Replication,我正在开发一个小型的即席复制框架(针对一家reatil公司),它只复制特定表的特定记录(大约200条),具体取决于特定的域级逻辑 为了了解每个目标主机的每个记录的复制状态,我有一个repStatus字符(主机的数量)类型列;其中主机始终代表相同的位置 此列每个位置的值可以是0(无操作),1(复制记录),2(复制记录),3(确认后重新发送),A(第一次错误),B(第二次错误)。。。等等 例如:012A表示: 不要向主机1发送任何内容 将此记录发送到主机2 在主机3中正确接收记录 从主机3接收到错
- 不要向主机1发送任何内容
- 将此记录发送到主机2
- 在主机3中正确接收记录
- 从主机3接收到错误
CREATE TABLE repStatus (tableID int, recordID int, targetHostID int, status int);
CREATE TYPE replication_status AS ENUM (
'no_action',
'replicate_record',
'record_replicated',
'error_1',
'error_2',
'error_3'
);
ALTER TABLE t ADD COLUMN rep_status_array replication_status[];
Wherestatus值现在甚至可以标准化为一个新表。但是,在一个表中处理200个表*~500000条记录可能需要相当多的行
基于经验的任何选择都是受欢迎的。因此,您的典型查询是将所有记录复制到主机x。。。对于该特定目标具有repStatus1或3。(做出假设,因为这不是问题所在。) 而要复制的记录是罕见的,因为通常大多数记录已经被复制了,对吗?(更多假设。) 表达式上的部分索引可能是一个非常快速的解决方案 如果只保留向每行添加文本字符串的设计,则可以沿以下行为每个目标创建部分索引:
CREATE INDEX tbl_rep_part1_idx ON tbl (tbl_id, substr(repstatus,1,1))
WHERE substr(repstatus,1,1) = '1' OR
substr(repstatus,1,1) = '3';
CREATE INDEX tbl_rep_part2_idx ON tbl (tbl_id, substr(repstatus,2,1))
WHERE substr(repstatus,2,1) OR
substr(repstatus,2,1);
...
由于每个索引的开销,所有部分索引的总和仅大于完整索引。
对表执行写操作时,只需更新受影响的部分索引
将使这些查询非常快速:
SELECT * FROM tbl WHERE substr(repstatus,1,1) = '1';
SELECT * FROM tbl WHERE substr(repstatus,1,1) = '1' OR
substr(repstatus,1,1) = '3';
在索引中添加的tbl_id
是可选的。我之所以添加它,是因为一个额外的4字节整数列可以利用因填充而丢失的空间(索引大小不会增长)。只有在你使用它的时候才包括它(或另一个小栏目)
与文本数组+GIN索引相比,期望得到什么?
整个想法只有在我的假设成立的情况下才适用。我之所以在文本数组+GIN索引上提出此路由,原因有三:
SELECT pg_column_size('{A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z}'::text[]) -- 232 byte
,pg_column_size('{A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z}'::"char"[]) -- 50 byte
,pg_column_size('ABCDEFGHIJKLMNOPQRSTUVWXYZ'::text); -- 27 byte
这与200 x 500k行有关。很多虽然部分索引的总和将略多于单个完整的GIN索引,但如果您希望覆盖整个表,如果我们只需要覆盖少数情况,则情况并非如此。无论哪种方式,每个查询所需的索引都会小得多。考虑到索引的大小,我不希望缓存索引。这就更加强调了这一点
因此,您典型的查询是将所有记录复制到主机x。。。对于该特定目标具有repStatus1或3。(做出假设,因为这不是问题所在。) 而要复制的记录是罕见的,因为通常大多数记录已经被复制了,对吗?(更多假设。) 表达式上的部分索引可能是一个非常快速的解决方案 如果只保留向每行添加文本字符串的设计,则可以沿以下行为每个目标创建部分索引:
CREATE INDEX tbl_rep_part1_idx ON tbl (tbl_id, substr(repstatus,1,1))
WHERE substr(repstatus,1,1) = '1' OR
substr(repstatus,1,1) = '3';
CREATE INDEX tbl_rep_part2_idx ON tbl (tbl_id, substr(repstatus,2,1))
WHERE substr(repstatus,2,1) OR
substr(repstatus,2,1);
...
由于每个索引的开销,所有部分索引的总和仅大于完整索引。
对表执行写操作时,只需更新受影响的部分索引
将使这些查询非常快速:
SELECT * FROM tbl WHERE substr(repstatus,1,1) = '1';
SELECT * FROM tbl WHERE substr(repstatus,1,1) = '1' OR
substr(repstatus,1,1) = '3';
在索引中添加的tbl_id
是可选的。我之所以添加它,是因为一个额外的4字节整数列可以利用因填充而丢失的空间(索引大小不会增长)。只有在你使用它的时候才包括它(或另一个小栏目)
与文本数组+GIN索引相比,期望得到什么?
整个想法只有在我的假设成立的情况下才适用。我之所以在文本数组+GIN索引上提出此路由,原因有三:
SELECT pg_column_size('{A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z}'::text[]) -- 232 byte
,pg_column_size('{A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z}'::"char"[]) -- 50 byte
,pg_column_size('ABCDEFGHIJKLMNOPQRSTUVWXYZ'::text); -- 27 byte
这与200 x 500k行有关。很多虽然部分索引的总和将略多于单个完整的GIN索引,但如果您希望覆盖整个表,如果我们只需要覆盖少数情况,则情况并非如此。无论哪种方式,每个查询所需的索引都会小得多。考虑到索引的大小,我不希望缓存索引。这就更加强调了这一点
嗯,我要做的第一件事是删除到处都是的讨厌的字符串解析,并用PostgreSQL本机类型替换它。要在每个记录上存储与当前解决方案类似的复制状态,请执行以下操作:
CREATE TABLE repStatus (tableID int, recordID int, targetHostID int, status int);
CREATE TYPE replication_status AS ENUM (
'no_action',
'replicate_record',
'record_replicated',
'error_1',
'error_2',
'error_3'
);
ALTER TABLE t ADD COLUMN rep_status_array replication_status[];
这会占用更多的存储空间--枚举值