mysql 开发进阶篇系列 10 锁问题 (相同索引键值或同一行或间隙锁的冲突)
2018-07-30 14:20
831 查看
1.使用相同索引键值的冲突
由于mysql 的行锁是针对索引加的锁,不是针对记录加的锁,所以虽然是访问不同行的记录,但如果是使用相同的索引键,是会出现锁冲突的。设计时要注意
例如:city表city_id字段有索引,Cityname字段没有索引:
2.使用不同索引键值但是同一行的冲突
当表有多个索引时候,不同的事务可以使用不同的索引锁定不同的行,无论什么索引,innodb都会使用行锁来对数据加锁。
例如city表city_id字段有主键索引,CityCode字段有普通索引:
3. 创建了索引,但使用的是表锁
在前面章节说过,创建了索引但不走索引的情况,这种情况下innodb将使用表锁,而不是行锁,因些分析锁冲突时,还需检查sql的执行计划,以确认是否真正使用了索引。
4. 间隙锁(next-key锁) 并发下要重点考虑
当我们用范围条件而不是相等条件检索数据,并请求共享或排它锁时,innodb会给符合条件的已有数据记录的索引项加锁;对于键值在条件范围内但并不存在的记录就叫做"间隙锁" 比如city表数据分布如下:
如果查询使用如下sql
select * from city where city_id>100 for update;
这就是一个范围条件的检索, innodb不但会对符合条件的101的记录加锁,也会对city_id大于101(虽然记录并不存在)的"间隙"加锁。使用间隙锁的目的是为了防止幻读,以满足相关的隔离级别。关于幻读查看"sql 开发进阶篇系列 6 锁问题(事务与隔离级别介绍)"
很明显,在使用范围条件的检索记录时, 会阻塞符合条件范围内键值的并发插入,往往造成严重的锁等待。在实现业务中尽量使用相等条件来检索数据。还需注意如查使用相等条件检索的数据不存在时,也会加间隙锁。
为了防止幻读,mysql隔离级别必须是REPEATABLE-READ和Serializable。REPEATABLE-READ也是默认的隔离级别。
由于mysql 的行锁是针对索引加的锁,不是针对记录加的锁,所以虽然是访问不同行的记录,但如果是使用相同的索引键,是会出现锁冲突的。设计时要注意
例如:city表city_id字段有索引,Cityname字段没有索引:
会话1 | 会话2 |
SET autocommit=0; | SET autocommit=0; |
SELECT * FROM city WHERE city_id=14 AND Cityname='深圳' FOR UPDATE; city_id country_id cityname CityCode 14 2 深圳 001 | |
会话2与会话1访问的是不同的记录,但是因为使用了相同的索引值,所以需要等待锁 SELECT * FROM city WHERE city_id=14 AND Cityname='长沙' FOR UPDATE; 等待... |
当表有多个索引时候,不同的事务可以使用不同的索引锁定不同的行,无论什么索引,innodb都会使用行锁来对数据加锁。
例如city表city_id字段有主键索引,CityCode字段有普通索引:
会话1 | 会话2 |
SET autocommit=0; | SET autocommit=0; |
SELECT * FROM city WHERE city_id=14 FOR UPDATE; city_id country_id cityname CityCode 14 2 深圳 001 | |
该记录没有被索引,所以可以获得锁 SELECT * FROM city WHERE CityCode='002' FOR UPDATE; city_id country_id cityname CityCode 15 2 长沙 002 | |
由于该记录被会话1锁定,所以需要等待 SELECT * FROM city WHERE CityCode='001' FOR UPDATE; 等待... |
在前面章节说过,创建了索引但不走索引的情况,这种情况下innodb将使用表锁,而不是行锁,因些分析锁冲突时,还需检查sql的执行计划,以确认是否真正使用了索引。
4. 间隙锁(next-key锁) 并发下要重点考虑
当我们用范围条件而不是相等条件检索数据,并请求共享或排它锁时,innodb会给符合条件的已有数据记录的索引项加锁;对于键值在条件范围内但并不存在的记录就叫做"间隙锁" 比如city表数据分布如下:
如果查询使用如下sql
select * from city where city_id>100 for update;
这就是一个范围条件的检索, innodb不但会对符合条件的101的记录加锁,也会对city_id大于101(虽然记录并不存在)的"间隙"加锁。使用间隙锁的目的是为了防止幻读,以满足相关的隔离级别。关于幻读查看"sql 开发进阶篇系列 6 锁问题(事务与隔离级别介绍)"
很明显,在使用范围条件的检索记录时, 会阻塞符合条件范围内键值的并发插入,往往造成严重的锁等待。在实现业务中尽量使用相等条件来检索数据。还需注意如查使用相等条件检索的数据不存在时,也会加间隙锁。
为了防止幻读,mysql隔离级别必须是REPEATABLE-READ和Serializable。REPEATABLE-READ也是默认的隔离级别。
会话1 | 会话2 |
SELECT @@tx_isolation @@tx_isolation REPEATABLE-READ | SELECT @@tx_isolation @@tx_isolation REPEATABLE-READ |
SET autocommit=0; | SET autocommit=0; |
-- 当前会话对不存在的记录加 for update; SELECT * FROM city WHERE city_id=102 FOR UPDATE; | |
如果这里插入的值>=102就会出现阻塞 INSERT INTO city VALUES(200,2,'江门','005') 错误代码: 1205 Lock wait timeout exceeded; try restarting transaction | |
ROLLBACK; | |
INSERT INTO city VALUES(200,2,'江门','005') 共 1 行受到影响 |
相关文章推荐
- mysql 开发进阶篇系列 13 锁问题(关于表锁,死锁示例,锁等待设置)
- mysql 开发进阶篇系列 6 锁问题(事务与隔离级别介绍)
- mysql 开发进阶篇系列 14 锁问题(避免死锁,死锁查看分析)
- mysql 开发进阶篇系列 15 锁问题 (总结)
- mysql 开发进阶篇系列 12 锁问题(隔离级别下锁的差异)
- mysql 开发进阶篇系列 7 锁问题(innodb锁争用情况及锁模式)
- mysql 开发进阶篇系列 3 SQL 优化(索引使用方法)
- mysql 开发进阶篇系列 8 锁问题 (共享锁与排它锁演示)
- mysql 开发进阶篇系列 11 锁问题 (恢复和复制的需要,对锁机制的影响)
- mysql 开发进阶篇系列 9 锁问题 (Innodb 行锁实现方式)
- iOS 10 开发适配系列 之 权限Crash问题
- mysql 开发基础系列15 索引的设计和使用
- iOS 10 开发适配系列 之 权限Crash问题
- 关于Entity Framework更新的几种方式以及可能遇到的问题(附加类型“Model”的实体失败,因为相同类型的其他实体已具有相同的主键值)在使用 "Attach" 方法或者将实体的状态设置为 "Unchanged" 或 "Modified" 时如果图形中的任何实体具有冲突键值,则可能会发生上述行为
- mysql 开发进阶篇系列 5 SQL 优化(表优化)
- [绍棠] iOS 10 开发适配系列 之 权限Crash问题
- mysql 开发进阶篇系列 1 SQL优化(show status命令)
- [MySQL优化案例]系列 ― 典型性索引引发CPU负载飙升问题
- iOS 10 开发适配系列 之 权限Crash问题
- iOS 10 开发适配系列 之 权限Crash问题