Messenger数据库(第2部分):我们对“利润”进行划分

我们已经成功地设计了用于存储通信的PostgreSQL数据库结构,一年过去了,用户正在积极地填充它,现在它已拥有数百万条记录 ,并且...事情开始放缓。



事实是, 随着表容量的增加,索引的“深度”也增加了 -尽管是对数的。 但是随着时间的流逝,这迫使服务器要处理许多页面的数据以执行与开始时相同的读/写任务。

这就是分区的解救之道。

我注意到,这与分片无关,即与不同数据库或服务器之间的数据分配无关。 因为即使将数据分成多个服务器,也无法摆脱索引随时间“膨胀”的问题。 显然,如果您有能力每天调试一台新服务器,那么您的问题将不再只是特定数据库的问题。

我们将不考虑用于“在硬件中”实现分区的特定脚本,而是考虑方法本身-什么以及如何“切成片”,以及这种愿望会导致什么。

概念图


再次,我们定义我们的目标:我们要确保今天,明天和一年后,在任何读/写操作期间读取的PostgreSQL数据的数量保持大致相同。

对于任何按时间顺序累积的数据 (消息,文档,日志,档案等),作为分区键的自然选择是事件日期/时间 。 在我们的例子中,这种事件是消息发送时刻

请注意,用户几乎总是只使用“最新”此类数据-他们阅读最新消息,分析最新日志……不,当然,他们可以在时间上往回滚动,很少这样做。

从这些限制中,很明显, “每日”部分将是消息的最佳解决方案-毕竟,我们的用户几乎总是会“今天”或“昨天”阅读收到的消息。

如果我们白天几乎只写和读一个节,这将使我们更加有效地使用内存和磁盘 -因为与整个表的“大而粗”节不同,所有节索引都容易装入RAM。

循序渐进


总的来说,以上所有听起来都是一笔可观的利润。 这是可以实现的,但是为此,我们将不得不努力-因为对一个实体进行分区决定导致需要“切割”并与其关联

消息,其属性和预测


由于我们决定按日期剪切消息,因此根据实体的属性划分实体属性(附件文件,邮件列表)以及消息的日期也是合理的。

由于我们的典型任务之一是仅查看消息寄存器(未读,传入,全部),因此按消息日期将它们“吸引”到分区中也是合乎逻辑的。


将分区键(消息的日期)添加到所有表:收件人,文件,注册表。 您不能添加到消息本身,而是使用现有的DateTime。

主题


由于主题是多条消息,因此不可能在同一模型中“剪切”,因此必须依靠其他内容。 在我们的情况下, 对应的第一个消息日期是理想的-即主题本身创建的时刻。


将分区键(主题日期)添加到所有表:主题,参与者。

但是现在我们同时遇到两个问题:

  • 在哪个部分搜索有关该主题的帖子?
  • 在哪个部分中从消息中搜索主题?

您当然可以继续在所有部分中进行搜索,但这将非常可悲,并且会否定我们所有的收入。 因此,为了知道确切的位置,我们将逻辑链接/指向各节:

  • 在消息中,添加带有主题日期的字段
  • 为此主题添加一组消息日期 (您可以使用单独的表,也可以使用日期数组)



由于对每个单独信件的消息日期列表几乎没有修改(毕竟,几乎所有消息都在相邻的1-2天之内),因此我将继续介绍该选项。

总计,考虑到分区,我们的基础结构采用以下形式:

表:RU,如果您不喜欢西里尔字母,则最好不要查看表/字段的名称
--     CREATE TABLE "_YYYYMMDD"( "" uuid PRIMARY KEY , "" uuid , "" date , "" uuid , "" --    timestamp , "" text ); CREATE TABLE "_YYYYMMDD"( "" date , "" uuid , "" uuid , PRIMARY KEY("", "") ); CREATE TABLE "_YYYYMMDD"( "" date , "" uuid PRIMARY KEY , "" uuid , "BLOB" uuid , "" text ); CREATE TABLE "_YYYYMMDD"( "" date , "" uuid , "" smallint , "" timestamp , "" uuid , PRIMARY KEY("", "", "") ); CREATE INDEX ON "_YYYYMMDD"("", "", "" DESC); --     CREATE TABLE "_YYYYMMDD"( "" date , "" uuid PRIMARY KEY , "" uuid , "" text ); CREATE TABLE "_YYYYMMDD"( "" date , "" uuid , "" uuid , PRIMARY KEY("", "") ); CREATE TABLE "_YYYYMMDD"( "" date , "" uuid PRIMARY KEY , "" date ); 


节省一分钱


好吧,如果我们不是使用基于字段值分布的经典分区选项 (通过触发器和继承或PARTITION BY),而是在应用程序级别上``手动''使用,我们可以看到分区键的值已经存储在表本身的名称中。

因此,如果您非常担心存储的数据量 ,则可以摆脱这些“额外”字段并参考特定表。 的确,在这种情况下,来自多个部分的所有样本都必须提交给应用程序侧。

Source: https://habr.com/ru/post/zh-CN483170/


All Articles