今天利用空闲时间给大家分享一下个人对事务隔离级别的理解,我们生活中应该都有存钱取钱的经历吧,那么我就用存钱取钱这个例子来讲解。
    现在银行有一张money表,大概记录了交易id,客户名称,客户余额。

    • READ-UNCOMMITTED
      (1)所有事务都可以看到其他未提交事务的执行结果
      (2)本隔离级别很少用于实际应用,因为它的性能也不比其他级别好多少
      (3)该级别引发的问题是——脏读(Dirty Read):读取到了未提交的数据

    小明在在ATM机往自己账户存入100元,此时未点确定,但是小明老婆小红却看到小明账户已经有100元了。
    小明:

    1. 06:48:10 root@mysql3306.sock [xucl]>start transaction;
    2. Query OK, 0 rows affected (0.00 sec)
    3. 06:48:24 root@mysql3306.sock [xucl]>insert into money values(1,'小明',100);
    4. Query OK, 1 row affected (0.00 sec)

    小红:

    1. 06:48:38 root@mysql3306.sock [xucl]>select * from money;
    2. +------+--------+--------+
    3. | tid | cname | cmoney |
    4. +------+--------+--------+
    5. | 1 | 小明 | 100 |
    6. +------+--------+--------+
    7. 1 row in set (0.00 sec)
    • READ-COMMITED
      (1)这是大多数数据库系统的默认隔离级别(但不是MySQL默认的)
      (2)它满足了隔离的简单定义:一个事务只能看见已经提交事务所做的改变
      (3)这种隔离级别出现的问题是——不可重复读(Nonrepeatable Read):不可重复读意味着我们在同一个事务中执行完全相同的select语句时可能看到不一样的结果。
      |——>导致这种情况的原因可能有:
      (1)有一个交叉的事务有新的commit,导致了数据的改变;
      (2)一个数据库被多个实例操作时,同一事务的其他实例在该实例处理其间可能会有新的commit
      我们还是用例子来理解这件事情,小红打算去取小明卡里的钱的时候,看到了小明卡里有100元,这个时候小明往自己卡里又偷偷存了100元进去,小红取消了取钱,上面显示小明卡里有200块了。
      小红:
    1. 06:55:56 root@mysql3306.sock [xucl]>start transaction;
    2. Query OK, 0 rows affected (0.00 sec)
    3. 06:56:02 root@mysql3306.sock [xucl]>select * from money;
    4. +------+--------+--------+
    5. | tid | cname | cmoney |
    6. +------+--------+--------+
    7. | 1 | 小明 | 100 |
    8. +------+--------+--------+
    9. 1 row in set (0.00 sec)

    此时小明偷偷存入100元并提交:

    1. 06:56:30 root@mysql3306.sock [xucl]>update money set cmoney=200 where tid = 1;
    2. Query OK, 1 row affected (0.00 sec)
    3. Rows matched: 1 Changed: 1 Warnings: 0
    4. 06:58:24 root@mysql3306.sock [xucl]>commit;
    5. Query OK, 0 rows affected (0.00 sec)

    此时小红取消交易,再次查看余额显示200元

    1. 06:56:04 root@mysql3306.sock [xucl]>select * from money;
    2. +------+--------+--------+
    3. | tid | cname | cmoney |
    4. +------+--------+--------+
    5. | 1 | 小明 | 200 |
    6. +------+--------+--------+
    7. 1 row in set (0.00 sec)

    如果把小红取钱看成一次交易的话,在交易的过程中看到的两次余额不一样,这就是不可重复读。

    • REPEATABLE READ
      (1)这是MySQL的默认事务隔离级别
      (2)它确保同一事务的多个实例在并发读取数据时,会看到同样的数据行
      (3)此级别可能出现的问题——幻读(Phantom Read):当用户读取某一范围的数据行时,另一个事务又在该范围内插入了新行,当用户再读取该范围的数据行时,会发现有新的“幻影” 行
      (4)InnoDB和Falcon存储引擎通过多版本并发控制(MVCC,Multiversion Concurrency Control)机制解决了该问题
      下面我们来看例子:
      小红:
    1. 07:12:40 root@mysql3306.sock [xucl]>start transaction;
    2. Query OK, 0 rows affected (0.00 sec)
    3. 07:12:51 root@mysql3306.sock [xucl]>select * from money;
    4. +------+--------+--------+
    5. | tid | cname | cmoney |
    6. +------+--------+--------+
    7. | 1 | 小明 | 200 |
    8. +------+--------+--------+
    9. 1 row in set (0.00 sec)

    小明:

    1. 07:12:31 root@mysql3306.sock [xucl]>start transaction;
    2. Query OK, 0 rows affected (0.00 sec)
    3. 07:13:08 root@mysql3306.sock [xucl]>update money set cmoney=300 where tid=1;
    4. Query OK, 1 row affected (0.00 sec)
    5. Rows matched: 1 Changed: 1 Warnings: 0
    6. 07:13:26 root@mysql3306.sock [xucl]>select * from money;
    7. +------+--------+--------+
    8. | tid | cname | cmoney |
    9. +------+--------+--------+
    10. | 1 | 小明 | 300 |
    11. +------+--------+--------+
    12. 1 row in set (0.00 sec)
    13. 07:13:34 root@mysql3306.sock [xucl]>commit;
    14. Query OK, 0 rows affected (0.00 sec)

    小红再次查看:

    1. 07:12:57 root@mysql3306.sock [xucl]>select * from money;
    2. +------+--------+--------+
    3. | tid | cname | cmoney |
    4. +------+--------+--------+
    5. | 1 | 小明 | 200 |
    6. +------+--------+--------+
    7. 1 row in set (0.00 sec)

    显示还是只有200元,小红取出卡后再次查看就变成300元了。

    1. 07:16:57 root@mysql3306.sock [xucl]>start transaction;
    2. Query OK, 0 rows affected (0.00 sec)
    3. 07:17:00 root@mysql3306.sock [xucl]>select * from money;
    4. +------+--------+--------+
    5. | tid | cname | cmoney |
    6. +------+--------+--------+
    7. | 1 | 小明 | 300 |
    8. +------+--------+--------+
    9. 1 row in set (0.00 sec)
    • SERIALIZABLE
      (1)这是最高的隔离级别
      (2)它通过强制事务排序,使之不可能相互冲突,从而解决幻读问题。简言之,它是在每个读的数据行上加上共享锁。
      (3)在这个级别,可能导致大量的超时现象和锁竞争
      简单来说就是小红一旦查看小明的账户,这个时候小明想要偷偷存钱进去就没办法了。
      小红:
    1. 07:24:52 root@mysql3306.sock [xucl]>start transaction;
    2. Query OK, 0 rows affected (0.00 sec)
    3. 07:25:33 root@mysql3306.sock [xucl]>select * from money;
    4. +------+--------+--------+
    5. | tid | cname | cmoney |
    6. +------+--------+--------+
    7. | 1 | 小明 | 300 |
    8. +------+--------+--------+
    9. 1 row in set (10.04 sec)

    小明:

    1. 07:26:15 root@mysql3306.sock [xucl]>update money set cmoney=400 where tid=1;

    小明发现自己的钱存不进去了。
    简单总结
    一、

    事务隔离级别 脏读 不可重复读 幻读
    读未提交(read-uncommitted)
    不可重复读(read-committed)
    可重复读(repeatable-read)
    串行化(serializable)

    二、事务隔离等级高->低:
    serializable -> repeatable-read -> read-committed -> read-uncommitted

    三、隔离级别越高,越能保证数据的完整性和一致性,但是对并发性能的影响也越大。对于多数应用程序,可以优先考虑把数据库系统的隔离级别设为Read Committed,它能够避免脏读取,而且具有较好的并发性能。尽管它会导致不可重复读、幻读这些并发问题,在可能出现这类问题的个别场合,可以由应用程序采用悲观锁或乐观锁来控制。