今天利用空闲时间给大家分享一下个人对事务隔离级别的理解,我们生活中应该都有存钱取钱的经历吧,那么我就用存钱取钱这个例子来讲解。
现在银行有一张money表,大概记录了交易id,客户名称,客户余额。
- READ-UNCOMMITTED
(1)所有事务都可以看到其他未提交事务的执行结果
(2)本隔离级别很少用于实际应用,因为它的性能也不比其他级别好多少
(3)该级别引发的问题是——脏读(Dirty Read):读取到了未提交的数据
小明在在ATM机往自己账户存入100元,此时未点确定,但是小明老婆小红却看到小明账户已经有100元了。
小明:
06:48:10 root@mysql3306.sock [xucl]>start transaction;
Query OK, 0 rows affected (0.00 sec)
06:48:24 root@mysql3306.sock [xucl]>insert into money values(1,'小明',100);
Query OK, 1 row affected (0.00 sec)
小红:
06:48:38 root@mysql3306.sock [xucl]>select * from money;
+------+--------+--------+
| tid | cname | cmoney |
+------+--------+--------+
| 1 | 小明 | 100 |
+------+--------+--------+
1 row in set (0.00 sec)
- READ-COMMITED
(1)这是大多数数据库系统的默认隔离级别(但不是MySQL默认的)
(2)它满足了隔离的简单定义:一个事务只能看见已经提交事务所做的改变
(3)这种隔离级别出现的问题是——不可重复读(Nonrepeatable Read):不可重复读意味着我们在同一个事务中执行完全相同的select语句时可能看到不一样的结果。
|——>导致这种情况的原因可能有:
(1)有一个交叉的事务有新的commit,导致了数据的改变;
(2)一个数据库被多个实例操作时,同一事务的其他实例在该实例处理其间可能会有新的commit
我们还是用例子来理解这件事情,小红打算去取小明卡里的钱的时候,看到了小明卡里有100元,这个时候小明往自己卡里又偷偷存了100元进去,小红取消了取钱,上面显示小明卡里有200块了。
小红:
06:55:56 root@mysql3306.sock [xucl]>start transaction;
Query OK, 0 rows affected (0.00 sec)
06:56:02 root@mysql3306.sock [xucl]>select * from money;
+------+--------+--------+
| tid | cname | cmoney |
+------+--------+--------+
| 1 | 小明 | 100 |
+------+--------+--------+
1 row in set (0.00 sec)
此时小明偷偷存入100元并提交:
06:56:30 root@mysql3306.sock [xucl]>update money set cmoney=200 where tid = 1;
Query OK, 1 row affected (0.00 sec)
Rows matched: 1 Changed: 1 Warnings: 0
06:58:24 root@mysql3306.sock [xucl]>commit;
Query OK, 0 rows affected (0.00 sec)
此时小红取消交易,再次查看余额显示200元
06:56:04 root@mysql3306.sock [xucl]>select * from money;
+------+--------+--------+
| tid | cname | cmoney |
+------+--------+--------+
| 1 | 小明 | 200 |
+------+--------+--------+
1 row in set (0.00 sec)
如果把小红取钱看成一次交易的话,在交易的过程中看到的两次余额不一样,这就是不可重复读。
- REPEATABLE READ
(1)这是MySQL的默认事务隔离级别
(2)它确保同一事务的多个实例在并发读取数据时,会看到同样的数据行
(3)此级别可能出现的问题——幻读(Phantom Read):当用户读取某一范围的数据行时,另一个事务又在该范围内插入了新行,当用户再读取该范围的数据行时,会发现有新的“幻影” 行
(4)InnoDB和Falcon存储引擎通过多版本并发控制(MVCC,Multiversion Concurrency Control)机制解决了该问题
下面我们来看例子:
小红:
07:12:40 root@mysql3306.sock [xucl]>start transaction;
Query OK, 0 rows affected (0.00 sec)
07:12:51 root@mysql3306.sock [xucl]>select * from money;
+------+--------+--------+
| tid | cname | cmoney |
+------+--------+--------+
| 1 | 小明 | 200 |
+------+--------+--------+
1 row in set (0.00 sec)
小明:
07:12:31 root@mysql3306.sock [xucl]>start transaction;
Query OK, 0 rows affected (0.00 sec)
07:13:08 root@mysql3306.sock [xucl]>update money set cmoney=300 where tid=1;
Query OK, 1 row affected (0.00 sec)
Rows matched: 1 Changed: 1 Warnings: 0
07:13:26 root@mysql3306.sock [xucl]>select * from money;
+------+--------+--------+
| tid | cname | cmoney |
+------+--------+--------+
| 1 | 小明 | 300 |
+------+--------+--------+
1 row in set (0.00 sec)
07:13:34 root@mysql3306.sock [xucl]>commit;
Query OK, 0 rows affected (0.00 sec)
小红再次查看:
07:12:57 root@mysql3306.sock [xucl]>select * from money;
+------+--------+--------+
| tid | cname | cmoney |
+------+--------+--------+
| 1 | 小明 | 200 |
+------+--------+--------+
1 row in set (0.00 sec)
显示还是只有200元,小红取出卡后再次查看就变成300元了。
07:16:57 root@mysql3306.sock [xucl]>start transaction;
Query OK, 0 rows affected (0.00 sec)
07:17:00 root@mysql3306.sock [xucl]>select * from money;
+------+--------+--------+
| tid | cname | cmoney |
+------+--------+--------+
| 1 | 小明 | 300 |
+------+--------+--------+
1 row in set (0.00 sec)
- SERIALIZABLE
(1)这是最高的隔离级别
(2)它通过强制事务排序,使之不可能相互冲突,从而解决幻读问题。简言之,它是在每个读的数据行上加上共享锁。
(3)在这个级别,可能导致大量的超时现象和锁竞争
简单来说就是小红一旦查看小明的账户,这个时候小明想要偷偷存钱进去就没办法了。
小红:
07:24:52 root@mysql3306.sock [xucl]>start transaction;
Query OK, 0 rows affected (0.00 sec)
07:25:33 root@mysql3306.sock [xucl]>select * from money;
+------+--------+--------+
| tid | cname | cmoney |
+------+--------+--------+
| 1 | 小明 | 300 |
+------+--------+--------+
1 row in set (10.04 sec)
小明:
07:26:15 root@mysql3306.sock [xucl]>update money set cmoney=400 where tid=1;
事务隔离级别 | 脏读 | 不可重复读 | 幻读 |
---|---|---|---|
读未提交(read-uncommitted) | 是 | 是 | 是 |
不可重复读(read-committed) | 否 | 是 | 是 |
可重复读(repeatable-read) | 否 | 否 | 是 |
串行化(serializable) | 否 | 否 | 否 |
小明发现自己的钱存不进去了。
简单总结:
一、
事务隔离级别 | 脏读 | 不可重复读 | 幻读 |
---|---|---|---|
读未提交(read-uncommitted) | 是 | 是 | 是 |
不可重复读(read-committed) | 否 | 是 | 是 |
可重复读(repeatable-read) | 否 | 否 | 是 |
串行化(serializable) | 否 | 否 | 否 |
二、事务隔离等级高->低:
serializable -> repeatable-read -> read-committed -> read-uncommitted
三、隔离级别越高,越能保证数据的完整性和一致性,但是对并发性能的影响也越大。对于多数应用程序,可以优先考虑把数据库系统的隔离级别设为Read Committed,它能够避免脏读取,而且具有较好的并发性能。尽管它会导致不可重复读、幻读这些并发问题,在可能出现这类问题的个别场合,可以由应用程序采用悲观锁或乐观锁来控制。
网友评论