Warning: file_get_contents(/data/phpspider/zhask/data//catemap/4/postgresql/9.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Database 支持数据库复制的数据结构设计_Database_Postgresql_Database Design_Data Structures_Database Replication - Fatal编程技术网

Database 支持数据库复制的数据结构设计

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接收到错

我正在开发一个小型的即席复制框架(针对一家reatil公司),它只复制特定表的特定记录(大约200条),具体取决于特定的域级逻辑

为了了解每个目标主机的每个记录的复制状态,我有一个repStatus字符(主机的数量)类型列;其中主机始终代表相同的位置

此列每个位置的值可以是0(无操作),1(复制记录),2(复制记录),3(确认后重新发送),A(第一次错误),B(第二次错误)。。。等等

例如:012A表示:

  • 不要向主机1发送任何内容
  • 将此记录发送到主机2
  • 在主机3中正确接收记录
  • 从主机3接收到错误
这看起来非常简单,并且具有“直截了当的阅读”:为了了解记录的状态,我只需阅读repStatus

但是,这种方法似乎会导致错误

因此,我确信有更好的设计可以从性能角度解决这个问题。可能一个引用表、记录和主机的传统表是一个解决方案:

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。。。对于该特定目标具有repStatus13。(做出假设,因为这不是问题所在。)

而要复制的记录是罕见的,因为通常大多数记录已经被复制了,对吗?(更多假设。)

表达式上的部分索引可能是一个非常快速的解决方案

如果只保留向每行添加文本字符串的设计,则可以沿以下行为每个目标创建部分索引:

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索引,但如果您希望覆盖整个表,如果我们只需要覆盖少数情况,则情况并非如此。无论哪种方式,每个查询所需的索引都会小得多。考虑到索引的大小,我不希望缓存索引。这就更加强调了这一点

  • 更便宜的写操作。我希望对于小部分索引而言,简单、小的b树索引的更新速度会大大加快,因为GIN在这方面的麻烦是众所周知的。不过,这必须得到验证


  • 因此,您典型的查询是将所有记录复制到主机x。。。对于该特定目标具有repStatus13。(做出假设,因为这不是问题所在。)

    而要复制的记录是罕见的,因为通常大多数记录已经被复制了,对吗?(更多假设。)

    表达式上的部分索引可能是一个非常快速的解决方案

    如果只保留向每行添加文本字符串的设计,则可以沿以下行为每个目标创建部分索引:

    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索引,但如果您希望覆盖整个表,如果我们只需要覆盖少数情况,则情况并非如此。无论哪种方式,每个查询所需的索引都会小得多。考虑到索引的大小,我不希望缓存索引。这就更加强调了这一点

  • 更便宜的写操作。我希望对于小部分索引而言,简单、小的b树索引的更新速度会大大加快,因为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[];
    
    这会占用更多的存储空间--枚举值