Warning: file_get_contents(/data/phpspider/zhask/data//catemap/8/mysql/68.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
MySQL InnoDB在选择更新和插入之间发生死锁 背景:_Mysql_Innodb_Deadlock_Database Deadlocks - Fatal编程技术网

MySQL InnoDB在选择更新和插入之间发生死锁 背景:

MySQL InnoDB在选择更新和插入之间发生死锁 背景:,mysql,innodb,deadlock,database-deadlocks,Mysql,Innodb,Deadlock,Database Deadlocks,在MySQL 5.7.18中 我有一个名为“test”的表,定义如下: | test | CREATE TABLE `test` ( `id` varchar(255) NOT NULL, `name` varchar(255) DEFAULT NULL, PRIMARY KEY (`id`), KEY `test_name_x01` (`name`) ) ENGINE=InnoDB DEFAULT CHARSET=latin1 | 它只有一行: id | name 2 |

在MySQL 5.7.18中 我有一个名为“test”的表,定义如下:

| test  | CREATE TABLE `test` (
  `id` varchar(255) NOT NULL,
  `name` varchar(255) DEFAULT NULL,
  PRIMARY KEY (`id`),
  KEY `test_name_x01` (`name`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1 |
它只有一行:

id | name
2  | eva
现在,我在REPEATABLE-READ隔离级别启动两个转换,并执行以下命令:

  • T1:开始
  • T2:开始
  • T2:从name='eva'处的测试中选择*进行更新
  • T1:从name='eva'处的测试中选择*进行更新;(目前T1持有)
  • T2:插入测试值(1,‘eva’);(死锁,T1被回滚)
  • InnoDB日志: 我的想法 根据我的研究,以下是我认为可能导致僵局的原因。但需要专业的确认

    1. T1 : begin;
    2. T2 : begin;
    3. T2 : select * from test where name='eva' for update; 
    
    步骤3需要:IX锁,索引“name”上的下一个键锁(负无穷大,“eva”]

    4. T1 : select * from test where name='eva' for update;
    
    步骤4需要:IX锁,索引“name”上的下一个键锁(负无穷大,“eva”] IX锁与IX兼容,因此T1可以在不释放T2的情况下获取它。 下一个密钥锁实际上包含两个部分:记录X+gap,因为不同的事务可以在一个gap上持有冲突的锁,T1也会在不等待T2的情况下持有gap锁。所以T1只等待T2释放记录锁(在二级索引名称='eva'和聚集索引id='1'上)继续

    5. T2 : insert into test values (1, 'eva'); (dead lock, T1 is rolled back)
    
    步骤5:插入需要插入强度锁,这与间隙锁不兼容。因此T2等待T1释放其间隙锁。但同时T1等待T2释放记录X锁

    ========================================================================

    更新后,我发现了我上面的解释无法解释的更有趣的事实。 此外,对于步骤5,在尝试使用不同的ID值后,我有以下观察结果:

    如果该表预加载了多个具有不同ID和相同名称“eva”的行,只有在步骤5中尝试插入的ID值小于所有现有行的最小ID时,死锁才是可复制的。

    例如,使用

    id | name
    2  | eva
    4  | eva
    
    对于上面的步骤5

    insert (0, 'eva') => deadlock 
    insert (1, 'eva') => deadlock
    insert (3, 'eva') => NO deadlock
    insert (5, 'eva') => NO deadlock
    
    不要在
    VARCHARs
    中存储不带引号的数字。这会挫败任何使用索引的尝试

    更多

    一般来说

    当可以通过索引识别单行时,只有该行将被“锁定”。单行锁定通常通过“延迟”一个事务直到另一个事务释放其锁来避免死锁

    当必须扫描整个表时,可能需要锁定所有行。这可能不允许“延迟”,并导致死锁

    无论如何

    通过准备重播整个事务来计划死锁。有些死锁是不可避免的


    (很抱歉说得含糊不清;这里可能有太多的变体。请修复引号或索引;这样可以避免大多数死锁。)

    如果允许您插入0或1,主键中2之前的间隔将不再存在。如果插入0,它将变成2个间隔,一个在0之前,一个在2之前……如果插入1,它将变成一个不同的间隔,从下确界到1之前。我不这样认为。MySQL将进行转换。MySQL进行转换,但它向后执行--varchar到number。因此,每个
    id
    在查找时都会转换为一个数字,以便与查询中未引用的数字进行比较。因此,您不会从
    id
    上获得任何索引的好处。好的。但这与我面临的死锁问题有关吗?引用
    id
    后的问题完全相同>在
    insert
    语句中。@superluli-参见我的添加。
    insert (0, 'eva') => deadlock 
    insert (1, 'eva') => deadlock
    insert (3, 'eva') => NO deadlock
    insert (5, 'eva') => NO deadlock