此问题的环境是AWS RDS上的PostgreSQL 9.6.5。
问题是关于包含以下逻辑数据模型的3亿行的表的最佳模式设计和批量更新策略:
id
:主键,最多40个字符的字符串code
:整数1-999 year
:整年null
),开启(true
/ 1
)和关闭(false
/ 0
)。有可能以额外的更新(见下文)为代价,将标志视为一个简单的位(开或关,不存在)。 "在"值通常非常稀疏:< 1/1000。 查询通常涉及一个或多个标志(按名称)是否存在的布尔表达式,偶尔也会涉及code
和year
。
数据通过Apache Spark批量更新,即,更新可以表示为平面文件,例如,以COPY格式,或作为SQL操作。任何时候只有一个更新处于活动状态。 code
和year
的更新非常罕见。对标志的更新会影响每次更新1-5%的行(3-15百万行)。更新行可以包含所有标志及其值,只包括" on"要更新的标志或仅更改其值的标志。在前一种情况下,Spark需要查询数据以获取标志的当前值。
更新期间会有一个小的读取负载。
问题是关于支持查询的最佳模式和相关更新策略。如上所述进行更新。
迄今为止的一些研究评论:
使用1,000多个布尔列将创建一个非常有效的行表示,但除了一些DDL复杂性之外,还需要1,000多个索引。
如果有一种方法可以索引单个位,那么位串会很棒。而且,它们没有提供表示缺席标志的好方法。使用此方法需要在标志名称和位ID之间维护查找表。如果需要,合并更新可以与||
一起使用,但是,鉴于PostgreSQL的MVCC,仅更新标志而不是替换整行似乎没什么好处。
JSONB字段提供索引。他们还提供null
代表,但这需要付出代价:所有标志都是"关闭"需要明确设置,这将使字段非常大。如果我们忽略null
表示,JSONB字段将相对较小。为了进一步缩小它们,我们可以使用短的1-3字符字段名称和查找表。相同的评论:与位串合并。
tsvector
/ tsquery
:对此数据类型没有经验,但理论上,它似乎是一组" on"的完全代表。按名称标记。必须使用查找表将标志名称映射到令牌,并附加要求以确保不会因为词干而发生冲突。
答案 0 :(得分:1)
不要将标志存放在主表中。
假设主表名为data
,请定义如下内容:
CREATE TABLE flag_names (
id smallint PRIMARY KEY,
name text NOT NULL
);
CREATE TABLE flag (
flagname_id smallint NOT NULL REFERENCES flag_names(id),
data_id text NOT NULL REFERENCES data(id),
value boolean NOT NULL,
PRIMARY KEY (flagname_id, data_id)
);
如果创建了新标志,请在flag_names
中插入新行。
如果某个标志设置为TRUE
或FALSE
,请在flag
表格中插入或更新一行。
使用flag
加入data
以测试是否设置了某个标记。