为什么我使用SELECT从mysql获得死锁。。。更新锁?

为什么我使用SELECT从mysql获得死锁。。。更新锁?,mysql,transactions,deadlock,Mysql,Transactions,Deadlock,我有两个线程,它们必须更新同一个表,但第一个线程使用主键锁定单个记录,第二个线程必须使用另一个索引锁定一组记录。锁是由选择。。。对于UPDATE steatment,我无法理解它们为何会陷入死锁 这是表格: CREATE TABLE `ingressi` ( `id` int(10) unsigned NOT NULL AUTO_INCREMENT, `evento` int(10) unsigned NOT NULL, `stato` int(10) unsigned NOT NULL,

我有两个线程,它们必须更新同一个表,但第一个线程使用主键锁定单个记录,第二个线程必须使用另一个索引锁定一组记录。锁是由选择。。。对于UPDATE steatment,我无法理解它们为何会陷入死锁

这是表格:

CREATE TABLE `ingressi` (
 `id` int(10) unsigned NOT NULL AUTO_INCREMENT,
 `evento` int(10) unsigned NOT NULL,
 `stato` int(10) unsigned NOT NULL,
 ....,
 ....,
 PRIMARY KEY (`id`),
  KEY `evento` (`evento`,`stato`) USING BTREE,
) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8
这是查询日志,请注意连接:

    43 Query    SET SESSION TRANSACTION ISOLATION LEVEL READ COMMITTED
    43 Query    set autocommit=0
    43 Query    SELECT stato FROM ingressi WHERE id=1 FOR UPDATE
    39 Query    SET SESSION TRANSACTION ISOLATION LEVEL READ COMMITTED
    39 Query    set autocommit=0
    39 Query    SELECT count(*) FROM ingressi WHERE evento=66 FOR UPDATE
    43 Query    UPDATE `ingressi` SET stato=0 WHERE id=1
    43 Query    COMMIT
就在最后一次查询之后,将抛出死锁错误

conn=39尝试获取锁时发现死锁;尝试重新启动 交易

43和39连接是使用连接池的SpringJava应用程序的JDBC连接


为什么第二个连接没有等待,而它处于死锁状态?

请详细说明您的用例。如果您有两个线程试图保护一个锁,您可以简单地同时触发update语句,并根据更新返回的行数相应地构建逻辑。需要更多信息才能进行评论。

当您使用辅助索引查找并锁定行时,MySQL将首先锁定辅助索引中的条目,然后锁定主键中相应的行。这两个步骤可能会导致死锁

让我们假设下一行:

+----+--------+-------+
| id | evento | stato |
+----+--------+-------+
|  1 |     66 |    10 |
+----+--------+-------+
您的第一个事务使用主键查找id=1的行,并在其上放置独占锁

您的第二个事务使用索引evento,strato查找evento=66的条目,在次索引中对该条目放置独占锁,然后尝试在主键的行中获取独占锁。由于它已被锁定,因此必须等待

您的第一个事务现在想要更新该行。它有一个专用锁,所以很好。但这一更新改变了strato。由于它是索引的,因此必须修改索引evento、strato中的相应条目,这需要一个独占锁。不幸的是,第二个事务有一个独占锁,因此第一个事务必须等待第二个事务

因为第二个事务已经在等待第一个事务,所以出现了死锁

那么如何预防呢

对于您的特定情况,您可以使用不同的辅助索引来查找您的行,例如KEY evento1 evento。如果MySQL使用此索引,并且为了确保这一点,您可以从ingressi FORCE index evento1中使用SELECT count*,其中evento=66用于更新,这应该可以防止这种特定的死锁:

第一个事务锁定主键id=1 第二个事务锁定二级索引evento1中的条目evento1=66,id=1 第二个事务尝试获取主键id=1的锁,该锁已锁定,因此等待 第一个事务更新行,需要在二级索引evento中的条目evento1=66、stato=10、id=1上获得锁。这一次,它工作了,因为它现在没有被锁定 第一个事务提交,释放主键id=1上的锁,第二个事务可以完成
如果这是一个合理的解决方案,你的具体情况将取决于,嗯,你的具体情况。例如,仅仅为了防止一年发生两次的僵局而添加一个你实际上不想要或不需要的索引,这可能是过火了。或者你可能有更多的情况,这些情况都略有不同,可能需要不同的方法。您可以在中找到一些附加的一般指南,例如..

是否可以添加“显示创建表”选项卡的结果?因此,我们不必猜测或假设表中有哪些索引。我刚刚用实际数据和查询编辑了这个问题,以便您了解如何设置索引。很抱歉,我没有考虑索引的重要性。不幸的是,这不适合。我必须为试图获得公共资源的并发请求提供服务。这是一个活动的票簿,在看了之后,我必须清点当前活动的票,以检查是否有新票的空间,以及其他许多检查。