在访问方法的文档中,用户要知道在试图访问同一数据项的进程之间受支持的隔离程度,这一点很重要。这一般是通过采用 SQL-92 隔离级别来定义的。本文对该系统进行了简短且简化的描述。
SQL-92 定义了四个隔离级别:
-
读操作未提交(Read Uncommitted)
-
读操作已提交(Read Committed)
-
可重复读(Repeatable Read)
-
可序列化(Serializable)
隔离级别是根据称为 现象(Phenomena)的三个禁止操作序列来声明的:
-
脏读(Dirty Read)
-
不可重复读(Non-Repeatable Read)
-
幻像读(Phantom Read)
如果访问方法不支持“可序列化”,则符合特定查询的数据集在事务内可能由于另一个事务进行了更新而发生更改。如果发生这样的情况,那么用户要知道受支持的是什么级别,这一点很重要,以便他们可以据此设计应用程序。“现象”定义了符合的数据集在事务中可能更改的方式。
![]() ![]() |
![]() |
如果一个事务在提交操作结果之前,另一个事务可以看到该结果,就会发生这种情况。请考虑以下示例:
事务 1 | Write(a) | Rollback | |
事务 2 | Read(a) |
如果事务 2 读取了事务 1 所写的值,则“脏读”就发生了。
![]() ![]() |
![]() |
如果一个事务在提交结果之前,另一个事务可以修改和删除它,就会发生这种情况。以下演示了这一点:
事务 1 | Read(a) | Read(a) | ||
事务 2 | Write/Delete(a) | Commit |
如果事务 1 每次从 Read 上获取了不同的结果,就会发生“不可重复读”。
![]() ![]() |
![]() |
如果一个事务在提交查询结果之前,另一个事务可以更改该结果,就会发生这种情况。以下演示了这一点:
事务 1 | Select(标准) | Select(标准) | ||
事务 2 | Update/Create(与标准匹配) | Commit |
如果事务 1 每次从 Select 上获取了不同的结果,就会发生“幻像读”。
![]() ![]() |
![]() |
SQL-92 隔离级别是根据发生何种“现象”而定义的:
隔离级别 | P1 | P2 | P3 |
读操作未提交 | 是 | 是 | 是 |
读操作已提交 | 否 | 是 | 是 |
可重复读 | 否 | 否 | 是 |
可序列化 | 否 | 否 | 否 |
因此如果支持“读操作已提交”,则您就决不会遇到“脏读”,但您可能会遇到“不可重复读”或“幻像读”。
上面的次序是根据“强度”排列的,与“读操作已提交”相比,“读操作未提交”受到的限制比较少,因而它的“强度”就比较小,或者说处于(隔离的)“较低级别”。
您的代码可能需要调用 mi_scan_isolevel()以验证是否支持当前请求的级别。使用隔离的较高级别总是有效支持较低级别,但当您使用隔离的较低级别时不能要求得到较高级别的支持。这样的话,如果 DataBlade 支持“可序列化”,则不需要调用 mi_scan_isolevel(),因为所有的级别都低于“可序列化”。
mi_scan_isolevel()记录在 Virtual-Table Interface Programmer's Manual中,而且它返回服务器正期望用于访问方法的隔离级别。加上级别在“读操作已提交”和“可重复读”之间的“游标稳定性”(Cursor Stability),这些返回值与上面解释的 ANSI SQL-92 级别相符合。Informix 服务器支持该级别,但它事实上不是 SQL-92 的一部分。DataBlade 应该通过支持“可重复读”或“可序列化”来支持“游标稳定性”。从 mi_scan_isolevel() 实际返回的值定义在 miami.h 中,如下所示:
- MI_ISO_NOTRANSACTION
- MI_ISO_READUNCOMMITTED
- MI_ISO_ READCOMMITTED
- MI_ISO_ CURSORSTABILITY
- MI_ISO_ REPEATABLEREAD
- MI_ISO_ SERIALIZABLE
如果您正在定义辅助访问方法,而且所有数据都保存在数据库内的表中,则数据库可以保证“可重复读”。但是如果期望支持“可序列化”,则那是您的访问方法的职责了。