REPEATABLE-READ隔离级别 && 间隙锁(GAP)

REPEATABLE-READ隔离级别 && 间隙锁(GAP)

表结构

create table t(
 name varchar(255) primary key,
 id int not null,
 key idx_id (id)
);
insert into t(name,id) values (‘a‘,15),
(‘b‘,10),(‘c‘,6),(‘d‘,10),(‘f‘,11),(‘zz‘,2);

Session A

mysql> begin;
Query OK, 0 rows affected (0.00 sec)

Session B

mysql> begin;
Query OK, 0 rows affected (0.00 sec)

Session A

mysql> select * from t;
+------+----+
| name | id |
+------+----+
| zz   |  2 |
| c    |  6 |
| b    | 10 |
| d    | 10 |
| f    | 11 |
| a    | 15 |
+------+----+
6 rows in set (0.00 sec)

mysql> select * from t where id = 10 for update;
+------+----+
| name | id |
+------+----+
| b    | 10 |
| d    | 10 |
+------+----+
2 rows in set (0.00 sec)

我们在Session A里执行当前读的select...for update操作,那这条sql语句时如何加锁的呢?看下图,

这幅图中有一个GAP锁,而且GAP锁看起来也不是加在记录上的,倒像是加载两条记录之间的位置,GAP锁有何用?

其实这个多出来的GAP锁,就是RR隔离级别,相对于RC隔离级别,不会出现幻读的关键。确实,GAP锁锁住的位置,也不是记录本身,而是两条记录之间的GAP。所谓幻读,就是同一个事务,连续做两次当前读 (例如:select * from t1 where id = 10 for update;),那么这两次当前读返回的是完全相同的记录 (记录数量一致,记录本身也一致),第二次的当前读,不会比第一次返回更多的记录 (幻象)。

如何保证两次当前读返回一致的记录,那就需要在第一次当前读与第二次当前读之间,其他的事务不会插入新的满足条件的记录并提交。为了实现这个功能,GAP锁应运而生。

如图中所示,有哪些位置可以插入新的满足条件的项 (id = 10),考虑到B+树索引的有序性,满足条件的项一定是连续存放的。记录[6,c]之前,不会插入id=10的记录;[6,c]与[10,b]间可以插入[10, aa];[10,b]与[10,d]间,可以插入新的[10,bb],[10,c]等;[10,d]与[11,f]间可以插入满足条件的[10,e],[10,z]等;而[11,f]之后也不会插入满足条件的记录。因此,为了保证[6,c]与[10,b]间,[10,b]与[10,d]间,[10,d]与[11,f]不会插入新的满足条件的记录,MySQL选择了用GAP锁,将这三个GAP给锁起来。

Insert操作,如insert [10,aa],首先会定位到[6,c]与[10,b]间,然后在插入前,会检查这个GAP是否已经被锁上,如果被锁上,则Insert不能插入记录。因此,通过第一遍的当前读,不仅将满足条件的记录锁上 (X锁),同时还是增加3把GAP锁,将可能插入满足条件记录的3个GAP给锁上,保证后续的Insert不能插入新的id=10的记录,也就杜绝了同一事务的第二次当前读,出现幻象的情况。

然后我们在Session B中做相应的Insert操作,验证一下上面的说法。

Session B

执行以下插入操作

mysql> select * from t;
+------+----+
| name | id |
+------+----+
| zz   |  2 |
| c    |  6 |
| b    | 10 |
| d    | 10 |
| f    | 11 |
| a    | 15 |
+------+----+
6 rows in set (0.00 sec)

mysql> insert t(name,id) values (‘aa‘,10);
ERROR 1205 (HY000): Lock wait timeout exceeded; try restarting transaction
mysql> insert t(name,id) values (‘bb‘,10);
ERROR 1205 (HY000): Lock wait timeout exceeded; try restarting transaction
mysql> insert t(name,id) values (‘e‘,11);
ERROR 1205 (HY000): Lock wait timeout exceeded; try restarting transaction
mysql>

可以看到我们执行了多次插入,都失败了,就是因为GAP上加了间隙锁的原因,导致插入不成功,也就防止了Session A第二次当前读的时候不会出现幻读。

当执行这条sql时insert t(name,id) values (‘bb‘,10)时,相应的锁的信息;

mysql> select * from INNODB_LOCKS;
+--------------+-------------+-----------+-----------+------------+------------+------------+-----------+----------+-----------+
| lock_id      | lock_trx_id | lock_mode | lock_type | lock_table | lock_index | lock_space | lock_page | lock_rec | lock_data |
+--------------+-------------+-----------+-----------+------------+------------+------------+-----------+----------+-----------+
| 11662:65:4:5 | 11662       | X,GAP     | RECORD    | `test`.`t` | idx_id     |         65 |         4 |        5 | 10, ‘d‘   |
| 11661:65:4:5 | 11661       | X         | RECORD    | `test`.`t` | idx_id     |         65 |         4 |        5 | 10, ‘d‘   |
+--------------+-------------+-----------+-----------+------------+------------+------------+-----------+----------+-----------+
2 rows in set (0.00 sec)

===========END===========

REPEATABLE-READ隔离级别 && 间隙锁(GAP)

