本文主要是针对《Mysql技术内幕:InnoDB 存储引擎》一书中第六章关于 InnoDB 存储引擎中锁的学习总结。mysql
锁是数据库系统区别于文件系统的一个关键特性,为了支持对共享资源的并发访问,提供数据的完整性和一致性,咱们必须为数据加锁,InnoDB 提供了一致性非锁定读、行级锁的支持。算法
锁类型 | S 锁 | X 锁 |
S 锁 | 兼容 | 不兼容 |
X 锁 | 兼容 | 不兼容 |
锁类型 | IS 锁 | IX 锁 |
表 S 锁 | 兼容 | 不兼容 |
表 X 锁 | 兼容 | 不兼容 |
在数据库的默认隔离级别为 REPEATABLE READ 下,Select 查询默认为一致性非锁定读,若是想要使用一致性锁定读,须要显示给 Select 查询加锁sql
Select ... FOR UPDATE - 给读取的行加上 X 锁
Select ... LOCK IN SHARE MODE - 给读取的行加上 S 锁
复制代码
CREATE TABLE `test` (
`id` int(1) NOT NULL AUTO_INCREMENT,
`name` varchar(8) NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=12;
复制代码
- 0:这是 MySQL5.1.22 版本以前自增加的实现方式,即经过表锁的 AUTO-INC Locking 方式
- 1:这是该参数的默认值。对于 simple inserts 该值会用互斥量去对内存中的计数器进行累加的操做,
对于 bulk inserts 仍是使用传统表锁的 AUTO-INC Locking方式
- 2:在这个模式下,对于全部的 insert-like 自增加的产生都是经过互斥量,
而不是经过 AUTO-INC Locking 的方式,显然这时性能最高的方式。
然而会带来必定的问题。由于并发插入的存在,在每次插入时,自增加的值可能不是连续的。
复制代码
- Record Lock(行锁):单个行记录上的锁,锁定单条索引记录。
- Gap Lock(间隙锁):锁定一个范围,但不包括记录自己
- Next-Key Lock(临键锁):是行锁和间隙锁的结合,锁定一个范围,而且锁定记录自己。
复制代码
- 此时全部在(1,10)区间内的记录行都会被锁住
SELECT * FROM table WHERE id BETWEN 1 AND 10 FOR UPDATE;
复制代码
- 执行如下 SQL 时,InnoDB 会获取 24 对应的临键锁 (10, 24],并获取下一个区间的间隙锁 (24, 32),因此最终在 (10, 32) 区间加上间隙锁
SELECT * FROM table WHERE age = 24 FOR UPDATE;
- 以后若是在其它事务中执行如下命令,则该命令会被阻塞:
INSERT INTO table(age, name) VALUES(26, 'Ezreal');
复制代码
方式一:将事务的隔离级别设置成 READ COMMITTED
方式二:将参数 innodb_locks_unsafe_for_binlog 设置为 1
复制代码
- 脏读是指在并发状况下,当前事务能够读到其它事务未提交到数据
- 脏读违反了数据库事务的隔离性要求
- InnoDB 中脏读的发生条件是事务的隔离级别为 READ UNCOMMITED
复制代码
- 不可重复读是指同一个事务内,屡次读取同一个数据集合结果不一致现象
- 不可重复读违反了数据库事务的一致性要求
- InnoDB 经过 next-key 算法来避免不可重复读现象
- 为了不不可重复读现象发生,须要将事务的隔离级别设置为 REPEATABLE READ 以上
复制代码
- 丢失更新是指一个事务的更新操做会被令一个事务的更新操做覆盖
- 在当前数据库任何隔离级别下,都不会致使数据库丢失更新现象发生
复制代码
- 阻塞主要是为了确保事务的并发可以正常有序的执行
- 经过参数 innodb_lock_wait_timeout 来设置事务的超时等待时间,默认是 50 s
- 经过参数 innodb_rollback_on_timeout 来设置事务超时是否进行回滚,默认不进行回滚
- 超时状况下默认不进行回滚,须要业务来捕获超时异常主动进行回滚,不然会致使数据的不一致性发生
复制代码
innoDB 解决死锁的两种方案:
- 事务超时回滚事务,这种方案的缺点是若是超时的事务占用的比重比较大,回滚很是耗时
- 采用 wait-for graphic(等待图) 来进行死锁检查,经过深度优先遍历算法主动检查死锁发生,释放回滚 undo 量最小的事务
复制代码