【问题标题】:Spring @Transactional + Isolation.REPEATABLE_READ for Rate LimitingSpring @Transactional + Isolation.REPEATABLE_READ 用于速率限制
【发布时间】:2018-01-13 10:47:13
【问题描述】:

我们正在尝试速率限制总数的方案。一个月内请求的 JSON 记录数达到 10000 个 API。 我们根据 client_id 和 Timestamp(这是主键)将记录的总数存储在表中。 根据请求,我们从该客户的表中获取该月带有时间戳的记录。 从这条记录中我们得到当前计数,然后将其增加为 no。请求中的当前记录并更新数据库。

使用Spring Transaction,伪代码如下

@Transactional(propagation=Propagation.REQUIRES_NEW, isolation=Isolation.REPEATABLE_READ)
public void updateLimitData(String clientId, currentRecordCount) {

//step 1
    startOfMonthTimestamp = getStartOfMonth();
    endOfMonthTimestamp = getEndOfMonth();

//step 2
    //read from DB
    latestLimitDetails = fetchFromDB(startOfMonthTimestamp, endOfMonthTimestamp, clientId);
    latestLimitDetails.count + currentRecordCount;

//step 3
    saveToDB(latestLimitDetails)

} 

我们希望确保在多个线程访问“updateLimitData()”方法的情况下,每个线程都会获取一个月内某个 clientId 的更新数据,并且不会错误地覆盖计数。 在上述场景中,如果多个线程访问方法“updateLimitData()”并到达“步骤 3”。第一个线程将更新 DB 中的“count”,然后第二个线程更新 DB 中可能没有最新计数的“count”。

我从 Isolation.REPEATABLE_READ 了解到,仅在“第 3 步”调用更新时,“写锁”被放置在行中(到那时其他线程将拥有陈旧的数据)。在多线程场景中,我如何确保始终线程从表中获取最新计数。
我想到的一个解决方案是同步这个块,但这在多服务器场景中效果不佳。

请提供解决方案。

【问题讨论】:

  • 如果我们使用不同级别的隔离,如“REPEATABLE_READ”和“SERIALIZABLE”,则仅在更新时放置锁定。是否选择语句也会导致行锁定。

标签: spring multithreading transactional transaction-isolation


【解决方案1】:

除非您在执行此操作时锁定表/行,否则事务不会帮助您(不要这样做,因为它会影响性能)。

您可以将其迁移到数据库,使用存储过程或函数调用在数据库中执行此增量。这将确保 ACID 和事务安全,因为这是内置在数据库中的。

我建议使用标准 Spring Actuator 执行此操作以生成 API 调用计数,但这意味着重写您的服务以使用执行器端点而不是数据库。您可以将其链接到您的网关/防火墙/负载平衡器,以在达到报价后拒绝访问 API。这意味着您的 API 端点是纯的,并且此逻辑已从您的 API 调用中删除。您开发者的所有新 API 都会自动获得此功能。

【讨论】:

    猜你喜欢
    • 2018-12-01
    • 2019-12-08
    • 2020-11-12
    • 2015-02-20
    • 1970-01-01
    • 2021-08-03
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多