Oracle、MySQL、PostgreSQL、openGauss、达梦、OB、Tidb数据库比较系列(二十): 事务隔离级别
事务隔离级别是数据库事务处理的基础,ACID 中的 “I”,即 Isolation,指的就是事务的隔离性。ANSI/ISO SQL 标准定义了4 种事务隔离级别,对于相同的事务,采用不同的隔离级别分别有不同的结果。这些隔离级别是根据3 个“现象”定义的,以下就是给定隔离级别可能允许或不允许的3 种现象:
脏读(dirty read):或叫读未提交。能读取未提交的数据,也就是脏数据。只要打开别人正在读写的一个OS 文件(不论文件中有什么数据),就可以达到脏读的效果。如果允许脏读,将影响数据完整性,另外外键约束会遭到破坏,而且会忽略惟一性约束。
不可重复读(nonrepeatable read):这意味着,如果你在T1 时间读取某一行,在T2 时间重新读取这一行时,这一行可能已经有所修改。也许它已经消失,有可能被更新了,等等。
幻像读(phantom read):这说明,如果你在T1 时间执行一个查询,而在T2 时间再执行这个查询,此时可能已经向数据库中增加了另外的行,这会影响你的结果。与不可重复读的区别在于:在幻像读中,已经读取的数据不会改变,只是与以前相比,会有更多的数据满足你的查询条件。
基于上述三种现象,ANSI 和 ISO/IEC 定义了四种隔离级别,这四种隔离级别如下:
- 读未提交(Read Uncommitted)
- 读已提交(Read Committed)
- 可重复读(Repeatable Read)
- 可串行化(Serializable)
虽然主流的数据源产品也有参考各自实现了全部或部分事务隔离级别,不过他们在概念上或者实现方式上还是存在一定差异,所以在数据库选型异构迁移改造时,尤其要注意该部分,防止高并发时出现了与原系统不一致的结果。
实际上除了以上三种现象问题,还有一些扩展问题在《A Critique of ANSI SQL Isolation Levels》论文提出, 同时有些数据库还实现了快照(snapshot)隔离级别
更新丢失(lost update):一个事务在读取元组并更新该元组的过程中,有另一个事务修改了该元组的值,导致最终这次修改丢失。
读偏斜(read skew):假设数据x,y有隐式的约束x+y=100;事务一读取x=50;事务二写x=25并更新y=75保证约束成立,事务二提交,事务一再读取y=75,导致事务一中读取x+y=125,不满足约束。
写偏斜(write skew):假设数据x,y有隐式的约束x+y<=100;事务一读取x=50,并写入y=50;事务二读取y=30并写入x=70,并提交;事务一再提交;最终导致x=70,y=50不满足x+y<=100的约束。
整理了以下扩展列表
隔离级别 | P0:脏写 | P1:脏读 | P4:更新丢失 | P2:不可重复读 | P3:幻读 | A5A:读偏斜 | A5B:写偏斜 |
读未提交 | 不可能 | 可能 | 可能 | 可能 | 可能 | 可能 | 可能 |
读已提交 | 不可能 | 不可能 | 可能 | 可能 | 可能 | 可能 | 可能 |
可重复读 | 不可能 | 不可能 | 不可能 | 不可能 | 可能 | 不可能 | 不可能 |
快照一致性读 | 不可能 | 不可能 | 不可能 | 不可能 | 偶尔 | 不可能 | 可能 |
可串行化 | 不可能 | 不可能 | 不可能 | 不可能 | 不可能 | 不可能 | 不可能 |
对标隔离级别名称主流数据库支持
隔离级别\数据库 | ORACLE | PostgreSQL | MySQL(InnoDB) | 达梦 | openGauss | Oceanbase | Tidb |
读未提交 | 不支持 | 不支持 | 支持 | 支持 | 不支持 | 不支持 | 不支持 |
读已提交 | 支持(默认) | 支持(默认) | 支持 | 支持(默认) | 支持(默认) | 支持(默认) | 支持(V4) |
可重复读 | 不支持* | 支持 | 支持(默认) | 支持 | 支持 | 支持 | 支持*(默认)(实际是快照) |
可串行化 | 支持 | 支持 | 支持 | 支持 | 不支持 | 支持 | 不支持 |
细节区别
**Oracle)
Oracle 明确地支持READ COMMITTED(读已提交)和SERIALIZABLE(可串行化)隔离级别,不过,这还不是全部。SQL 标准试图建立多种隔离级别,从而允许在各个级别上完成的查询有不同程度的一致性。REPEATABLE READ(可重复读)可以保证由查询得到读一致的(read-consistent)结果。READ COMMITTED 不能提供一致的结果,而READ UNCOMMITTED(读未提交)级别用来得到非阻塞读(non-blocking read)。 不过,在Oracle 中,READ COMMITTED 则有得到读一致查询所需的所有属性。
如在Oracle中查询不会被同一个数据的更新所阻塞,也不会因为查询而阻塞同一数据的更新。Oracle 不需要脏读来达到这个目的,而且也不支持脏读。
在Oracle 中, 由于使用多版本和读一致查询,Oracle 也可以按查询开始时数据的样子对已修改的数据进行重构(从undo),恢复其“本来面目”。
除了4 个已定义的SQL 隔离级别外,Oracle 还提供了另外一个级别,称为READ ONLY( 只读)。 如果事务使用READ ONLY隔离级别,只能看到事务开始那一刻提交的修改,但是插入、更新和删除不允许采用这种模式 ,可以得到REPEATABLE READ 和SERIALIZABLE级别的隔离性。
**OceanBase)
OceanBase 在oracle和MySQL租户支持除脏读以外的三种标准,其中可串行化隔离级别类似 Oracle 数据库的 Serializable,并非严格意义上的 Serializable。
** TiDB)
TiDB 实现了快照隔离 (Snapshot Isolation, SI) 级别的一致性。对标PostgreSQL 和 MySQL 的:可重复读 (Repeatable Read) 或 快照隔离 (Snapshot Isolation)。该隔离级别不同于 ANSI 可重复读隔离级别和 MySQL 可重复读隔离级别。参数 transaction\_isolation 只是为了兼容 MySQL,在 TiDB 无实际意义。对标Oracl 可序列化 (Serializable)。
1.与 ANSI 可重复读隔离级别的区别
尽管名称是可重复读隔离级别,但是 TiDB 中可重复读隔离级别和 ANSI 可重复隔离级别是不同的。按照 A Critique of ANSI SQL Isolation Levels 论文中的标准,TiDB 实现的是论文中的快照隔离级别。该隔离级别不会出现狭义上的幻读 (A3),但不会阻止广义上的幻读 (P3),同时,SI 还会出现写偏斜,而 ANSI 可重复读隔离级别不会出现写偏斜,会出现幻读。
2. 与 MySQL 可重复读隔离级别的区别
MySQL 可重复读隔离级别在更新时并不检验当前版本是否可见,也就是说,即使该行在事务启动后被更新过,同样可以继续更新。这种情况在 TiDB 使用乐观事务时会导致事务回滚,导致事务最终失败,而 TiDB 默认的悲观事务和 MySQL 是可以更新成功的。
Note:
以上内容整理自互联网与个人理解,后期版本迭代有可能发生改变,后期更新。
对不起,这篇文章暂时关闭评论。