【问题标题】:Postgres: one table with many columns or several tables with fewer columns?Postgres:一张多列的表还是多列少的几张表?
【发布时间】:2011-06-20 04:42:57
【问题描述】:

我的问题与 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 的内部结构,所以我正在寻找建议和特别是最佳实践来估算权衡,而不必先做这项工作,然后确定是否工作是值得的。这种变化需要在重写已经高度优化的查询方面付出相当大的努力,所以我宁愿深入了解我可以期待什么结果。谢谢,M。

【问题讨论】:

    标签: sql database-design postgresql data-modeling


    【解决方案1】:

    更新更大的行需要一定的成本。

    一个公式可以帮助解决这个问题。如果你不拆分,你的成本是

    成本 = xU + yS

    地点:

    U = 整行的更新(表不拆分)

    S = 选择成本

    x,y = 动作计数

    那么,如果你把它分开,你就是想弄清楚这个:

    成本 = gU1 + hU2 + xS1 + yS2

    在哪里

    U1 = 更新更小的表(成本更低)

    U2 = 更新更大的表(成本更低)

    S1 = 从较小的表中选择

    S2 = 从更大的表中选择

    g,h,x,y = 单个动作发生的频率

    因此,如果 g >> h,将它们分开是值得的。特别是如果 x >> y 那么它真的很划算。

    编辑:在回应 cmets 时,我还要指出,如果数据库处于持续负载、没有不活动状态,这些成本将变得更加重要。相反,如果服务器没有经历持续的负载,它主要是不活动的,每秒只有 1 或 2 个 trx,长时间不活动(其中“长”= 几秒钟),那么,如果是我,我不会复杂化我的代码,因为性能优势不会显示为真正可衡量的东西。

    【讨论】:

    • 谢谢肯,这是我想要的方向。你会说 U1 和 U2(和 U)都与列的宽度成正比吗?你会说 (U1 + U2) == U 在一定的开销范围内 (U1 + U2
    • 而且由于两个表中的行之间存在 1:1 的关系,您会说连接成本 (U1+U2) 吗?
    • @Mayur,几年前我进行了大量的写入测试,发现 U1 和 U2 在持续负载下肯定与行大小成正比。关键点是持续负载,如果您的更新是“频繁”的,但“频繁”意味着一次/20 秒,并且数据库通常一次不活动几秒钟,那么拆分页面可能没有任何收获。
    • @Mayur,在第一个近似值中,是的,连接成本为 S1+S2。虽然我们有索引、缓存等,但操作的数量是计算成本的基础。
    【解决方案2】:

    与此有关的 Postgresql 实现细节之一是它从不“更新”存储在磁盘上的行,它总是写入新版本。因此,在开始时将固定宽度的列放在一起并没有快速的胜利,就像在 Oracle 中一样,例如 (iirc)。

    确实,根据是否倾向于一起更新,将不同表中的列分组在一起可以产生更少的需要清理的垃圾。实验和测量结果是这里的关键。例如,如果您有一些经常更新的数据,您应该调查表上的“填充因子”设置。此设置使 PostgreSQL 在插入时在表页中保留一些空闲空间,允许将更新版本的行尽可能添加到与先前版本相同的页中:这可以减轻更新的负担,因为它可能意味着索引指向不必更新行,但代价是整个表会占用更多磁盘空间。

    正如 Xaade 所提到的,有很多关于这个主题的材料。我想强调一下我的评论,即需要衡量所做的任何更改的影响.. 有时看起来像是大获全胜的东西实际上并没有实现。

    【讨论】:

      【解决方案3】:

      无论列如何存储,都值得将其拆分。您在并发方面遇到的问题要少得多,加快对部分数据的查找速度,通过提供三个索引进行搜索来加快索引搜索速度,而不必创建这些辅助键,等等。

      您可以通过作弊或一次只允许查看这么多行来减轻内部联接的影响。 您可以通过提供界面而不是允许直接查找、仅在可见行上显示内部连接数据(一次只能在屏幕上查看这么多行)或通过显示当前选定行的附加数据或通过每次使用浏览按钮查找时只允许 X 行。如果您使用作弊,请确保缓存扩展查找的结果。

      【讨论】:

      • 谢谢。关于“作弊”的建议与我的应用程序并不真正相关,因为人类很少对这些表格进行排序。这一切都是为了机器,所以除非真的需要看到,否则已经不寻找列了。 (即 SELECT id, state, other_table_id2 WHEN...)有没有一种方法可以估计连接增加的成本,或者减少表大小的节省?
      • 查看数据库管理。这是一个完整的职业领域,因为要确定这些事情需要进行大量分析。您不仅要考虑估计解决方案的机会成本,还要考虑每个解决方案的机会成本。您必须估计数据何时更新,拆分表可能会减少冲突。您必须针对每种可能的组合进行计算,以找到最有效的解决方案。但是,当处理大量数据时,INNER JOIN 只是偶尔使用,并且表的某些部分可以独立更新,拆分总是有益的。
      猜你喜欢
      • 2012-04-04
      • 2021-07-31
      • 2012-11-28
      • 2016-12-24
      • 2011-03-14
      • 1970-01-01
      • 2020-03-25
      • 1970-01-01
      • 2021-12-22
      相关资源
      最近更新 更多