【问题标题】:Partitioning instead of thousand tables with identical structure分区而不是具有相同结构的千张表
【发布时间】:2021-04-08 17:28:02
【问题描述】:

我有遗留但相当大(~25Gb)的数据库,设计有问题。整个数据库中常用的“模式”归结为以下几点:

  • 将逻辑部分分成不同的表(journal_1、journal_2、journal_n)
  • 所有表都有一个唯一的 bigserial/autoincrement 字段(journal_id_seq_1、journal_id_seq_2、journal_id_seq_n)
  • 所有表都有一个或多个外键指向一个或多个引用表(日志表有 2 个外键,一组表具有另一个结构(log_1、log_2、log_n)仅引用一个)

我非常好奇(实际上接近于恐慌 :) 如果有大约 50,000 个这样的表会发生什么(现在“只是”大约 15k)。

我的想法是将所有内容(具有相同结构的表)放在一个具有通用名称(比如说日志)的大表中,添加一个带有 journal_id 的列(从后缀 journal_{1|2|3} 中提取)分区通过此列,显然为每个具有相同命名约定的表创建分区表。此外,需要将 bigserial 字段转换为常规 bigint,但我仍然需要为每个分区保留序列并在每次插入时手动调用 nextval。除了 seq_id 之外,还需要使用 journal_id 字段扩展主键。最后,我看到了分片的好处,当数据库变得庞大时,它可以应用于分区。

请分享您对此策略的看法,尤其是关于外键的看法。现在我们至少需要将 max_locks_per_transaction 限制为 512,否则 pg_dump 会失败 ERROR: out of shared memory HINT: You might need to increase max_locks_per_transaction. pg_dump: error: query was: LOCK TABLE。除了锁定噩梦,据我所知,Postgres 对每个数据库的关系有限制(总数很大但不是无限的)。我是否需要为每个分区表创建外键,或者分区表的一部分(某些行)将在插入、删除或更新时被锁定,因为所有分区都只是“存储”而不是真正的关系实体?

提前谢谢你。

【问题讨论】:

    标签: postgresql partitioning


    【解决方案1】:

    15K 桌 == 堵嘴!

    分区可能不会比多个表更好。

    除了极少数情况外,两者都不提供任何性能优势。

    让我们研究一下未来对分片的需求。仅此一点,就可以证明journal_nnn 的存在是合理的。在这种情况下,一些期刊将在一台机器上,一些在另一台机器上,等等。所有期刊都在使用中吗?还是它们中的大多数都是“旧的”并且没有真正使用过?

    PRIMARY KEY 可以是两个(或更多)列的组合。

    AUTO_INCREMENT 比手动创建“序列号”有一些优势。 (但是,问题没有足够的细节让我详细说明。)

    FOREIGN KEYs 是两件事:隐含的INDEX(有利于性能)和约束(有利于完整性)。在调试良好的应用程序中,完整性检查是不必要的开销。它们必须在分区和分片中被抛弃。

    【讨论】:

    • 200000个分区,查询计划会变得慢的不能忍受,每条SQL语句都需要非常多的锁和打开文件。这不会很好。
    • 感谢您的快速回复。因此,您推荐的解决方案 1)只需将具有相同结构的表合并为一个,通过旧的 seq_id 和新的 journal_id(从表名中提取)来区分 2)分区对于未来的分片是有意义的,但范围必须更宽(按年?例如)
    • 按年?这是否意味着大部分活动都在一个表/分区/分片中?
    • 我需要仔细检查数据“粒度”以选择合适的范围。我举了一个例子,只是为了了解要保持性能需要多大的分区。
    【解决方案2】:

    为什么对这么小的数据库使用分区?您的平均表大小不到 2MB,真的非常小。

    摆脱分区,您的问题就迎刃而解了。

    【讨论】:

    • 是的,这就是我首先要做的——合并具有相同结构的表,并且(为将来)留出分割分区的可能性,前提是它们足够大。我现在只有一个悬而未决的问题,我不确定。我应该如何更好地处理串行字段?计划是将(合并)值放入 bigint 字段中,但继续为每个日志/日志创建序列,并在插入时手动调用 nextval 取决于 journal_id。本例中的主键是复合的:journal_id(从表名中提取)和seq_id(每个journal_id的独立序列)。
    • 每张桌子我只需要一个大序列号,但前提是我需要序列号。我也会尝试让它尽可能简单,创建一个可以为所有(或至少为大多数)类型的期刊/日志服务的表。您的设置听起来恰恰相反,尽可能复杂。在这种情况下,维护变得困难,数据库变得缓慢等等。力求简单。
    • 谢谢@Frank。这正是我想要实现的目标 - 重新设计这一遗产并使其尽可能简单,但所有期刊都已经有了连续刊,我不能只在新(合并)表中制作一个新的和一个序列。跨度>
    【解决方案3】:

    拥有 50000 个表开始变得痛苦,像这样的小型数据库毫无意义。分区也是如此——毕竟,分区是带有副业的表。

    我会为每个对象类型定义一个表。

    关于自动生成的主键号:创建一个由旧主键和表号组成的组合主键(journal_id)。对于新条目,请使用初始化为高于所有表的现有最大值的序列。

    【讨论】:

    • 但是 bigserial 字段呢?这个想法是继续创建(但手动)序列并在插入时调用 nextval。
    • 为什么不bigserial(或者,更好的是,一个身份列)?
    • 因为每个日志表都有自己的 bigserial 并且这个自动递增的值在其他地方使用:(所以,我不能只为所有合并表创建一个新的全局序列。我猜你可能会问,因为由于性能,数千个序列也不是好方法?
    • 啊,我明白了。我在答案中添加了一个想法。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2010-11-27
    • 2018-09-27
    • 1970-01-01
    • 2023-03-15
    相关资源
    最近更新 更多