几个例子了解InnoDB的隔离级别

在数据库终端中验证事务隔离级别:

数据库表展示

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 //抛出错误

猜你喜欢

转载自www.cnblogs.com/insaneXs/p/12908571.html