概述
最近因为工作需要,对MySQL的事务处理进行了一系列测试验证,其中,对于MySQL的事务到底时什么时候生成了数据的快照,结果似乎跟薛定谔的猫理念很像,很有意思;过程我贴出来,有兴趣的朋友可以一起研究研究。
事务不依赖于虚拟表或者辅助表,而是主要依赖于 MySQL 的 InnoDB 存储引擎所提供的多版本并发控制 (MVCC) 机制和事务日志。
在事务开始时,MySQL 会记录当前事务的快照 (snapshot),这意味着在事务期间,你看到的数据是事务开始时的版本,即使其他事务对数据进行了修改。
本文的所有步骤严格按照时间线前后关联,顺序变了,结果就会变化。
假如有如下表:
# Session 2MySQL localhost:3306 ssl xiaml SQL > select * from accounts;
+------------+---------+
| account_id | balance |
+------------+---------+
| 4 | 500.00 |
| 7 | 5000.00 |
| 8 | 6000.00 |
| 9 | 1000.00 |
| 10 | 2000.00 |
+------------+---------+
5 rows in set (0.0005 sec)MySQL localhost:3306 ssl xiaml SQL >
- 1.
- 2.
- 3.
- 4.
- 5.
- 6.
- 7.
- 8.
- 9.
- 10.
- 11.
- 12.
- 13.
测试1:事务中进行了数据观察
# Session1
mysql> START TRANSACTION;
Query OK, 0 rows affected (0.00 sec)mysql>
-- 此时并不建立快照,此时对事务而言,数据与实际表完全一致。5行数据。但是别查询!!
- 1.
- 2.
- 3.
- 4.
- 5.
- 6.
# Session 2
# 删除两条数据MySQL localhost:3306 ssl xiaml SQL > DELETE FROM accounts WHERE account_id = 7 or account_id = 8;
Query OK, 2 row affected (0.0038 sec)MySQL localhost:3306 ssl xiaml SQL >MySQL localhost:3306 ssl xiaml SQL > select * from accounts;
+------------+---------+
| account_id | balance |
+------------+---------+
| 4 | 500.00 |
| 9 | 1000.00 |
| 10 | 2000.00 |
+------------+---------+
3 rows in set (0.0009 sec)MySQL localhost:3306 ssl xiaml SQL >
- 1.
- 2.
- 3.
- 4.
- 5.
- 6.
- 7.
- 8.
- 9.
- 10.
- 11.
- 12.
- 13.
- 14.
- 15.
# Session1
mysql> START TRANSACTION;
Query OK, 0 rows affected (0.00 sec)mysql>
-- 此时并不建立快照,此时对事务而言,数据与实际表完全一致。***********3行数据。**************
mysql> select * from accounts;
+------------+---------+
| account_id | balance |
+------------+---------+
| 4 | 500.00 |
| 9 | 1000.00 |
| 10 | 2000.00 |
+------------+---------+
3 rows in set (0.00 sec)mysql>
-- 此时快照已建立,也就是说START TRANSACTION;后的任何语句就是精确的快照点。
- 1.
- 2.
- 3.
- 4.
- 5.
- 6.
- 7.
- 8.
- 9.
- 10.
- 11.
- 12.
- 13.
- 14.
- 15.
- 16.
- 17.
- 18.
# Session 2
# 再删除一条数据MySQL localhost:3306 ssl xiaml SQL > DELETE FROM accounts WHERE account_id =9;
Query OK, 1 row affected (0.0038 sec)MySQL localhost:3306 ssl xiaml SQL >MySQL localhost:3306 ssl xiaml SQL > select * from accounts;
+------------+---------+
| account_id | balance |
+------------+---------+
| 4 | 500.00 |
| 10 | 2000.00 |
+------------+---------+
2 rows in set (0.0006 sec)MySQL localhost:3306 ssl xiaml SQL >
- 1.
- 2.
- 3.
- 4.
- 5.
- 6.
- 7.
- 8.
- 9.
- 10.
- 11.
- 12.
- 13.
- 14.
# Session1
mysql> START TRANSACTION;
Query OK, 0 rows affected (0.00 sec)mysql>
-- 此时并不建立快照,此时对事务而言,数据与实际表完全一致。***********3行数据。**************
mysql> select * from accounts;
+------------+---------+
| account_id | balance |
+------------+---------+
| 4 | 500.00 |
| 9 | 1000.00 |
| 10 | 2000.00 |
+------------+---------+
3 rows in set (0.00 sec)mysql>
-- 此时快照已建立,也就是说START TRANSACTION;后的任何语句就是精确的快照点。
mysql> select * from accounts;
+------------+---------+
| account_id | balance |
+------------+---------+
| 4 | 500.00 |
| 9 | 1000.00 |
| 10 | 2000.00 |
+------------+---------+
3 rows in set (0.00 sec)mysql>
-- 因为快照已建立,因此,对于事务来说时间就凝固在那一刻了;其他会话的改变对凝固状态下的数据无影响。
- 1.
- 2.
- 3.
- 4.
- 5.
- 6.
- 7.
- 8.
- 9.
- 10.
- 11.
- 12.
- 13.
- 14.
- 15.
- 16.
- 17.
- 18.
- 19.
- 20.
- 21.
- 22.
- 23.
- 24.
- 25.
- 26.
- 27.
- 28.
- 29.
- 30.
很像薛定谔的猫的感觉,只要观测过,从那一刻就出现了分支。
测试1:事务中先不做数据观察
那我们再做个实验;事务开始后不做任何地数据的操作甚至查询都不做,只定义一个变量;然后在另一个会话删除数据然后观察删除是否在事务中产生分支?
# Session1
MySQL localhost:3306 ssl xiaml SQL > select * from accounts;
+------------+---------+
| account_id | balance |
+------------+---------+
| 4 | 500.00 |
| 7 | 5000.00 |
| 8 | 6000.00 |
| 9 | 1000.00 |
| 10 | 2000.00 |
+------------+---------+
5 rows in set (0.0009 sec)MySQL localhost:3306 ssl xiaml SQL >
- 1.
- 2.
- 3.
- 4.
- 5.
- 6.
- 7.
- 8.
- 9.
- 10.
- 11.
- 12.
- 13.
# Sesson2
mysql> START TRANSACTION;
Query OK, 0 rows affected (0.00 sec)mysql>
-- 此时并不建立快照,此时对事务而言,数据与实际表完全一致。5行数据。但是别查询!!
-- 在另一个会话中删除2条数据
- 1.
- 2.
- 3.
- 4.
- 5.
- 6.
- 7.
# Session1
# 删除两条数据MySQL localhost:3306 ssl xiaml SQL > DELETE FROM accounts WHERE account_id =7 or account_id = 8;
Query OK, 2 rows affected (0.0044 sec)MySQL localhost:3306 ssl xiaml SQL > select * from accounts;
+------------+---------+
| account_id | balance |
+------------+---------+
| 4 | 500.00 |
| 9 | 1000.00 |
| 10 | 2000.00 |
+------------+---------+
3 rows in set (0.0015 sec)MySQL localhost:3306 ssl xiaml SQL >
- 1.
- 2.
- 3.
- 4.
- 5.
- 6.
- 7.
- 8.
- 9.
- 10.
- 11.
- 12.
- 13.
- 14.
# Session2
mysql> START TRANSACTION;
Query OK, 0 rows affected (0.00 sec)
mysql>
-- 此时并不建立快照,此时对事务而言,数据与实际表完全一致。5行数据。但是别查询!!
-- 在另一个会话中删除2条数据
mysql> SET @name := 'xiamingliang';
Query OK, 0 rows affected (0.00 sec)mysql>
-- 仅仅在事务中创建一个变量,不对数据进行任何形式的查询与变更动作!!
-- 在另一个会话中再删除1条数据
- 1.
- 2.
- 3.
- 4.
- 5.
- 6.
- 7.
- 8.
- 9.
- 10.
- 11.
- 12.
# Session1
# 再删除一条数据MySQL localhost:3306 ssl xiaml SQL > DELETE FROM accounts WHERE account_id = 9;
Query OK, 1 row affected (0.0028 sec)MySQL localhost:3306 ssl xiaml SQL > select * from accounts;
+------------+---------+
| account_id | balance |
+------------+---------+
| 4 | 500.00 |
| 10 | 2000.00 |
+------------+---------+
2 rows in set (0.0005 sec)MySQL localhost:3306 ssl xiaml SQL >
- 1.
- 2.
- 3.
- 4.
- 5.
- 6.
- 7.
- 8.
- 9.
- 10.
- 11.
- 12.
- 13.
# Session2
mysql> START TRANSACTION;
Query OK, 0 rows affected (0.00 sec)
mysql>
-- 此时并不建立快照,此时对事务而言,数据与实际表完全一致。5行数据。但是别查询!!
-- 在另一个会话中删除2条数据
mysql> SET @name := 'xiamingliang';
Query OK, 0 rows affected (0.00 sec)mysql>
-- 仅仅在事务中创建一个变量,不对数据进行任何形式的查询与变更动作!!
-- 在另一个会话中再删除1条数据
mysql> select * from accounts;
+------------+---------+
| account_id | balance |
+------------+---------+
| 4 | 500.00 |
| 10 | 2000.00 |
+------------+---------+
2 rows in set (0.00 sec)mysql>
-- 在另一个会话中再删除1条数据
- 1.
- 2.
- 3.
- 4.
- 5.
- 6.
- 7.
- 8.
- 9.
- 10.
- 11.
- 12.
- 13.
- 14.
- 15.
- 16.
- 17.
- 18.
- 19.
- 20.
- 21.
- 22.
- 23.
# Session1
# 再删除一条数据MySQL localhost:3306 ssl xiaml SQL > DELETE FROM accounts WHERE account_id = 10;
Query OK, 1 row affected (0.0037 sec)MySQL localhost:3306 ssl xiaml SQL > select * from accounts;
+------------+---------+
| account_id | balance |
+------------+---------+
| 4 | 500.00 |
+------------+---------+
1 row in set (0.0013 sec)MySQL localhost:3306 ssl xiaml SQL >
- 1.
- 2.
- 3.
- 4.
- 5.
- 6.
- 7.
- 8.
- 9.
- 10.
- 11.
- 12.
# Session2
mysql> START TRANSACTION;
Query OK, 0 rows affected (0.00 sec)
mysql>
-- 此时并不建立快照,此时对事务而言,数据与实际表完全一致。5行数据。但是别查询!!
-- 在另一个会话中删除2条数据
mysql> SET @name := 'xiamingliang';
Query OK, 0 rows affected (0.00 sec)mysql>
-- 仅仅在事务中创建一个变量,不对数据进行任何形式的查询与变更动作!!
-- 在另一个会话中再删除1条数据
mysql> select * from accounts;
+------------+---------+
| account_id | balance |
+------------+---------+
| 4 | 500.00 |
| 10 | 2000.00 |
+------------+---------+
2 rows in set (0.00 sec)mysql>
-- 在另一个会话中再删除1条数据;此时新的快照分支就形成了
mysql> select * from accounts;
+------------+---------+
| account_id | balance |
+------------+---------+
| 4 | 500.00 |
| 10 | 2000.00 |
+------------+---------+
2 rows in set (0.00 sec)mysql>
-- 因为快照已建立,因此,对于事务来说时间就凝固在那一刻了;其他会话的改变对凝固状态下的数据无影响。
- 1.
- 2.
- 3.
- 4.
- 5.
- 6.
- 7.
- 8.
- 9.
- 10.
- 11.
- 12.
- 13.
- 14.
- 15.
- 16.
- 17.
- 18.
- 19.
- 20.
- 21.
- 22.
- 23.
- 24.
- 25.
- 26.
- 27.
- 28.
- 29.
- 30.
- 31.
- 32.
- 33.
- 34.
这再次印证了;只要在事务开始后以任何形式观察过数据,那么从这一刻开始新的分支就形成了;真的很像薛定谔的猫和平行宇宙的感觉。