说到数据库的隔离级别,我想大家都能说出一二,但是很多时候都是从网上看来的,很多都点到为止不够详细,并且没有经过实践的检验,所以有时候我们会发现有些东西并没有按照我们预期的来工作,这里就是一个例子。
MySQL目前流行的版本默认的事务隔离级别一般是可重复读,一般我们理解在这个隔离级别下,我们新建两个事务A和事务B,事务A的修改是不影响事务B的,也就是说A事务修改数据后,B事务读到的数据是不变的,也就是可重复读,同时在next-key锁的作用下,又解决了幻读。不过这里有一个问题就是B事务读到的数据是什么时候的数据?这句话隐藏着一个操作,就是在事务A提交前,事务B已经进行过一次查询,否则,事务B会读取最新的数据,因为事务是在begin后第一次select等操作时才开启的。文字描述的可能不太好理解,所以,直接通过操作的截图来理解这一过程。
首先是两个场景
+------+------+------+------+
| id | name | addr | fk |
+------+------+------+------+
| 2 | ss1 | 111 | 2 |
| 5 | ss2 | 111 | 3 |
| 6 | ss3 | 111 | 4 |
| 999 | ss4 | 111 | 5 |
| 1000 | ss5 | 111 | 6 |
| 1008 | ssx | 111 | 100 |
| 1014 | xx | 111 | 7 |
| 1015 | xx | 111 | 8 |
| 1012 | xx | 111 | 101 |
| 1013 | xx | 111 | 101 |
| 888 | xxx | 111 | 7 |
| 1 | yy | 111 | 9 |
+------+------+------+------+
图1中左边事务A,右边事务B,在此情况下,我们称事务B是可重复读的,因为两次读的结果是一样的,注意事务B读取的值是事务A修改后的值。
图1
那我们再来看一下图2,在已经执行begin的前提下,事务B读到的是事务A修改后的值。
图2
这是为什么呢?按照我们通常的理解,事务A的提交应该对事务B没有影响才对,怕不是我们对可重复有什么误解?
先来看这样一个操作,查询当前事务的trx_id
SELECT TRX_ID FROM INFORMATION_SCHEMA.INNODB_TRX WHERE TRX_MYSQL_THREAD_ID = CONNECTION_ID();那么我们来尝试一下
可以看到,在执行begin后并没有生成TRX_ID,也就是说此时事务还没有开始,根据此篇文章http://www..com/su-han/p/begin.html的说法,只有在执行了select或者以start transaction with consistent snapshot开始事务,才能够正式开启事务。这个过程涉及到了非锁定一致性读,也就是在事务第一次读取的时候生成了一个快照,在RR隔离级别下,该快照是事务开始时的数据,也即是产生了TRX_ID之后。更多关于非锁定一致性读和锁定一致性读可参考https://www.linuxidc.com/Linux/2017-08/146216.htm
参考文献:[1] https://www..com/Mr-Dawei/p/7460909.html?utm_source=debugrun&utm_medium=referral
[2] http://www..com/su-han/p/begin.html
[3] https://blog..net/skiof007/article/details/53376751
[4] https://blog..net/zyz511919766/article/details/49451255
[5] https://www.linuxidc.com/Linux/2017-08/146216.htm