关于数据库:Oracle-事物隔离级别那些事儿

37次阅读

共计 4414 个字符,预计需要花费 12 分钟才能阅读完成。

昨天看到墨天轮小助手的一道对于 Mysql 数据库事务隔离度的问题,突然想到了以前遇到过的一件对于 ORACLE 数据库事务隔离度的事儿,感觉能够帮忙大家加深对于数据库事务隔离的了解,于是整理出来分享给大家。

首先讲一下事务隔离度。
事务隔离度(Transaction isolation models)并不是个别数据库提出的概念,而是一个国际化规范(ANSI and ISO/IEC)。所有数据库(不仅仅是传统的关系数据库,例如 OARCLE,Mysql,SQL Server,也包含 NoSQL 数据库,例如 TiDB,OceanBase 等等)都要在这个规范上设计,区别在于反对哪个(或那些)模式。它标识一个数据库系统能在什么水平上保障“读一致性”的能力。

上面用一个简略的小例子阐明一下事务隔离度(Transaction isolation models)。有两个事物,事物 A 负责更新表 T1,事物 B 负责查看表 T1。

事物 A:
T1.c1=0-> 更新 T1 (c1=1) —> Commit —> 更新 T1 (c1=2) —> Commit->

事物 B:
—– t1 ——————–> t2 ———> t3 —————- >t4 ———>t5

假如事物 B 从 t1 时刻开始。
如果他在 t1 时刻看到 T1.c1=0,t2 时刻看到 T1.c1=1,在 t3 时刻看到 T1.c1=1,在 t4 时刻看到 T1.c1=2,在 t5 时刻看到 T1.c1=2,那么就是 Read uncommitted
如果他在 t1 时刻看到 T1.c1=0,t2 时刻看到 T1.c1=0,在 t3 时刻看到 T1.c1=1,在 t4 时刻看到 T1.c1=1,在 t5 时刻看到 T1.c1=2,那么就是 Read committed
如果他在 t2 时刻看到 T1.c1=0,t2 时刻看到 T1.c1=0,在 t3 时刻看到 T1.c1=1,在 t4 时刻看到 T1.c1=1,在 t5 时刻看到 T1.c1=1,那么就是 Repeatable read
如果他在 t2 时刻看到 T1.c1=0,t2 时刻看到 T1.c1=0,在 t3 时刻看到 T1.c1=0,在 t4 时刻看到 T1.c1=0,在 t5 时刻看到 T1.c1=0,那么就是Serializable read

而后来答复一下墨天轮小助手的问题。
MySQL 数据库是全面反对下面 4 种事务隔离度的,默认隔离度是“Repeatable read”。

应用命令:SET session TRANSACTION ISOLATION LEVEL xxxx; 能够批改事务隔离度。
(参数能够为:Read uncommitted,Read committed,Repeatable,Serializable)

那么 ORACLE 数据库呢?

ORACLE 数据库只反对 Read committed 和 Serializable read 两种事务隔离度,默认隔离度是“Read committed”。

参考文档:Master Note: Oracle Transaction Management (Local) Overview (ドキュメント ID 1506115.1)
------------------------------------------------------------------------------------------------------------------
Oracle database provides Read committed and Serializable isolation levels with "Read committed" as the default. In addition,
Oracle database also provides another isolation level - Read-only isolation level,
which is similar to the Serializable level but doesn't allow DML statements in the transaction(except for SYS).
These isolation levels can be set at the session level using the "SET TRANSACTION..." command
------------------------------------------------------------------------------------------------------------------

那这件事儿到底有啥理论影响呢?给大家讲一个这样的事例。

ORACLE 数据库里有一种叫做“Materialized View”(物化视图)的 Object。它提供一种能够主动或手动的把一个表的数据同步到另外一个表(物化视图)的办法。这个同步的过程就叫做“Refresh”。

