ANSI/ISO SQL标准定义了4中事务隔离级别:
- 未提交读(read uncommitted)
- 提交读(read committed)
- 重复读(repeatable read)
- 串行读(serializable)
对于不同的事务,采用不同的隔离级别分别有不同的结果。不同的隔离级别有不同的现象。主要有下面3种现在:
- 1、脏读(dirty read):一个事务可以读取另一个尚未提交事务的修改数据。
- 非重复读(nonrepeatable read):在同一个事务中,同一个查询在S1时间读取某一行,在S2时间重新读取这一行时候,这一行的数据已经发生修改,可能被更新了(update),也可能被删除了(delete)。
- 幻像读(phantom read):在同一事务中,同一查询多次进行时候,由于其他插入操作(insert)的事务提交,导致每次返回不同的结果集。
不同的隔离级别有不同的现象,并有不同的锁定/并发机制,隔离级别越高,数据库的并发性就越差,4种事务隔离级别分别表现的现象如下表:
隔离级别 | 脏读 | 非重复读 | 幻像读 |
read uncommitted | 允许 | 允许 | 允许 |
read committed | 允许 | 允许 | |
repeatable read | 允许 | ||
serializable |
数据库中的默认事务隔离级别
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
在Oracle中默认的事务隔离级别是提交读(read committed)。 对于MySQL的Innodb的默认事务隔离级别是重复读(repeatable read)。可以通过下面的命令查看: mysql> SELECT @@GLOBAL.tx_isolation, @@tx_isolation; +———————–+—————–+ | @@GLOBAL.tx_isolation | @@tx_isolation | +———————–+—————–+ | REPEATABLE-READ | REPEATABLE-READ | +———————–+—————–+ 1 row in set (0.00 sec) |
测试mysql默认事务隔离级别(repeatable read)
Time | terminal1 | terminal2 |
S1 | set autocommit=0; | set autocommit=0; |
S2 | mysql> select * from book; +—-+——+ | c1 | c2 | +—-+——+ | 1 | Luis | | 2 | lisa | | 3 | nana | +—-+——+ 3 rows in set | |
S3 | update book set c2=”shooter” where c1=3; Query OK, 1 row affected Rows matched: 1 Changed: 1 Warnings: 0 | |
S4 | mysql> select * from book; +—-+——+ | c1 | c2 | +—-+——+ | 1 | Luis | | 2 | lisa | | 3 | nana | +—-+——+ 3 rows in set Terminal2 未提交,看到查询数据不变,说明不允许脏读(无脏读) | |
S5 | commit; | |
S6 | mysql> select * from book; +—-+——+ | c1 | c2 | +—-+——+ | 1 | Luis | | 2 | lisa | | 3 | nana | +—-+——+ 3 rows in set Terminal2已提交,数据还是无改变,说明可以重复读 | |
S7 | commit; | |
S8 | mysql> select * from book; +—-+———+ | c1 | c2 | +—-+———+ | 1 | Luis | | 2 | lisa | | 3 | shooter | +—-+———+ 3 rows in set 提交事务后看到了最新数据 | |
S9 | insert into book (c1,c2) values (40,”亮亮”); commit; | |
mysql> select * from book; +—-+———+ | c1 | c2 | +—-+———+ | 1 | Luis | | 2 | lisa | | 3 | shooter | +—-+———+ 3 rows in set Terminal2插入数据后已提交,查询仍未发生变化,说明未发生幻象读 | ||
commit; mysql> commit; Query OK, 0 rows affected mysql> select * from book; +—-+———+ | c1 | c2 | +—-+———+ | 1 | Luis | | 2 | lisa | | 3 | shooter | | 40 | 亮亮 | +—-+———+ 4 rows in set 事务提交后,看到最新数据。 |
1 |
上面的结果可以看到Mysql Innodb的重复读(repeatable read)不允许脏读,不允许非重复读(即可以重复读,Innodb使用多版本一致性读来实现)和不允许幻象读(这点和ANSI/ISO SQL标准定义的有所区别). |
通过上述实验得出mysql(repeatable read)隔离级别如下
隔离级别 | 脏读 | 非重复读 | 重复读 | 幻象读 |
repeatable read | 不允许 | 不允许 | 允许 | 不允许 |
同样的测试:
1 2 3 |
1、当terminal2进行truncate表的时候,这个时候session 1再次查询就看不到数据。 2、当terminal2进行alter表的时候,这个时候session 1再次查询就看不到数据。 |
造成以上的原因是因为 mysql的持续非锁定读,在repeatable read级别下,读采用的是持续非锁定读。
持续读意味着InnoDB使用它的多版本化来给一个查询,展示某个时间点处数据库的快照。查询看到在那个时间点之前 被 提交的那些 确切事务做的更改,并且没有其后的事务或未提交事务做的改变。这个规则的例外是,查询看到发布该查询的事务本身所做的改变。
如果你运行在默认的REPEATABLE READ隔离级别,则在同一事务内的所有持续读读取由该事务中第一个这样的读所确立的快照。你可以通过提交当前事务并在发布新查询的事务之后,为你的查询获得一个更新的快照。
持续读是默认模式,在其中InnoDBzai在READ COMMITTED和REPEATABLE READ隔离级别处理SELECT语句。持续读不在任何它访问的表上设置锁定,因此,其它用户可自由地在持续读在一个表上执行的同一时间修改这些表
注意:持续读不在DROP TABLE和ALTER TABLE上作用。持续读不在DROP TABLE上作用,因为MySQL不能使用已经被移除的表,并且InnoDB 破坏了该表。持续读不在ALTER TABLE上作用,因为它在某事务内执行,该事务创建一个新表,并且从旧表往新表中插入行。现在,当你重新发出持续读之时,它不能在新表中看见任何行,因为它们被插入到一个在持续读读取的快照中不可见的事务 里。
- 本文固定链接: https://www.yoyoask.com/?p=2887
- 转载请注明: shooter 于 SHOOTER 发表