隔离等级与隔离策略
严格的隔离级别直接影响并发性。因此,允许并发处理的隔离要求经常被放宽。
ISO/ANSI SQL 标准定义了四个隔离级别,它们提供不同的和递增的隔离级别。
这四个级别如下:
-
Read Uncommitted(读取未提交)
-
Read Committed (读取已提交)
-
Repeatable Read (可重复读取)
-
Serializable (可序列化)
此外,没有隔离或完全混乱可以被视为第五级隔离。
使用示例可以清楚地解释隔离级别。参考一个简单的数据集合(或 RDBMS 世界中的表) ,
如表所示。
| ID | NAME | OCCUPATION | LOCATION(CITY) 1 |
|---|---|---|---|
| 1 | James Joyce | Author | New York 2 |
| 2 | Hari Krishna | Developer | San Francisco 3 |
| 3 | Eric Chen | Entrepreneur | Boston |
现在,假设有两个独立的事务,事务1和事务2并发地操作这个数据集:
1.事务1读取集合中的所有三个数据点。
2.然后,事务2使用 id 2读取数据点,并将该数据项的 Location (City)属性从“ San Francisco”更新到“ San Jose”但是,它不提交更改。
3.事务1重新读取集合中的所有三个数据点。 事务2回滚在步骤2中执行的更新.根据隔离级别的不同,结果会有所不同。
如果隔离级别设置为 Read Uncommit 则事务1将在步骤3中看到事务2(从步骤2开始)更新的但未提交的更改。与步骤4一样,这种未提交的更改可以回滚,因此,这种读操作被恰当地称为脏读操作。
如果隔离级别稍微严格一些,并且设置为下一个级别 Read Commit
操作 1在重新读取步骤3中的数据时不会看到未提交的更改。
现在,交换步骤3和4以及事务2提交更新的情况。新的步骤如下:
- 事务1读取集合中的所有三个数据点。
2.然后,事务2使用 id 2读取数据点,并将该数据项的 Location (City) 属性从“ San Francisco”更新到“ San Jose”但是,它还没有提交更改。
3.事务2提交在步骤2中执行的更新。
4.事务1重新读取集合中的所有三个数据点。
读取未提交隔离级别不受步骤更改的影响。这个级别允许脏读操作,所以显然提交的更新可以毫无困难地读取。但是,读取提交的行为不同。
现在,因为已经在步骤3中提交了更改,所以事务1读取步骤4中更新的数据。步骤1和步骤4的读取不相同,因此这是一种不可重复读取的情况。
当隔离级别升级到可重复读取时,步骤1和步骤4中的读取是相同的。也就是说,事务1与事务2中提交的更新隔离开来,而它们都是并发进行的。
尽管在这个级别上保证了可重复读取,但是可能会发生相关记录的插入和删除。这可能导致在后续读取中包含和排除数据项,通常称为幻像读取。
要通过幻像读取的情况下参考一个新的步骤序列如下:
1.事务1运行一个范围查询,要求所有 id 在1到5之间的数据项(包括这两个数据项)。因为集合中最初有三个数据点,并且都满足条件,所以返回所有三个数据点。
2.然后,事务2插入一个具有以下值的新数据项:
{ Id = 4,Name = ‘ Jane Watson’,Occupation = ‘ Chef’,Location (City) = ‘ Atlanta’}
3.事务2提交步骤2.4中插入的数据。
现在,将隔离设置为可重复读级别后,在步骤1和步骤4中返回给事务1的数据集就不一样了。
步骤4除了原来的三个数据点之外,还查看 id 为4的数据项。
为了避免幻象读取,需要涉及读取的范围锁定,并使用最高级别的隔离 Serializer。序列化这个术语意味着事务的顺序处理或序列排序,但并不总是这样。
但是,当其中一个并发事务处理数据范围时,它确实阻塞了其他并发事务。在一些数据库中,快照隔离被用来实现可序列化的隔离。这样的数据库在启动时提供带有快照的事务,并且只有在快照后没有任何更改时才允许提交。
使用更高的隔离级别增加了饥饿和死锁的可能性。当一个事务将其他事务的资源锁定不让其他事务使用时,就会发生; 当两个并发事务相互等待完成并释放一个资源时,就会发生死锁。
本文正在参加「金石计划 . 瓜分6万现金大奖」