如果有很多物化视图须要手动 Refresh 时,一个一个的刷新显然是比拟麻烦的,于是 ORACLE 提供了把多个物化视图编组(Group),而后一起 Refresh 的办法。

ORACLE 数据库还有一种叫做“Foreign Key”(外键束缚)的货色。它提供一种两个表之间的数据束缚机能。具体是怎么束缚的,置信大家都晓得,这篇文章里不再赘述。咱们只须要晓得主表中没有的数据是不可能在外键表中存在的。

当初有这样一个场景:

1. 有两个具备外键束缚的表:T1 是主表,T2 是外键表。2. 这两个表都有一个物化视图:MV10 和 MV20。3. 有一个解决给 T1 和 T2 插入记录。因为 T1,T2 之间存在外键束缚,必须先对 T1 插入数据,Commit。而后对 T2 插入数据,Commit。4. 在解决 3 进行的同时,另外一个 Session 对物化视图 Group MV10 ~ MV20 进行 Refresh。

大家想一下,在下面的场景中,有没有可能主表 T1 的物化视图 MV10 不存在,而在外键表 T2 的物化视图 MV20 中存在的记录呢??

答案是:有可能。
因为 Mview Group 进行 Refresh 时,刷新程序是 Mview 名的字母顺。在下面的场景中就是先刷新 MV1,再刷新 MV2

参考文档:Materialized Views (MVIEWs) Refresh Order Using "DBMS_SNAPSHOT.REFRESH" LIST Parameter; 9i Vs 10g and Higher Versions (ドキュメント ID 1452382.1)
------------------------------------------------------------------------------------------------------------------
From 10g onwards, it refreshes in alphabetical order, i.e the refresh starts with "MVIEWa"
instead of "MVIEWi" and ends with "MVIEWk" instead of "MVIEWa".  
Because of this, dependency MVIEWs are not getting correct data.
------------------------------------------------------------------------------------------------------------------

为了解释下面的起因,我还是画这样的图例:
事物 3:—> 更新 T1 (insert into t1 values(1);) —> Commit —> 更新 T2 (insert into t2 values(1);) —> Commit —>
事物 4:—-> Refresh MV10 —-> Refresh MV11 —-> Refresh MV12—–> …略… —–> Refresh MV19——> Refresh MV20——–>

因为 ORACLE 数据库的默认事务隔离度是“Read committed”。
也就是说刷新主表 T1 的物化视图 MV10 时,主表 T1 并没有 Commit,事物 4 看不见事物 3 的更新。等到刷新外键表 T2 的物化视图 MV20 时,主表 T1 和外键表 T2 都曾经 Commit,事物 4 看见了事物 3 的更新。

所以,下面的景象看起来是不合理的(外键表的 Mview 里有数据而主表的 Mview 里没有数据),但却是合乎式样动作的(expected behavior)。

依据下面的事项,ORACLE 最初也颁布了官网文档。

参考文档:MVIEW of Child Table is Refreshed but MVIEW of Parent Table with Foreign Key Constraint is not Refreshed (ドキュメント ID 2697569.1)
------------------------------------------------------------------------------------------------------------------
Cause
It's an expected behavior, as describe in Doc ID 1452382.1
If a group of materialized views are refreshing with "DBMS_SNAPSHOT.REFRESH" LIST Parameter,
Oracle will refresh the MVIEWs in alphabetical order.

So if the Data inserts and Mview refresh operations are at the same,
Because of the commit and refresh timing, MVIEW of Parent Table may be not Refreshed with some new data.

Reference:
Materialized Views (MVIEWs) Refresh Order Using "DBMS_SNAPSHOT.REFRESH" LIST Parameter; 9i Vs 10g and Higher Versions (Doc ID 1452382.1)

Solution
Don't Insert data to base table and Refresh the mview at the same time.
------------------------------------------------------------------------------------------------------------------

然而这个问题也能够通过设置事物 4 的 SESSION 隔离度来回避。

例如:
set transaction isolation level serializable;

正文完
 0