首页 > 解决方案 > 当多个线程正在等待的锁定行被删除时,MySQL 会发生什么?

问题描述

我有一个要求,其中一个请求必须访问数据库中最顶层的行,读取它并删除它,通过锁定特定行一次性完成。

这里的用例是 URL 缩短应用程序将查询该表以获取预先生成的 URL 键并在完成后将其删除。

我正在使用 Spring Data JPA,所以我将添加以下代码

URLKeyEntity.java

@Entity(name = "urlKeyEntity")
@Table(name = "url_key")
@Data
public class URLKeyEntity {

 @Id
 @GeneratedValue(strategy = GenerationType.AUTO)
 private Long id;
 private String urlKey;
}

URLKeyRepository.java

@Repository
public interface URLKeyRepository extends JpaRepository<URLKeyEntity, Long> {

 @Lock(LockMode.PESSIMISTIC_WRITE)
 @Query("SELECT u FROM urlKeyEntity u ORDER BY u.id")
 List<URLKeyEntity> fetchTopRow(Pageable p);
}

URLKeyService.java

@Service
public class URLKeyService {

 @Autowired
 private URLKeyRepository urlKeyRepository;

 @Transactional
 public String getUrlKey() {
  final URLKeyEntity u = urlKeyRepository.findTopRow(PageRequest.of(0,1));
  final String key = u.get(0).getUrlKey();
  urlKeyRepository.delete(u.getId());
 }
}

我难以理解的是,当多个线程试图执行相同的方法时,这将如何发挥作用。

比如说,线程T1T2正在并行执行相同的方法。在任何给定点,T1 获取第一行 R1 上的锁,而 T2 等待同一行上的锁。

到 T1 完成时,行本身并不存在,那么 T2 将如何进行?

如果我们在整个表上获得锁,那么随着写入负载的增加,这可能会导致性能下降。

处理此问题的最佳方法是什么,以免锁定成为可扩展性的瓶颈?

标签: javamysqlspringmultithreadingspring-data-jpa

解决方案


完成后将其删除

你说的是毫秒后吗?还是几个小时后?锁定行“直到完成”最多几秒钟是可行的。对于更长的“锁”,您需要发明一种不同的机制。(如果需要,我们可以讨论。)

这是 InnoDB 如何锁定行,让你做一些工作,然后删除它:

START TRANSACTION;
SELECT id, ...
     ORDER BY ... DESC
     LIMIT 1
     FOR UPDATE;   -- 'lock' the last row
... do the work
DELETE ... WHERE id = ...;
COMMIT;

如果您需要打破该语句序列,请执行ROLLBACK.

如果您希望有多个线程分别处理该表中的某行,请这样说。我所介绍的基本上一次只能做一个“最后一行”;其他线程必须等待。

(如果您想讨论您的代码,请提供生成的 SQL;这是本站的通用语言。)


推荐阅读