什么是事物
事物通俗的来讲就是就是一组操作事件,可以类比于Java里面的原子操作。在一个事物中,要么全部成功,要么就是全部失败。
mysql中的事物
在Mysql的innodb
中,事物的默认级别是 可重复读
,在该级别下,事物可能出现幻读。出现幻读的情况是该引擎为行级锁,导致mysql在进行一个事物的时候只会锁定与该事物有关的几行。
示例如下:
新建一个表,其中的数据如下:
1 2 3 4 5 6 7 8 9
| mysql> select * from curd; +----+------+ | id | name | +----+------+ | 3 | 4 | | 4 | 3 | | 5 | 2 | | 6 | 1 | +----+------+
|
开启一个事物:
1 2 3 4 5 6
| START TRANSACTION; SELECT * FROM curd; UPDATE curd SET curd.name='1000'; SELECT * FROM curd;
|
此时两次查询的结果为:
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
| mysql> START TRANSACTION; Query OK, 0 rows affected (0.00 sec)
mysql> SELECT * FROM curd; + | id | name | + | 3 | 4 | | 4 | 3 | | 5 | 2 | | 6 | 1 | + 4 rows in set (0.00 sec)
mysql> UPDATE curd SET curd.name='1000'; Query OK, 4 rows affected (0.00 sec) Rows matched: 4 Changed: 4 Warnings: 0
mysql> SELECT * FROM curd; + | id | name | + | 3 | 1000 | | 4 | 1000 | | 5 | 1000 | | 6 | 1000 | + 4 rows in set (0.00 sec)
|
此时可以看到,在事物一未提交之前,事物一做了一个全表的更新,将该表的数值全部更新为1000了。
然后开启 事物二
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| mysql> START TRANSACTION; Query OK, 0 rows affected (0.00 sec)
mysql> SELECT * FROM curd; + | id | name | + | 3 | 4 | | 4 | 3 | | 5 | 2 | | 6 | 1 | + 4 rows in set (0.00 sec)
mysql> INSERT INTO curd(curd.id,curd.name) VALUES(7,'7'); ERROR 1205 (HY000): Lock wait timeout exceeded; try restarting transaction
|
当事物二执行完这一句之后,回到事物一,然后查询表数据。
1 2 3 4 5 6 7 8 9 10 11 12
| mysql> SELECT * FROM curd; + | id | name | + | 3 | 1000 | | 4 | 1000 | | 5 | 1000 | | 6 | 1000 | | 7 | 7 | + 4 rows in set (0.00 sec)
|
此时就是幻读
,事物一明明就更新了6条数据,为什么会出现7条数据。。
然后事物一提交:
1 2
| mysql> commit; Query OK, 0 rows affected (0.01 sec)
|
此时数据库中的数据已经全部更新成1000,但是在事物二中:
1 2 3 4 5 6 7 8 9 10 11
| mysql> SELECT * FROM curd; + | id | name | + | 3 | 4 | | 4 | 3 | | 5 | 2 | | 6 | 1 | | 7 | 7 | + 5 rows in set (0.00 sec)
|
说明两个事物之间不能读取其他事物未提交的更改,并且在mysql的该级别下,事物开始和结束的时候所读取的数据是一样的。
此时提交事物二:
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| mysql> commit; Query OK, 0 rows affected (0.00 sec)
mysql> SELECT * FROM curd; + | id | name | + | 3 | 1000 | | 4 | 1000 | | 5 | 1000 | | 6 | 1000 | | 7 | 7 | + 5 rows in set (0.00 sec)
|
可以看到此时数据库中的数据已经如成为了上面所示了。
原理
具体是因为mysql的innodb使用的是行级锁
,而且在mysql的每一个表里面都会有两个隐藏的列,分别是创建的时候版本号和删除的时候的版本号。
所以每一个事物执行的时候的版本号都会被该事物号所更新,因此会导致有的事物在执行期间会导致看到不同的数据。
注意
现在mysql5.7貌似已经解决了幻读。。用命令行进行测试的。