首页
技术小册
AIGC
面试刷题
技术文章
MAGENTO
云计算
视频课程
源码下载
PDF书籍
「涨薪秘籍」
登录
注册
01 | 基础架构:一条SQL查询语句是如何执行的?
02 | 日志系统:一条SQL更新语句是如何执行的?
03 | 事务隔离:为什么你改了我还看不见?
04 | 深入浅出索引(上)
05 | 深入浅出索引(下)
06 | 全局锁和表锁 :给表加个字段怎么有这么多阻碍?
07 | 行锁功过:怎么减少行锁对性能的影响?
08 | 事务到底是隔离的还是不隔离的?
09 | 普通索引和唯一索引,应该怎么选择?
10 | MySQL为什么有时候会选错索引?
11 | 怎么给字符串字段加索引?
12 | 为什么我的MySQL会“抖”一下?
13 | 为什么表数据删掉一半,表文件大小不变?
14 | count(*)这么慢,我该怎么办?
15 | 答疑文章(一):日志和索引相关问题
16 | “order by”是怎么工作的?
17 | 如何正确地显示随机消息?
18 | 为什么这些SQL语句逻辑相同,性能却差异巨大?
19 | 为什么我只查一行的语句,也执行这么慢?
20 | 幻读是什么,幻读有什么问题?
21 | 为什么我只改一行的语句,锁这么多?
22 | MySQL有哪些“饮鸩止渴”提高性能的方法?
23 | MySQL是怎么保证数据不丢的?
24 | MySQL是怎么保证主备一致的?
25 | MySQL是怎么保证高可用的?
26 | 备库为什么会延迟好几个小时?
27 | 主库出问题了,从库怎么办?
28 | 读写分离有哪些坑?
29 | 如何判断一个数据库是不是出问题了?
30 | 答疑文章(二):用动态的观点看加锁
31 | 误删数据后除了跑路,还能怎么办?
32 | 为什么还有kill不掉的语句?
33 | 我查这么多数据,会不会把数据库内存打爆?
34 | 到底可不可以使用join?
35 | join语句怎么优化?
36 | 为什么临时表可以重名?
37 | 什么时候会使用内部临时表?
38 | 都说InnoDB好,那还要不要使用Memory引擎?
39 | 自增主键为什么不是连续的?
40 | insert语句的锁为什么这么多?
41 | 怎么最快地复制一张表?
42 | grant之后要跟着flush privileges吗?
43 | 要不要使用分区表?
44 | 答疑文章(三):说一说这些好问题
45 | 自增id用完怎么办?
当前位置:
首页>>
技术小册>>
MySQL 实战 45 讲
小册名称:MySQL 实战 45 讲
### 21 | 为什么我只改一行的语句,锁这么多? 在数据库管理系统中,尤其是像MySQL这样的关系型数据库,锁机制是保证数据完整性和并发控制的核心手段之一。然而,在日常的开发和维护过程中,开发者常常会遇到一种困惑:明明只是执行了一个看似简单的、仅针对单条记录修改的SQL语句,却意外地触发了远比预期要多的锁,导致性能下降或甚至死锁的发生。本章将深入探讨这一现象背后的原因,解析MySQL中的锁机制,以及如何优化以减少不必要的锁开销。 #### 一、MySQL锁的基本概念 在MySQL中,锁主要分为两大类:共享锁(Shared Locks)和排他锁(Exclusive Locks),以及根据作用范围的不同,可以细分为表级锁(Table-Level Locks)和行级锁(Row-Level Locks)。 - **共享锁(S锁)**:允许多个事务同时读取一个资源,但不允许对其进行修改。 - **排他锁(X锁)**:当一个事务对资源加上排他锁后,其他事务既不能读取也不能修改该资源,直到锁被释放。 - **表级锁**:锁定整个表,开销小但并发性能低,常见于MyISAM存储引擎。 - **行级锁**:仅锁定需要修改的行,并发性能高,但开销相对较大,是InnoDB存储引擎的默认锁策略。 #### 二、为何只改一行却锁得多? 当你执行一个看似只影响一行的SQL更新语句时,实际上可能触发了比预期更多的锁,这主要由以下几个因素导致: ##### 1. 索引的使用与未命中 - **索引使用**:如果SQL语句能够利用到有效的索引,那么MySQL将能够精确定位到需要修改的行,并只对这些行加锁。然而,如果索引设计不合理或查询条件未能充分利用索引,MySQL可能需要扫描更多的行来找到目标行,进而锁定更多的数据。 - **索引未命中**:当查询条件未能命中索引时(即进行全表扫描),MySQL会锁定整个表的所有行,直到事务结束,这自然会导致锁的范围远超出一行。 ##### 2. 锁的粒度与升级 - **行级锁升级为表级锁**:在某些情况下,如进行外键约束检查、触发器执行等操作时,InnoDB可能会将原本的行级锁升级为表级锁,以保证数据的一致性。这种锁升级会显著增加锁的范围。 - **间隙锁(Gap Locks)与临键锁(Next-Key Locks)**:InnoDB除了对实际存在的行加锁外,还会对索引间的间隙加锁(Gap Locks),以及将记录锁与间隙锁结合起来的临键锁(Next-Key Locks),以防止幻读。这意味着即使只修改了一行,也可能因为间隙锁的存在而影响到更多未实际修改的数据行。 ##### 3. 事务隔离级别 MySQL支持四种事务隔离级别:READ UNCOMMITTED、READ COMMITTED、REPEATABLE READ(InnoDB默认)和SERIALIZABLE。不同的隔离级别对锁的行为有不同的影响。 - **REPEATABLE READ**:在这个级别下,InnoDB会使用临键锁来避免幻读,这可能导致即使在单条记录更新时,也会锁定更多的数据。 - **SERIALIZABLE**:这是最高的隔离级别,它会使用表级锁来避免任何并发问题,自然会导致锁的范围最大。 ##### 4. 锁等待与死锁 - **锁等待**:当多个事务尝试以不同顺序锁定相同的资源时,可能会导致锁等待。即使每个事务本身只锁定少量资源,但由于等待其他事务释放锁,整体上看似锁定了更多资源。 - **死锁**:当两个或多个事务相互等待对方持有的锁时,会形成死锁。解决死锁通常需要数据库管理系统进行干预,如回滚其中一个事务,这可能导致性能下降和事务失败。 #### 三、优化策略 为了减少不必要的锁开销,可以从以下几个方面进行优化: 1. **优化索引**:确保查询条件能够充分利用索引,减少全表扫描的机会。 2. **合理设计事务**:尽量缩小事务的范围,避免在事务中执行不必要的操作,减少锁持有时间。 3. **调整事务隔离级别**:根据实际需求选择合适的隔离级别,避免过高的隔离级别带来的额外锁开销。 4. **使用乐观锁或悲观锁策略**:根据业务场景选择适合的锁策略,乐观锁通过版本号等方式减少数据库层面的锁竞争,悲观锁则通过数据库自身的锁机制保证数据一致性。 5. **监控与分析**:利用MySQL的性能监控工具(如`SHOW ENGINE INNODB STATUS`、`INFORMATION_SCHEMA`表等)分析锁竞争情况,定位瓶颈并进行优化。 #### 四、结论 在MySQL中,即使只是修改一行的SQL语句,也可能因为索引、锁粒度、事务隔离级别等多种因素导致锁的范围远超出一行。理解和优化这些因素对于提高数据库性能和避免并发问题至关重要。通过优化索引设计、合理控制事务范围、调整隔离级别以及使用合适的锁策略,我们可以有效减少不必要的锁开销,提升数据库的整体性能。
上一篇:
20 | 幻读是什么,幻读有什么问题?
下一篇:
22 | MySQL有哪些“饮鸩止渴”提高性能的方法?
该分类下的相关小册推荐:
MySQL从入门到精通(五)
细说MySQL(零基础到高级应用)
MySQL8.0入门与实践
SQL零基础到熟练应用(增删改查)
MySQL从入门到精通(四)
MySQL从入门到精通(一)
MySQL必知必会核心内容
MySQL必会核心问题
MySQL从入门到精通(二)
MySQL从入门到精通(三)