mysql(InnoDB)事务隔离级别(READ COMMITTED) 与 锁,MVCC

READ COMMITTED

(提交读)

  1. 了解了之前 READ UNCOMMITTED 隔离级别是如何加锁的, 并且在文章中, 已经知道 READ COMMITTED 隔离级别可以解决脏读的问题, 那接下来, 对于 READ COMMITTED 隔离级别, 试想一下如果让你用锁来设计, 你会怎么做?

    • 既然READ COMMITTED 隔离级别可以解决脏读的问题, 也就是他可以让事务只能读其他事务已提交的的记录。
    • 如果用锁机制来实现该隔离级别:
      试想一下, 当在事务A中读取数据D的时候, 假设D之前已经在事务B中了, 并且事务B中对数据D做了修改, 但是事务B还没有完成(commit/rollback), 那如何让事务A无法读取数据D呢?
      当事务B在对数据D操作的时候, 假设给数据D加上了行级的排他锁(X lock), 那事务A自然只能阻塞等事务A完成后才能读取数据D了!
    • 数据库这样做的话确实实现了READ COMMITTED隔离级别的效果, 也就避免了脏读, 但问题是这是一种很低效的做法, 因为对于大部分应用来说, 读操作是多于写操作的, 当写操作加锁时, 那么读操作全部被阻塞, 这样在大用户量高并发的情况下, 会直接降低数据库的读效率。
  2. 那么, 既然用锁机制实现该隔离级别是低效的做法, 数据库是如何做的?
    之前在相关MVCC的文章中可以得到答案: 数据库是使用了 排他锁+MVCC 的机制来实现该隔离级别的, 而不是单纯的使用锁或者单纯的使用MVCC

READ COMMITTED与锁 测试

  1. 数据表结构如下:

    mysql> select * from test_transaction;
    +----+---------------+-----+--------+--------------------+
    | id | user_name     | age | gender | desctiption        |
    +----+---------------+-----+--------+--------------------+
    |  1 | 金刚狼     | 127 |      2 | 我有一双铁爪 |
    |  2 | 钢铁侠-rym | 120 |      1 | 我有一身铁甲 |
    |  3 | 绿巨人     |   0 |      2 | 我有一身肉    |
    +----+---------------+-----+--------+--------------------+
    3 rows in set (0.00 sec)
     
    mysql>
  2. 重新设置客户端1事务隔离级别为read committed: SET SESSION TRANSACTION ISOLATION LEVEL READ COMMITTED;

    mysql> SELECT @@SESSION.tx_isolation;
    +------------------------+
    | @@SESSION.tx_isolation |
    +------------------------+
    | REPEATABLE-READ        |
    +------------------------+
    1 row in set (0.00 sec)
     
    mysql> SET SESSION TRANSACTION ISOLATION LEVEL READ COMMITTED;
    Query OK, 0 rows affected (0.00 sec)
     
    mysql> SELECT @@SESSION.tx_isolation;
    +------------------------+
    | @@SESSION.tx_isolation |
    +------------------------+
    | READ-COMMITTED         |
    +------------------------+
    1 row in set (0.00 sec)
     
    mysql>
  3. 再重新打开一个客户端2并设置事务隔离级别为read committed;
  4. 客户端1中打开事务, 然后更改数据, 先不提交; 然后在客户端2中打开事务, 读取客户端1中尚未提交的那条被修改数据
    mysql(InnoDB)事务隔离级别(READ COMMITTED) 与 锁,MVCC
  5. 结果发现在客户端2中可以正常读取到那条数据, 只不过, 那条数据并不是被客户端1事务中修改后的数据, 而是最初的稳定数据, 这就避免了脏读!!
  6. 对于该隔离级别修改数据时使用的锁类型, 其分析方法, 和之前一篇MySQL(INNODB引擎)事务READ UNCOMMITTED隔离级别和锁的关系 是一样的:

    • 可以在客户端1的事务在修改数据并且未提交时, 在客户端2中对同一数据进行修改, 然后在客户端2阻塞阶段通过
      查看表的加锁情况: select * from information_schema.INNODB_LOCKS;,
      事务状态: select * from information_schema.INNODB_TRX;,
      进行分析, 结果就不展示了, 可以自行测试一下, 该隔离级别修改数据时使用的也是排他锁, 并且客户端2的修改语句会锁等待~
      (和之前分析READ UNCOMMITTED隔离级别一样, 既然使用了排他锁, 竟然别的事务还能读取, 这特么不就又违反了排他锁的特性么? 还是那句话, 另一个事务在读取的时候并不会加锁, 而是用的MVCC机制读取的镜像)
  7. 小结:
    InnoDB在该隔离级别(READ COMMITTED)写数据是使用排他锁, 读取数据不加锁而是使用了MVCC机制, 这样就可以大大提高并发读写效率, 写不影响读, 因为读并未加锁, 读的是记录的镜像版本!!

相关推荐