在 MySQL 中同时分配资源

Assign resources concurrently in MySQL

我正在尝试实施一个市场来销售某些可替代物品,我希望将这些物品伪随机分配给每个用户。 假设我有一个项目集合 items = [1, 2, 3, 4, 5, 6, 7, 8, 9].

用户可以请求购买一定数量的物品。 POST /buy?count=N

在我的代码中,我获得了商品样本,我想将这些商品出售给用户 [1, 3, 4] = sample(items)

我目前正在做的是以下内容

我基本上是使用数据库(mysql)来保证操作的一致性。问题是当我对这个逻辑进行压力测试时,查询失败并显示 ER_LOCK_DEADLOCKER_LOCK_WAIT_TIMEOUT.

我尝试了一些事情:

我尝试对示例中的项目进行排序,认为这样可以防止死锁,但没有奏效。

本来想把这些操作都放到一个队列里,然后一个个处理,但是应用在aws上运行一个集群,所以有多个实例。

我认为将数据库隔离级别设置为 SERIALIZABLE 会降低性能,但确实可以解决问题,但事实并非如此。

我认为使用数据库来保证一致性是可行的方法,但我不知道如何。

这里是4个client的简单日志,我明白为什么client 2, 3, 4无法锁定资源(5和8被client 1锁定)但是为什么会出现僵局?为什么他们不能失败?

C1 AVAILABLE 9 [1, 2, 3, 4, 5, 6, 7, 8, 9] PURCHASING 3 [ 1, 5, 8 ]

C2 AVAILABLE 9 [1, 2, 3, 4, 5, 6, 7, 8, 9] PURCHASING 3 [ 5, 7, 9 ]

C3 AVAILABLE 9 [1, 2, 3, 4, 5, 6, 7, 8, 9] PURCHASING 3 [ 4, 5, 9 ]

C4 AVAILABLE 9 [1, 2, 3, 4, 5, 6, 7, 8, 9] PURCHASING 3 [ 2, 4, 8 ]

C2 FAILED for 3 [ 5, 7, 9 ] ER_LOCK_DEADLOCK: insert into `user_items` (`item_id`, `user_id`) values (5, 6) - ER_LOCK_DEADLOCK: Deadlock found when trying to get lock; try restarting transaction

C4 FAILED for 3 [ 2, 4, 8 ] ER_LOCK_DEADLOCK: insert into `user_items` (`item_id`, `user_id`) values (2, 6) - ER_LOCK_DEADLOCK: Deadlock found when trying to get lock; try restarting transaction

C3 FAILED for 3 [ 4, 5, 9 ] ER_LOCK_DEADLOCK: insert into `user_items` (`item_id`, `user_id`) values (4, 6) - ER_LOCK_DEADLOCK: Deadlock found when trying to get lock; try restarting transaction

C1 ASSIGNED SUCCESSFULLY 3 [ 1, 5, 8 ]

问题出在间隙锁上,我在其中执行插入的 table 在两列上都有唯一索引。为了防止重复并通常保持索引更新,数据库引擎锁定跨越多个值的整个区域。

发生的事情是并发查询将不同区域锁定在彼此之上,导致死锁。

我通过使用更新来获取项目的行锁来更改逻辑,如果更新成功,我可以继续插入,否则我只是中止事务。

通过这种方式,我要么在任何给定时间有一个查询 运行(如果分区导致将某些值分配给不同的查询,则只有一个会继续)或对不相交的数据集进行多个查询。