我的问题与Postgres的工作方式有关:
我有一张桌子:
CREATE TABLE A (
id SERIAL,
name VARCHAR(32),
type VARCHAR(32) NOT NULL,
priority SMALLINT NOT NULL,
x SMALLINT NOT NULL,
y SMALLINT NOT NULL,
start timestamp with time zone,
end timestamp with time zone,
state Astate NOT NULL,
other_table_id1 bigint REFERENCES W,
other_table_id2 bigint NOT NULL REFERENCES S,
PRIMARY KEY(id)
);
在other_table_id1,state和other_table_id2上附加索引。
该表非常大,并且在列上看到了很多更新:other_table_id1,state。开始和结束列的一些更新,但其余的是不可变的。 (Astate是列状态的枚举类型。)
我想知道将两个最常更新的列拆分为单独的表是否有意义。我希望获得的是性能,因为当我只是查找该信息或减少更新的重量时(因为?)读取和写入较短的行的成本较低。但是,当需要(偶尔)需要同时获取特定项目的所有数据时,我需要权衡联接成本。
有一次,我认为每列都是单独存储的。但后来,当我在某处读到减少表格一侧的列宽度确实会影响使用另一列查找数据时的性能时,我修改了我的想法(因为行存储在一起,所以整行行长度会更短)。所以我现在的印象是,一行的所有数据都物理存储在磁盘上;所以建议分割表听起来像是有帮助的。当我当前写4个字节来更新状态时,我是否相信我正在重写实际上永远不会改变的64字节文本(名称,类型)?
我对表格“规范化”并不熟悉Postgres的内部结构并不熟悉,所以我正在寻找建议和esp最佳实践来估算权衡,而不必先做工作,然后确定是否工作是值得的。这种改变需要花费大量精力来重写已经高度优化的查询,所以我宁愿深入了解我可以期待的结果。谢谢,M。
答案 0 :(得分:4)
更新较大的行有一定的成本。
公式可以帮助解决这个问题。如果您不拆分,则费用为
费用= xU + yS
其中:
U =整行的更新(表格未拆分)
S =选择的费用
x,y =行动计数
然后,如果你拆分它,你试图弄明白:
成本= gU1 + hU2 + xS1 + yS2
,其中
U1 =更小的表格(更低的成本)
U2 =更大的表格(更低的成本)
S1 =从较小的表中选择
S2 =从较大的表中选择
g,h,x,y =个别行为发生的频率
所以如果g>> h,打破它们是值得的。特别是如果x>>那么它确实付出了代价。
编辑:在回应评论时,我还要指出,如果数据库处于持续负载,没有不活动状态,这些成本将变得更加重要。相反,如果服务器没有经历持续负载,它几乎是非活动的,只有1或2 trx每秒,长时间的延伸(“长”=几秒)不活动,然后,如果是我,我不会复杂我的代码,因为性能优势不会成为真正可衡量的东西。
答案 1 :(得分:2)
Postgresql的一个实现细节与此有关,它永远不会“更新”存储在磁盘上的行,它总是编写新版本。因此,在开始时将固定宽度列放在一起就像没有快速获胜一样,例如(iirc)。
确实,根据列是否倾向于一起更新,将列分组在不同的表中可以产生更少的垃圾,这些垃圾必须被抽真空。实验和测量结果是关键。如果您有一些经常更新的数据,您应该调查表上的“fillfactor”设置。此设置使PostgreSQL在插入时在表页中留下一些空闲空间,允许在可能的情况下将更新版本的行添加到与先前版本相同的页面:这可以减轻更新的负担,因为它可能意味着索引指向不必更新行,代价是让表占用整个磁盘上的更多空间。
正如Xaade所提到的,有很多材料可以用于这个主题。我想强调一下我需要衡量所做出的任何改变的影响的评论。有时看起来似乎是一场大胜,结果却不是在实践中。
答案 2 :(得分:0)
无论列是如何存储的,都值得将其拆分。您可以减少并发问题,加快部分数据查找速度,通过提供三个索引来加速索引搜索,而无需创建这些辅助密钥,等等。
您可以通过作弊或仅允许一次查看这么多行来减少内部联接的影响。 您可以通过提供界面而不是允许直接查找,仅在可见行上显示内部联接数据(您一次只能在屏幕上查看这么多行),或通过显示当前所选行的其他数据,或通过每个查询只允许使用浏览按钮X行。如果您使用作弊,请确保缓存扩展查找的结果。