【问题标题】:MySQL query giving me incorrect or old dataMySQL 查询给了我不正确或旧的数据
【发布时间】:2021-04-09 14:25:37
【问题描述】:

我有一张表,其中的值是这样的,

id value status
1 a PENDING
2 b COMPLETE
3 c INPROGRESS
4 d PENDING
5 e PENDING

现在,我有两个线程正在尝试做同样的事情。它们如下。

  1. 从表中获取一条不是 INPROGRESS 或 COMPLETE 的记录。
  2. 将其标记为 INPROGRESS。
  3. 处理记录。
  4. 将其标记为 COMPLETE。

我已经实现了以下内容,以确保两个线程不会同时拾取相同的记录。

  1. “select * from table where status not in ('COMPLETE', 'INPROGRESS') limit 1 for update”从表中获取合适的记录。
  2. 在单个事务中完成第 1 步和第 2 步,以便表变为读锁定状态,直到拾取的记录被标记为 INPROGRESS。

但是,我面临以下问题
如果线程 1 正在获取记录,而它尚未将记录标记为“INPROGRESS”,则另一个线程(线程 2)尝试执行相同操作。线程 2 有时会首先获取线程 1 已经选择的记录,并在线程 1 事务解除锁定后返回状态为“INPROGRESS”的相同记录。

除了在获取记录后添加状态值检查外,我无法弄清楚如何解决此问题,但我认为可能有更好的方法。

如果有更好的方法,请告诉我。

这是负责获取要处理的记录的方法的示例代码,

// 存储库

class TableRepository implements JpaRepository<Table, Long> {
 @Query(value = "select * from table where status not in ('COMPLETE', 'INPROGRESS') limit 1 for update", nativeQuery=true)
 Table getOneValidRecord(); 
}

// 服务类

class TableService {

@Autowired
private TableRepository repository;

@Transactional(propagation = Propagation.REQUIRES_NEW)
public Table getOneValidRecord() {

Table table = repository.getOneValidRecord();
table.setStatus("INPROGRESS");
return repository.save(table);
}

}

【问题讨论】:

    标签: mysql multithreading spring-boot spring-data-jpa


    【解决方案1】:

    您必须使用中间保留状态。在 SQL 级别:

    第 1 步。 尝试预订。

    UPDATE table
    SET status = 'Thread 1 reserved'
    WHERE status = 'PENDING'
    ORDER BY id
    LIMIT 1
    

    第 2 步。检查预订是否成功。

    SELECT COUNT(*)
    FROM table
    WHERE status = 'Thread 1 reserved'
    

    如果输出为零,则线程 2 过度保留了同一行。转到步骤 1。 如果输出大于 1,则发生紧急情况。呼叫接线员。

    第 3 步。处理行。

    UPDATE table
    SET status = 'INPROGRESS'
    WHERE status = 'Thread 1 reserved'
    ORDER BY id
    LIMIT 1
    

    【讨论】:

    • 我明白了你的方法,但如果两个线程同时调用该方法,每次都不是问题吗?
    • @HindolDey 如果两个线程同时调用此代码,则一个线程 (A) 将覆盖另一个线程 (B) 设置的值。所以 A 将处理,而 B 将 如果输出为零,则线程 2 过度保留了同一行。转到步骤 1。
    • 感谢@Akina 的回答。但我认为这种方法的开销可能超出预期。
    • @HindolDey 但我认为这种方法的开销可能比预期的要多。 附加操作(确保该行保留给当前线程并且不会重新保留)产生额外的开销 - 这对你来说是不是很奇怪?
    • 这种方法可能有助于这个具体的例子。但我希望找到我在 MySQL 中错误地使用“for update”查询的位置或方式,以便获取旧数据。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-09-16
    • 2015-01-07
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2023-04-02
    相关资源
    最近更新 更多