问题起因:
两条写sql,操作的记录没有任何冲突,但发生死锁
预备知识:
InnoDB行锁是通过给索引上的索引项加锁来实现的
创建测试表
CREATE TABLE `t1` (
`pk_id` INT(11) NOT NULL,
`type` INT(11) NOT NULL,
`status` INT(11) NOT NULL,
PRIMARY KEY (`pk_id`)
);
create index idx_type on t1(type);
create index idx_status on t1(status);
生成测试数据
INSERT INTO t1 (pk_id,TYPE,STATUS)
VALUES
(1,1,0),
(2,1,0),
(3,1,0),
(4,2,0),
(5,2,0),
(6,1,1),
(7,1,1),
(8,2,1);
例1.不一样的锁等待
连接A执行
SET autocommit=0;
BEGIN;
SELECT * FROM t1 force index(PRIMARY) WHERE pk_id<4 AND TYPE=1 AND pk_id!=2 FOR UPDATE;
马上返回查到的结果有两条pk_id为1和3
连接B执行
SET autocommit=0;
BEGIN;
SELECT * FROM t1 WHERE pk_id=2 FOR UPDATE;
执行后连接B一直是等待状态,如果连接A commit,连接B马上就执行完成
说明:连接A虽然查出来的结果只有pk_id为1和3的两条记录,但把pk_id为2的PRIMARY索引记录也锁住了,所以连接B一直等待
换个索引试试
在连接A里
commit;
SELECT * FROM t1 force index(idx_type) WHERE pk_id<4 AND TYPE=1 AND pk_id!=2 FOR UPDATE;
注意只换了force index使用的索引,其他都没变
在连接B里想写操作TYPE=1的记录(pk_id为1、2、3、6、7)都等待,因为连接A把idx_type中TYPE=1的记录都锁了
和之前例子对照可以发现,索引锁是按使用的索引来操作,并且可以确定的是锁的范围会超出查询结果范围,这点和一般以为的不一样,具体算法还有待研究。
例2.死锁
连接A执行
COMMIT;
SET autocommit=0;
SELECT * FROM t1 WHERE pk_id<5 FOR UPDATE;
连接A先锁住了pk索引的部分记录
接着连接B执行
COMMIT;
SET autocommit=0;
SELECT * FROM t1 FORCE INDEX (idx_status) WHERE STATUS=0 FOR UPDATE;
连接B锁往了idx_status的部分记录,再要锁pk时被连接A block,所以只能等待
最后连接A执行
UPDATE t1 SET STATUS=6 WHERE pk_id<5;
这时连接B报dead lock found
简单来讲连接A先锁住pk,B先锁住idx_status再拿pk就拿不到,这时A再拿idx_status就死锁了
类似于一个人有X但要Y,一个人有Y但要X,互不相让,就死锁了。
例3.想不到的死锁
把例1和例2的情况结合起来,就会出来本文最开始碰到的问题,想不到的死锁,即更新的记录完全不冲突,但就是死锁了
比如
SELECT * FROM t1 force index(idx_type) WHERE pk_id<4 AND TYPE=1 FOR UPDATE;
和
update t1 set status=1 where pk_id=6
虽然想操作的记录不同,但锁的记录有相同的,所以也可能会死锁
例4.index merge死锁
如果sql where里同时使用了type和status,因为type和status上都有单字段索引,所以explain会发现使用了index merge
有的sql使用的索引是先idx_type再idx_status,有的先idx_status再idx_type
这样如果锁的记录有冲突,就可能和例3一样死锁了
解决方案:
1.只有一个pk,不要其他索引。这样只有lock wait,不会死锁
2.有多个index,但写数据时使用的都是同样的index组合
3.有多个index,按不同的index组合写数据,但逻辑上保证锁的记录不冲突
时间所限,只整理了大概的逻辑,一些细节未深入。有兴趣的可以看看mysql的next-key locking
相关推荐
这个SQL命令用于快速从文件中导入大量数据到MySQL表中。异常可能由以下原因引起: - 文件路径不正确或文件不存在。 - 文件格式不匹配,如字段分隔符、转义字符设置不正确。 - 权限问题:用户可能没有足够的权限...
虽然这可能是导致死锁的一个因素,但从理论上来讲,删除相同记录的事务不应该发生死锁。然而,实际发生的死锁表明,我们必须更深入地理解InnoDB的锁机制和事务处理。 接下来,我们要学习如何解读MySQL的死锁日志。...
在MySQL中,尤其是在InnoDB存储引擎下,当检测到死锁时,MySQL会自动选择回滚其中一个事务来解除死锁状态。 #### 二、死锁产生的原因 1. **循环等待**:多个事务之间存在循环的资源等待关系。 2. **互斥条件**:...
在MySQL的使用过程中,死锁是一个较为常见的现象,尤其是在并发量较大的应用场景下。死锁的发生往往会给系统带来不可预知的影响,严重时甚至会导致整个数据库服务不可用。对于MySQL/InnoDB数据库而言,了解死锁的...
MySQL数据库死锁是数据库系统中常见的问题,它发生在两个或多个事务在执行过程中,因争夺资源而造成的一种相互等待的现象,若无外力干涉它们将无法继续执行。了解和处理死锁是数据库管理员和开发人员必备的技能。...
通过以上分析,我们可以看到,"MySQL数据库CPU飙升及烂sql记录"是一个多方面的问题,需要从多个角度进行诊断和处理。在实际操作中,我们需要结合具体环境,运用适当的工具和技术,确保数据库的稳定高效运行。对于...
MySQL开发者SQL权威指南是一本专为MySQL开发人员设计的详细教程,旨在帮助读者深入理解和熟练掌握SQL语言在MySQL环境中的应用。SQL(Structured Query Language),结构化查询语言,是管理和处理关系数据库的标准...
根据提供的SQL脚本内容,我们可以看出该脚本主要用于查询MySQL数据库中出现死锁的情况,并获取相关信息,包括但不限于死锁的ID、导致死锁的具体SQL语句以及引发死锁的客户端等。通过这些信息,数据库管理员或开发...
MySQL数据库虽然没有内置的死锁日志,但在InnoDB存储引擎中,当发生死锁时,会自动检测并回滚其中一个事务,同时记录在"SHOW ENGINE INNODB STATUS"的输出中。通过对这些信息的分析,可以了解死锁的详细过程。 除了...
2. 使用死锁检测机制,当检测到死锁时,MySQL会回滚其中一个事务以打破死锁循环。 3. 优化SQL查询,避免全表扫描,减少间隙锁的使用。 4. 考虑使用更宽松的事务隔离级别,如可重复读(Repeatable Read)降级为读已...
本文将以一个具体的死锁案例为背景,深入分析MySQL中的死锁机制,探讨死锁的成因,并提出预防策略,旨在帮助读者清晰理解死锁问题的背景、阅读死锁日志的方法、深入剖析死锁的原因以及总结死锁问题。 首先,死锁...
《SQLMonitor2.4.3.6:高效监控Oracle与MYSQL数据库操作的得力助手》 在数据库管理领域,实时监控数据库的运行状态和SQL语句执行情况是至关重要的任务,这有助于确保系统的稳定性和性能优化。SQLMonitor2.4.3.6是一...
本案例中的死锁发生在“pop购药”系统的订单支付状态更新操作中,涉及到了两个事务,我们分别称之为Session1和Session2。 首先,让我们详细解析一下这两个事务的操作: **Transaction 1 (Session1)**: 执行的SQL...
- **Session2**:尝试插入id为23的新记录,但由于Session1已经锁定了id为22的行,所以Session2在尝试插入id为23的记录时触发了死锁。 #### 三、解决死锁的方法 针对MySQL中的死锁问题,可以采取以下几种策略来预防...
两个并发的事务分别尝试更新`Purchasing.Vendor`表中的不同行,但顺序相反,从而导致死锁。当死锁发生时,SQL Server将返回错误消息`1205`,指示事务已被选为死锁牺牲品,需要重新运行。 为了预防和减少死锁,可以...
MySQL中的死锁是指两个或多个事务在相互等待对方释放资源,导致它们都无法继续执行的情况。解决MySQL中的死锁问题通常涉及多个方面,包括监控死锁、分析死锁原因、优化事务设计以及调整系统配置等。以下是一些解决...
这个项目收集了一些常见的 MySQL 死锁案例,大多数案例都来源于网络,并对其进行分类汇总,试图通过死锁日志分析出每种死锁的原因,还原出死锁现场。 实际上,我们在定位死锁问题时,不仅应该对死锁日志进行分析,...
"Effective MySQL之SQL语句最优化"这本书深入探讨了如何通过优化SQL查询来提高MySQL数据库的效率。以下是基于这个主题的一些关键知识点: 1. **索引优化**:索引是数据库性能提升的核心。书中的内容可能涵盖如何...