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