时间: 2024-10-22 02:05:05

REPEATABLE-READ隔离级别 && 间隙锁(GAP)的相关文章

MySQL数据库引擎、事务隔离级别、锁

MySQL数据库引擎.事务隔离级别.锁 数据库引擎InnoDB和MyISAM有什么区别 大体区别为: MyISAM类型不支持事务处理等高级处理,而InnoDB类型支持.MyISAM类型的表强调的是性能,其执行效率比InnoDB类型更快,但是不支持事务,而InnoDB提供事务支持以及外键等高级数据库功能. 具体实现的区别: InnoDB不支持FULLTEXT类型的索引 InnoDB中不保存表的具体行数,也就是说,执行查询SQL时,InnoDB要扫描一遍整个表来计算有多少行,而MyISAM只要简单的

数据库事务、事务隔离级别以及锁机制详解

以下主要以MySQL(InnoDB引擎)数据库为讨论背景,纯属个人学习总结,不对的地方还请指出! 什么是事务? 事务是作为一个逻辑单元执行的一系列操作,要么一起成功,要么一起失败.一个逻辑工作单元必须有四个属性,称为 ACID(原子性.致性.隔离性和持久性)属性,只有这样才能成为一个事务. 数据库事物的四大特性(ACID): 1)原子性:(Atomicity) 务必须是原子工作单元:对于其数据修改,要么全都执行,要么全都不执行. 2)一致性:(Consistency) 事务在完成时,必须使所有的

mysql 隔离级别和锁相关

mysql数据库中存在三种锁,间隙锁(gap-lock) , 行锁(record-lock),gap-record-lock gap-record-lock 是间隙锁和行锁的组合, 间隙锁(gap lock) 行锁(record lock) record-lock: 测试表: CREATE TABLE `tt` ( `tid` int(11) NOT NULL DEFAULT '0', `tname` varchar(10) DEFAULT NULL, `tage` tinyint(4) DEF

关于不同隔离级别下锁机制的研究

众所周之,隔离级别分为:       Read Uncommited:可以读取提交的记录      ReadCommitted:仅读到提交的数据,会产生幻读现象     Repeatable Read :对读取到的数据加锁,并对读取的范围加锁,不存在幻读现象     Serializable:读加读锁写加写锁,串行执行   情况一:主键(where  主键=???) Read Committed隔离级别 show create table key_id; +--------+----------

MySQL事务及实现、隔离级别及锁与优化

事务  事务是应用程序中一系列严密的操作,所有操作必须成功完成,否则在每个操作中所作的所有更改都会被撤消.事务是逻辑上的一组操作,要么都执行,要么都不执行. ACID简介 原子性(Atomicity).一致性(Correspondence).隔离性(Isolation).持久性(Durability). (1)原子性:整个事务中的所有操作,要么全部完成,要么全部不完成,不可能停滞在中间某个环节.事务在执行过程中发生错误,会被回滚(Rollback)到事务开始前的状态,就像这个事务从来没有执行过一

MySql的隔离级别和锁的关系

一.事务的4个基本特征 Atomic(原子性): 事务中包含的操作被看做一个逻辑单元,这个逻辑单元中的操作要 么全部成功,要么全部失败. Consistency(一致性): 只有合法的数据可以被写入数据库,否则事务应该将其回滚到最初 状态. Isolation(隔离性): 事务允许多个用户对同一个数据进行并发访问,而不破坏数据的正 确性和完整性.同时,并行事务的修改必须与其他并行事务的修改 相互独立. Durability(持久性): 事务结束后,事务处理的结果必须能够得到固化. 二.数据库隔离

数据库事务隔离级别与锁

一.事务的4个基本特征     所谓事务是用户定义的一个数据库操作序列,这些操作要么全做要么全不做,是一个不可分割的工作单位.例如,在关系数据库中,一个事务可以是一条SQL语句.一组SQL语句或整个程序. 事务ACID特性. ACID就是:原子性(Atomicity ).一致性( Consistency ).隔离性或独立性( Isolation)和持久性(Durabilily). 事务和程序是两个概念.一般地讲,一个程序中包含多个事务.事务的开始与结束可以由用户显式控制.如果用户没有显式地定义事

数据库事务中的隔离级别和锁+spring Transactional注解

数据库事务中的隔离级别和锁 数据库事务在后端开发中占非常重要的地位,如何确保数据读取的正确性.安全性也是我们需要研究的问题. ACID 首先总结一下数据库事务正确执行的四个要素(ACID): 原子性(Atomicity):即事务是不可分割的最小工作单元,事务内的操作要么全做,要么全不做,不能只做一部分:一致性(Consistency):在事务执行前数据库的数据处于正确的状态,而事务执行完成后数据库的数据还是处于正确的状态,即数据完整性约束没有被破坏:比如我们做银行转账的相关业务,A转账给B,要求

mysql事务隔离级别与锁的关系

其实操作了这么久mysql一直也没有把mysql中事务跟锁的关系弄得特别清楚.然后搜到美团这篇文章,顺便结合一下自己遇到的问题总结一下. 首先事务有四种隔离级别: Reference: http://tech.meituan.com/innodb-lock.html Innodb中的事务隔离级别和锁的关系