参考Wikipedia: http://en.wikipedia.org/wiki/Isolation_(database_systems)#Repeatable_reads_.28phantom_reads.29
Isolation的理想状态是:the result of Transaction A is invisible to Transaction B until Transaction A is completed。不过这么一来,只有Serializable级别才能满足要求,即Transactions是一个接一个地执行,不允许Transactions的并行。如果想要Transactions的并行,那么就不可能有绝对的isolation。为此,ANSI/ISO制定的SQL标准给Isolation分了4个级别,由高到低分别是:
级别越高的,isolation性越好,而低级别的isolation会有各种各样的并发问题(如上面括号中所示)。下面一一介绍。
1. Read Uncommitted
Transaction 1可以查看Transaction 2未提交的操作结果,这会导致Dirty Read的问题,如下图所示:
Transaction 1的执行的第二个query会读到Transaction 2中update的结果,如果Transaction 2 rollback的话,这个读到的数据明显是错误的。
2. Read Committed
如果我们强制Transaction 1只能读Transaction 2中已经commit的update,就可以解决Dirty Read的问题。强制的手段是:每次query(比如SELECT)时,都会获取当前数据库(或者可能只是query涉及的表)的一个snapshot,query从这个snapshot中获得结果。没有commit的update不会在snapshot中出现,所以就不会被query到。
这样允许Transaction 2并行执行,且不会影响Transaction 1。当Transaction 1 commit的时候,DBMS检测是否有冲突(比如Transaction 1也update了Transaction 2中update的row),如果执行的结果等同于顺序执行Transaction 1和 Transaction 2 ,则认为没有冲突。
但这样也会有问题:Nonrepeatable Read,即可能执行同一query两次而出现不同的结果,我们认为这是不符合一致性原则的。
3. Repeatable Read
解决Nonrepeatable Read问题的办法是给Transaction 1中的SELECT涉及的行加一个read lock。
锁的排斥功能很明确:read locks不会block read locks,只block write locks;write locks block both read locks and write locks。
不过会有新的问题:phantom read。
当Transaction 1执行一个range query(范围查询,如like, between等)时,只会为涉及到的row加read lock,如果插入一个新row在range之内,第二次range query还是会被读出。
4. Serializable
强制Transactions按顺序执行,所以Transaction之间不可能冲突。强制的手段是给place a read lock on every row the transaction read;同时,如果有range query,还会添加一个range lock。明显,Serializable会耗费很多的lock overhead。
Serializable相当于在Transaction 1开始时(而不是query开始时)给当前数据库(或是Transaction涉及的表)take a snapshot,Transaction 1中的所有query都从这一个snapshot中获取结果。