概述

最近因为工作需要,对MySQL的事务处理进行了一系列测试验证,其中,对于MySQL的事务到底时什么时候生成了数据的快照,结果似乎跟薛定谔的猫理念很像,很有意思;过程我贴出来,有兴趣的朋友可以一起研究研究。

事务不依赖于虚拟表或者辅助表,而是主要依赖于 MySQL 的 InnoDB 存储引擎所提供的多版本并发控制 (MVCC) 机制和事务日志。

在事务开始时,MySQL 会记录当前事务的快照 (snapshot),这意味着在事务期间,你看到的数据是事务开始时的版本,即使其他事务对数据进行了修改。

本文的所有步骤严格按照时间线前后关联,顺序变了,结果就会变化。

【MySQL】事务的快照生成时间点和薛定谔的猫相关?_事务

假如有如下表:

# 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.

这再次印证了;只要在事务开始后以任何形式观察过数据,那么从这一刻开始新的分支就形成了;真的很像薛定谔的猫和平行宇宙的感觉。