本题环境为PostgreSQL 9.6.5 on AWS RDS。
问题是关于一个包含以下逻辑数据模型的 3 亿行表的最佳模式设计和批量更新策略:
id
:主键,最长40个字符的字符串code
:整数1-999year
: 整数年- flags:可变数量(1000+),每个都与一个名称相关联,随着时间的推移添加新的标志。理想情况下,应将标志视为具有三个值:不存在 (
null
)、on (true
/1
) 和 off (假
/0
)。以额外更新为代价(见下文),可以将标志视为一个简单的位(打开或关闭,不存在)。 “开”值通常非常稀疏:< 1/1000。
查询通常涉及关于是否存在一个或多个标志(按名称)的 bool 表达式,偶尔也会涉及 code
和 year
。
数据通过 Apache Spark 批量更新,即更新可以表示为平面文件,例如 COPY 格式,或表示为 SQL 操作。任何时候只有一个更新处于事件状态。 code
和 year
很少更新。标志更新每次更新影响 1-5% 的行(3-15 百万行)。更新行可能包含所有标志及其值,仅更新“on”标志或仅包含其值已更改的标志。在前一种情况下,Spark 需要查询数据以获取标志的当前值。
更新期间会有少量读取负载。
问题是关于支持所描述的查询和更新的最佳架构和相关更新策略。
目前研究的一些评论:
使用 1,000 多个 bool 列将创建非常高效的行表示,但除了一些 DDL 复杂性之外,还需要 1,000 多个索引。
如果有一种索引单个位的方法,位串会很棒。此外,它们没有提供表示缺失标志的好方法。使用这种方法需要在标志名称和位 ID 之间维护一个查找表。如果需要,合并更新可与
<||
一起使用,但考虑到 PostgreSQL 的 MVCC,与替换整行相比,仅更新标志似乎没有太大好处。JSONB 字段提供索引。它们还提供
null
表示,但这是有代价的:所有“关闭”的标志都需要显式设置,这会使字段变得非常大。如果我们忽略null
表示,JSONB 字段会相对较小。为了进一步缩小它们,我们可以使用带有查找表的 1-3 个字符的短字段名称。相同的评论:与位串合并。tsvector
/tsquery
:没有使用过这种数据类型的经验,但从理论上讲,它似乎是一组“on”标志的精确表示按名字。必须使用查找表将标记名称映射到 token ,并附加要求以确保不会因词干提取而发生冲突。
最佳答案
不要将标志存储在主表中。
假设主表名为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
来测试是否设置了某个标志。
关于postgresql - 对索引位集进行大量更新的最佳方法,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/47404915/