在数据库终端中验证事务隔离级别:
数据库表展示
mysql> show columns from user_balance;
+---------+---------------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+---------+---------------+------+-----+---------+----------------+
| id | int(11) | NO | PRI | NULL | auto_increment |
| name | varchar(20) | YES | | NULL | |
| balance | decimal(10,0) | YES | | NULL | |
+---------+---------------+------+-----+---------+----------------+
3 rows in set (0.00 sec)
初始数据状态
mysql> select * from user_balance;
+----+--------+---------+
| id | name | balance |
+----+--------+---------+
| 1 | 张三 | 1000 |
| 2 | 李四 | 1000 |
+----+--------+---------+
2 rows in set (0.00 sec)
每次测试结束后,我们都将数据还愿至此状态,在进行下次测试。
关闭全局自动提交:
set global autocommit = 0;
isolation_read_uncommit 情况下验证脏读的问题
设置并确认全局隔离级别为read_uncommit
mysql> set global transaction isolation level read uncommitted;
Query OK, 0 rows affected (0.00 sec)
mysql> select @@global.tx_isolation;
+-----------------------+
| @@global.tx_isolation |
+-----------------------+
| READ-UNCOMMITTED |
+-----------------------+
1 row in set, 1 warning (0.00 sec)
测试方式,在两个终端中同时依次运行事务:
事务 1 | 事务 2 |
---|---|
mysql> begin; Query OK, 0 rows affected (0.00 sec) |
begin; Query OK, 0 rows affected (0.00 sec) |
mysql> select * from user_balance where id = 3; Empty set (0.00 sec) |
|
mysql> insert into user_balance values (3, '王五', '1000'); Query OK, 1 row affected (0.02 sec) |
|
mysql> select * from user_balance where id = 3; +----+--------+---------+ | id | name | balance | +----+--------+---------+ | 3 | 王五 | 1000 | +----+--------+---------+ 1 row in set (0.00 sec) |
|
mysql> rollback; Query OK, 0 rows affected (0.00 sec) |
|
mysql> select * from user_balance where id =3; Empty set (0.00 sec) |
|
mysql> commit; Query OK, 0 rows affected (0.00 sec) |
可以看到事务1在事务2未提交事务前,就能读取到事务2对数据库的修改,但是读到的数据是不可靠的脏数据,因为事务2很可能会回滚,所以READ_UNCOMMITED连脏读都避免不了。
isolation_read_committed 情况下验证不可重复读的问题:
修改并确认当前事务级别是read_committed
:
mysql> set global transaction isolation level read committed;
Query OK, 0 rows affected (0.00 sec)
测试方式:开两个数据库终端,前后执行顺序依次为:
事务1 | 事务2 |
---|---|
mysql> begin; Query OK, 0 rows affected (0.00 sec) |
begin; Query OK, 0 rows affected (0.00 sec) |
mysql> select * from user_balance where id = 2; +----+--------+---------+ | id | name | balance | +----+--------+---------+ | 2 | 李四 | 1000 | +----+--------+---------+ 1 row in set (0.00 sec) |
|
mysql> update user_balance set name = '李四光' where id = 2; Query OK, 1 row affected (0.00 sec) Rows matched: 1 Changed: 1 Warnings: 0 |
|
mysql> select * from user_balance where id = 2; +----+--------+---------+ | id | name | balance | +----+--------+---------+ | 2 | 李四 | 1000 | +----+--------+---------+ 1 row in set (0.00 sec) |
|
mysql> commit; Query OK, 0 rows affected (0.01 sec) |
|
mysql> select * from user_balance where id = 2; +----+--------+---------+ | id | name | balance | +----+--------+---------+ | 2 | 李四光 | 1000 | +----+--------+---------+ 1 row in set (0.00 sec) |
|
mysql> commit; Query OK, 0 rows affected (0.01 sec) |
上面的例子可以看到事务1在事务2提交前后,对同一数据的查询结果不同,这就是不可重复读的问题。
有同学会问,这样不是实事求是?不是很合理嘛?
其实不然,我们换个例子考虑不可重复读造成的危害。
假设事务1依旧先查询id=2的信息,发现账户上有1000块,然后希望这时候从账户上支出1000块,但是在这之前,事务2也对id=2的账户也进行了操作,从账户支出了500,并提交了事务。但是由于隔离级别允许不可重复读存在,那么事务1也能正常提交。id=2的账户余额被清为0,但是却总共支持了1500块。这显然是银行类系统无法接受的。
isolation_repeatable_read 情况下验证幻读问题:
确认当前隔离级别是为repeatable_read
:
mysql> select @@global.tx_isolation;
+-----------------------+
| @@global.tx_isolation |
+-----------------------+
| REPEATABLE-READ |
+-----------------------+
1 row in set, 1 warning (0.00 sec)
测试方式:开两个数据库终端,前后执行顺序依次:
事务 1 | 事务2 |
---|---|
mysql> begin; | mysql> begin; |
mysql> select * from user_balance where id = 3; Empty set (0.00 sec) |
|
mysql> insert into user_balance values (3, '王五', '1000'); Query OK, 1 row affected (0.00 sec) |
|
mysql> select * from user_balance where id = 3; Empty set (0.00 sec) |
|
mysql> commit; | |
mysql> select * from user_balance where id = 3; Empty set (0.00 sec) |
|
mysql> insert into user_balance values(3, '赵六', 1000); ERROR 1062 (23000): Duplicate entry '3' for key 'PRIMARY' |
|
mysql> commit; Query OK, 0 rows affected (0.00 sec) |
事务1无论是在事务2提交前后,都看不到id = 3
的数据;但是事务1希望插入id = 3
的数据时,数据库却产生主键重复的错误。
事务1就懵逼了,明明我从数据库查询没有id = 3
的数据,但是我插入时你却跟我说有数据,难道我刚才产生幻觉了?这就是所谓的REPEATABLE_READ
解决不了幻读的问题。
isolation_serialization事务执行验证
这次我们直接验证两个事务对同一行记录进行修改的情况
首先设置并确认隔离级别为SERIALIZATION
mysql> set global transaction isolation level serializable;
开启两个事务:
事务 1 | 事务 2 |
---|---|
mysql> begin; Query OK, 0 rows affected (0.00 sec) |
|
begin; Query OK, 0 rows affected (0.00 sec) |
|
mysql> select * from user_balance where id = 2; +----+--------+---------+ | id | name | balance | +----+--------+---------+ | 2 | 李四 | 1000 | +----+--------+---------+ 1 row in set (0.00 sec) |
|
mysql> update user_balance set balance = 800 where id =2; //终端被挂起 | |
mysql> update user_balance set balance = 500 where id = 2; Query OK, 1 row affected (0.00 sec) Rows matched: 1 Changed: 1 Warnings: 0 |
//继续挂起 |
mysql> commit; Query OK, 0 rows affected (0.03 sec) |
ERROR 1213 (40001): Deadlock found when trying to get lock; try restarting transaction //抛出错误 |