数据库事务隔离
in sqlCo-De with 0 comment

数据库事务隔离

in sqlCo-De with 0 comment

很多数据库管理系统(DBMS)定义了不同的“事务隔离等级”来控制锁的程度。在很多数据库系统中,多数的事务都避免高等级的隔离等级(如可串行化)从而减少锁的开销。程序员需要小心的分析数据库访问部分的代码来保证隔离级别的降低不会造成难以发现的代码bug。相反的,更高的隔离级别会增加死锁发生的几率,同样需要编程过程中去避免。

由于更高的隔离级别中不存在被一个更低的隔离级别禁止的操作,DBMS被允许使用一个比请求的隔离级别更高的隔离级别。

Mysql/InnoDB标准参考:事务隔离级别

ANSI/ISO SQL定义的标准隔离级别如下:

可串行化

可串行化(SERIALIZABLE)是最高的隔离级别。

在基于锁机制并发控制的DBMS上,可串行化要求在选定对象上的读锁和写锁直到事务结束后才能释放。在SELECT的查询中使用一个“WHERE”子句来描述一个范围时应该获得一个“范围锁”(range-locks)。这种机制可以避免“幻影读”现象。

当采用不基于锁的并发控制时不用获取锁。但当系统探测到几个并发事务有“写冲突”的时候,只有其中一个是允许提交的。这种机制的详细描述见“快照隔离”

可重复读

在可重复读(REPEATABLE READS)隔离级别中,基于锁机制并发控制的DBMS需要对选定对象的读锁(read locks)和写锁(write locks)一直保持到事务结束,但不要求“范围锁”,因此可能会发生“幻影读”。
B开启事务,统计本月收入,一共读取到100条记录,求和为1000,此时A虽然无法修改100条记录中的任意一条,但是可以提交一条新的属于本月的收入记录100。最终本月实际收入应该为101条记录,1100,但B的统计结果为100条记录,总金额1000,B的读取发生了幻觉,少读取了一条,数据不一致,本质范围内数据变化无法在事务中控制

提交读

在提交读(READ COMMITTED)级别中,基于锁机制并发控制的DBMS需要对选定对象的写锁一直保持到事务结束,但是读锁在SELECT操作完成后马上释放(因此“不可重复读”现象可能会发生,见下面描述)。和前一种隔离级别一样,也不要求“范围锁”。
B开启事务,读取当前数据为70,即将执行数据扣减70的写操作前,此时A修改数据为50并提交事务,B的写操作会基于50进行扣减,最终数据为-20,但B认为是0,数据不一致,即数据不支持同一事务中重复读取。

未提交读

未提交读(READ UNCOMMITTED)是最低的隔离级别。允许“脏读”(dirty reads),事务可以看到其他事务“尚未提交”的修改。
A开启事务,执行数据修改,将数据改为80,但事务未提交,此时B执行数据读取,能够读取到未提交的数据=80的,然后A执行数据回滚,数据改为70。最终数据为70,但b读取到了未提交的80的,以为是80,数据不一致。

通过比低一级的隔离级别要求更多的限制,高一级的级别提供更强的隔离性。标准允许事务运行在更强的事务隔离级别上。(如在可重复读隔离级别上执行提交读的事务是没有问题的)

默认隔离级别

不同的DBMS默认隔离级别也不同。大多数据库允许用户设置隔离级别。有些DBMS在执行一个SELECT语句时使用额外的语法来获取锁(如SELECT ... FOR UPDATE来获得在访问的数据行上的排他锁)。

隔离级别、读现象和锁

隔离级别vs读现象

隔离脏读不可重复读幻影读
未提交读可能发生可能发生可能发生
提交读 可能发生可能发生
可重复读 可能发生
可序列化

值得一提的是,避免以上三种现象虽然可以满足 ANSI 对可串行化(Serializable)级别的定义,但是其并非真正的可串行化,不能保证执行效果和串行执行完全一致,可能会出现串行化异常,例如写偏差和只读事务偏差。

隔离级别vs 锁持续时间

在基于锁的并发控制中,隔离级别决定了锁的持有时间。"C"-表示锁会持续到事务提交。 "S" –表示锁持续到当前语句执行完毕。如果锁在语句执行完毕就释放则另外一个事务就可以在这个事务提交前修改锁定的数据,从而造成混乱。

隔离写操作读操作范围操作(...WHERE...)
未提交读C--
提交读CS-
可重复读CCS
可序列化CCC
Comments are closed.