MySQL中InnoDB的间隙锁问题

作者:goldensun 时间:2024-01-25 11:13:58 

 在为一个客户排除死锁问题时我遇到了一个有趣的包括InnoDB间隙锁的情形。对于一个WHERE子句不匹配任何行的非插入的写操作中,我预期事务应该不会有锁,但我错了。让我们看一下这张表及示例UPDATE。
 


mysql> SHOW CREATE TABLE preferences \G
*************************** 1. row ***************************
   Table: preferences
Create Table: CREATE TABLE `preferences` (
`numericId` int(10) unsigned NOT NULL,
`receiveNotifications` tinyint(1) DEFAULT NULL,
PRIMARY KEY (`numericId`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1
1 row in set (0.00 sec)
mysql> BEGIN;
Query OK, 0 rows affected (0.00 sec)
mysql> SELECT COUNT(*) FROM preferences;
+----------+
| COUNT(*) |
+----------+
|    0 |
+----------+
1 row in set (0.01 sec)
mysql> UPDATE preferences SET receiveNotifications='1' WHERE numericId = '2';
Query OK, 0 rows affected (0.01 sec)
Rows matched: 0 Changed: 0 Warnings: 0

InnoDB状态显示这个UPDATE在主索引记录上持有了一个X锁:
 


---TRANSACTION 4A18101, ACTIVE 12 sec
2 lock struct(s), heap size 376, 1 row lock(s)
MySQL thread id 3, OS thread handle 0x7ff2200cd700, query id 35 localhost msandbox
Trx read view will not see trx with id >= 4A18102, sees < 4A18102
TABLE LOCK table `test`.`preferences` trx id 4A18101 lock mode IX
RECORD LOCKS space id 31766 page no 3 n bits 72 index `PRIMARY` of table `test`.`preferences` trx id 4A18101 lock_mode X


这是为什么呢,Heikki在其bug报告中做了解释,这很有意义,我知道修复起来很困难,但略带厌恶地我又希望它能被差异化处理。为完成这篇文章,让我证明下上面说到的死锁情况,下面中mysql1是第一个会话,mysql2是另一个,查询的顺序如下:
 


mysql1> BEGIN;
Query OK, 0 rows affected (0.00 sec)
mysql1> UPDATE preferences SET receiveNotifications='1' WHERE numericId = '1';
Query OK, 0 rows affected (0.00 sec)
Rows matched: 0 Changed: 0 Warnings: 0
mysql2> BEGIN;
Query OK, 0 rows affected (0.00 sec)
mysql2> UPDATE preferences SET receiveNotifications='1' WHERE numericId = '2';
Query OK, 0 rows affected (0.00 sec)
Rows matched: 0 Changed: 0 Warnings: 0
mysql1> INSERT INTO preferences (numericId, receiveNotifications) VALUES ('1', '1'); -- This one goes into LOCK WAIT
mysql2> INSERT INTO preferences (numericId, receiveNotifications) VALUES ('2', '1');
ERROR 1213 (40001): Deadlock found when trying to get lock; try restarting transaction

现在你看到导致死锁是多么的容易,因此一定要避免这种情况——如果来自于事务的INSERT部分导致非插入的写操作可能不匹配任何行的话,不要这样做,使用REPLACE INTO或使用READ-COMMITTED事务隔离。

标签:InnoDB,锁
0
投稿

猜你喜欢

  • asp实现树型结构

    2008-04-13 06:06:00
  • python中的不可变数据类型与可变数据类型详解

    2022-12-27 21:56:24
  • python数据库操作指南之PyMysql使用详解

    2023-01-02 04:04:17
  • Python tempfile模块学习笔记(临时文件)

    2022-05-27 02:32:08
  • PHP获取类私有属性的3种方法

    2023-11-20 10:32:16
  • 基于node打包可执行文件工具_Pkg使用心得分享

    2024-05-08 09:37:47
  • wxPython之解决闪烁的问题

    2022-05-12 13:21:30
  • python 中的列表解析和生成表达式

    2022-01-30 16:14:15
  • 使用Django开发简单接口实现文章增删改查

    2023-02-12 22:29:37
  • PHP的HTTP客户端Guzzle简单使用方法分析

    2023-07-16 05:58:56
  • pandas的resample重采样的使用

    2023-04-07 10:33:29
  • 原生javascript AJAX 三级联动的实现代码

    2024-04-18 10:00:46
  • 浅析Python中的多重继承

    2021-03-19 21:17:56
  • Python利用plotly绘制正二十面体详解

    2021-07-25 13:14:27
  • 深入解答关于Python的11道基本面试题

    2021-11-13 00:34:51
  • 调试一段PHP程序时遇到的三个问题

    2023-06-22 11:39:22
  • Linux下创建Postgresql数据库的方法步骤

    2024-01-29 09:29:19
  • python读取ini配置文件过程示范

    2023-07-02 01:18:36
  • Python结巴中文分词工具使用过程中遇到的问题及解决方法

    2023-04-28 18:23:28
  • python三引号如何输入

    2021-08-12 12:42:34
  • asp之家 网络编程 m.aspxhome.com