好吧,我要做的第一件事就是将 icky 字符串解析放到任何地方,并将其替换为 PostgreSQL 本机类型。将复制状态存储在与当前解决方案类似的每条记录上:
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[];
这会花费您更多的存储空间——枚举值是 4 个字节而不是 1,并且数组有一些开销。但是,通过向数据库教授您的概念而不是隐藏它们,您可以编写如下内容:
-- find all records that need to be replicated to host 4
SELECT * FROM t WHERE rep_status_array[4] = 'replicate_record';
-- find all records that contain any error status
SELECT * FROM t WHERE rep_status_array &&
ARRAY['error_1', 'error_2', 'error_3']::replication_status[];
如果这对您的用例有帮助,您可以直接放置 GIN 索引rep_status_array
,但最好查看您的查询并专门为您使用的内容创建索引:
CREATE INDEX t_replication_host_4_key ON t ((rep_status_array[4]));
CREATE INDEX t_replication_error_key ON t (id)
WHERE rep_status_array && ARRAY['error_1', 'error_2', 'error_3']::replication_status[];
也就是说,给定 200 个表,我很想将其拆分为一个复制状态表——一行包含状态数组或每台主机一行,具体取决于其余复制逻辑的工作方式。我仍然会使用该枚举:
CREATE TABLE adhoc_replication (
record_id bigint not null,
table_oid oid not null,
host_id integer not null,
replication_status status not null default 'no_action',
primary key (record_id,table_oid,host_id)
);
PostgreSQL 在内部为每个表分配一个 OID (try SELECT *, tableoid FROM t LIMIT 1
),它是单个数据库系统中方便的稳定数字标识符。换句话说,如果表被删除并重新创建,它会发生变化(例如,如果您转储和恢复数据库,可能会发生这种情况),出于同样的原因,开发和生产之间很可能会有所不同。如果您希望在添加或重命名表时使用这些情况来换取中断,请使用枚举而不是 OID。
将单个表用于所有复制将允许您轻松地重用触发器和查询等,从而将大多数复制逻辑与其复制的数据解耦。它还允许您通过引用单个索引来查询所有原始表中给定主机的状态,这可能很重要。
至于表大小,PostgreSQL 绝对可以处理同一张表中的 1000 万行。如果您使用专用的复制相关表,则始终可以按主机分区。(按表分区对我来说毫无意义;它似乎比在每个上游行上存储复制状态更糟糕。)分区的方式或是否合适完全取决于您打算向数据库提出什么样的问题,以及基表上发生了什么样的活动。(分区意味着维护许多较小的 blob 而不是几个大的 blob,并且可能会访问许多较小的 blob 以执行单个操作。)实际上是选择您希望磁盘何时进行搜索的问题。