减少 PostgreSQL 13 中大容量消息的真空开销和分区策略

问题描述 投票:0回答:1

我有一个用例,我需要处理包含为多个客户生成的消息的文件。这些消息需要存储在数据库中并转发给相应的客户。一旦收到确认,我将从数据库中清除相应的消息。如果没有收到确认,我会在一定时间间隔后重复重试,直到消息被确认。

此外,还有一个配置(我在应用程序启动期间加载的 customerConfig),其中包含基于客户和消息类型的消息保留期及其动态,这意味着客户可以随时更改这些值。例如,对于 customer1,计费消息的保留期为 30 天,而 customerSupport 消息的保留期为 15 天。对于 customer2 计费消息有 60 天的保留期,而 customerSupport 消息有 20 天的保留期。

CREATE TABLE CUSTOMER_MESSAGES (
    CUSTOMER_ID         VARCHAR(50)     NOT NULL,
    MESSAGE_ID          VARCHAR(50)     NOT NULL,
    CORRELATION_ID      VARCHAR(50)     NOT NULL,
    RETRY_COUNT         NUMERIC(10, 0)  NOT NULL,
    CRTE_TS             TIMESTAMP       NOT NULL,
    UPTS_TS             TIMESTAMP       NOT NULL,
    MESSAGE             VARCHAR,
    CONSTRAINT CUSTOMER_MESSAGES_PK PRIMARY KEY (CUSTOMER_ID, MESSAGE_ID)
) PARTITION BY HASH (CUSTOMER_ID);

CREATE TABLE CUSTOMER_MESSAGES_P0 PARTITION OF CUSTOMER_MESSAGES FOR VALUES WITH (MODULUS 10, REMAINDER 0);
CREATE TABLE CUSTOMER_MESSAGES_P1 PARTITION OF CUSTOMER_MESSAGES FOR VALUES WITH (MODULUS 10, REMAINDER 1);
CREATE TABLE CUSTOMER_MESSAGES_P2 PARTITION OF CUSTOMER_MESSAGES FOR VALUES WITH (MODULUS 10, REMAINDER 2);
...
CREATE TABLE CUSTOMER_MESSAGES_P9 PARTITION OF CUSTOMER_MESSAGES FOR VALUES WITH (MODULUS 10, REMAINDER 9);

消息确认配对是根据CORRELATION_ID完成的。

这种分区策略是在消息量较小(小于5万条)且只有20个客户的情况下选择的。现在,消息量已增长到2000万条左右,客户数量约为10000人。

当前表格设计的问题在于

  1. 它会产生大量“真空”或空白空间,从而导致性能问题。我们通常会收到 70% 的消息的确认,而 30% 的消息仍未确认。
  2. 一个分区最终会包含大量消息,因为消息分发是基于客户的。一位客户在给定时期内可能拥有 30-45% 的交易量。

我们还考虑按 CRTE_TS(而不是 CUSTOMER_ID)进行分区,以便我们可以在保留期后删除分区。然而,由于每个客户和每种消息类型的保留期各不相同,因此实施起来可能具有挑战性。

我正在使用 PostgreSQL 13,后端应用程序处于 Spring Boot 状态。

有人可以建议我应该采用哪种分区策略吗? (假设 PostgreSQL 允许按 RANGE、HASH 或 LIST 进行分区)您应该在哪一列上创建分区?

java database postgresql spring-boot database-design
1个回答
0
投票

由于消息只是临时存储,因此预计会出现大量“真空”。这就是您的用例的性质以及 Postgres 的工作方式。 (对于 Oracle 来说,情况会有所不同。)

使用当前的分区策略,您基本上是在尝试将负载平均分配到多个分区。这可能无法解决您的性能问题。由于分区由多个客户共享并继续接收新数据,因此在保留期后删除也无济于事。如果没有分区,您的系统很可能不会变慢。

没有关于您的数据的附加信息(有关消息被确认所需时间、消息重试次数等的统计数据)和性能问题(缓慢插入、缓慢更新、长时间运行的作业、锁争用等)。 ),很难提出更好的划分策略。但大多数成功的分区策略将热数据与冷数据分开。这样,热数据就可以很好地利用缓存,而冷数据则大部分留在缓存之外。

因此,一种可能的策略是按重试次数和/或创建数据(后者具有滚动分区)进行分隔。如果大多数消息都得到快速确认并且重试之间的时间增加,那么这将是有意义的。完成这项工作并不容易,因为您不能再在 CUSTOMER_ID 和 MESSAGE_ID 上拥有唯一的密钥。

但话又说回来,您的问题可能完全不同,更改分区策略可能没有帮助。您说消息确认是通过相关 ID 进行的,但您没有提到它的索引。也许更新表只是为了记录重试是问题所在(它将为每个更新的行创建一个新版本,从而为真空产生大量工作)。这可以用不同的方式解决。

© www.soinside.com 2019 - 2024. All rights reserved.