• 几个例子了解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 //抛出错误
  • 相关阅读:
    Unit of Work
    OAuth做webapi认证
    Js数组
    UWP开发的一些思考
    表格行拖拽
    委托
    Git协作流程(转)
    全自动Web后门扫描(转)
    Gradle 2.0用户手册——总览(译)(转)
    面向对象之两大要领 (转)
  • 原文地址:https://www.cnblogs.com/insaneXs/p/12908571.html
Copyright © 2020-2023  润新知