SQL Server 的早期版本支持 SQL-92 標(biāo)準(zhǔn)中定義的四個(gè)隔離級(jí)別:
READ UNCOMMITTED 是限制性最弱的隔離級(jí)別,因?yàn)樵摷?jí)別忽略其他事務(wù)放置的鎖。 使用 READ UNCOMMITTED 級(jí)別執(zhí)行的事務(wù),可以讀取尚未由其他事務(wù)提交的修改后的數(shù)據(jù)值;這些行為稱為“臟”讀。
READ COMMITTED 是 SQL Server 默認(rèn)的隔離級(jí)別。 該級(jí)別通過(guò)指定語(yǔ)句不能讀取其他事務(wù)已修改但是尚未提交的數(shù)據(jù)值,禁止執(zhí)行臟讀。 在當(dāng)前事務(wù)中的各個(gè)語(yǔ)句執(zhí)行之間,其他事務(wù)仍可以修改、插入或刪除數(shù)據(jù),從而產(chǎn)生無(wú)法重復(fù)的讀操作,或“影子”數(shù)據(jù)。
REPEATABLE READ 是比 READ COMMITTED 限制性更強(qiáng)的隔離級(jí)別。 該級(jí)別包括 READ COMMITTED,并且另外指定了在當(dāng)前事務(wù)提交之前,其他任何事務(wù)均不可以修改或刪除當(dāng)前事務(wù)已讀取的數(shù)據(jù)。 并發(fā)性低于 READ COMMITTED,因?yàn)橐炎x數(shù)據(jù)的共享鎖在整個(gè)事務(wù)期間持有,而不是在每個(gè)語(yǔ)句結(jié)束時(shí)釋放。
SERIALIZABLE 是限制性最強(qiáng)的隔離級(jí)別,因?yàn)樵摷?jí)別鎖定整個(gè)范圍的鍵,并一直持有鎖,直到事務(wù)完成。 該級(jí)別包括 REPEATABLE READ,并增加了在事務(wù)完成之前,其他事務(wù)不能向事務(wù)已讀取的范圍插入新行的限制。
鋪墊了這么多,終于介紹到了今天的主角,"快照”一詞反映的情況是:事務(wù)中的所有查詢根據(jù)事務(wù)開(kāi)始那一刻數(shù)據(jù)庫(kù)的狀態(tài),看到數(shù)據(jù)庫(kù)的相同版本(即快照)。 不會(huì)在快照事務(wù)中的基礎(chǔ)數(shù)據(jù)行或數(shù)據(jù)頁(yè)上獲取鎖,這樣可以執(zhí)行其他事務(wù),而不會(huì)被以前未完成的事務(wù)所阻止。 修改數(shù)據(jù)的事務(wù)不會(huì)阻止讀取數(shù)據(jù)的事務(wù),讀取數(shù)據(jù)的事務(wù)不會(huì)阻止寫入數(shù)據(jù)的事務(wù),就好像通常情況下在 SQL Server 中使用默認(rèn)的 READ COMMITTED 隔離級(jí)別一樣。 這種無(wú)阻止的行為也大大降低了復(fù)雜事務(wù)出現(xiàn)死鎖的可能性。
SQL Server 通過(guò)引入 SNAPSHOT 隔離級(jí)別并另外實(shí)現(xiàn) READ COMMITTED 而引入了對(duì) SQL-92 隔離級(jí)別的擴(kuò)展。READ_COMMITTED_SNAPSHOT 隔離級(jí)別可以透明地替換所有事務(wù)的 READ COMMITTED。
- SNAPSHOT 隔離指定在一個(gè)事務(wù)中讀取的數(shù)據(jù)永遠(yuǎn)不會(huì)反映其他同時(shí)進(jìn)行的事務(wù)所作的更改。
- 事務(wù)使用事務(wù)開(kāi)始時(shí)存在的數(shù)據(jù)行版本。
- 在讀取數(shù)據(jù)時(shí)不會(huì)對(duì)數(shù)據(jù)放置任何鎖,所以,SNAPSHOT 事務(wù)不會(huì)阻止其他事務(wù)寫入數(shù)據(jù)。
- 寫入數(shù)據(jù)的事務(wù)不會(huì)阻止快照事務(wù)讀取數(shù)據(jù)。
啟用快照隔離之后,每個(gè)事務(wù)的已更新行版本在 tempdb 中維護(hù)。 唯一的事務(wù)序列號(hào)標(biāo)識(shí)每個(gè)事務(wù),并且為每個(gè)行版本記錄這些唯一的編號(hào)。 事務(wù)使用序列號(hào)在事務(wù)序列號(hào)之前的最新行版本。 事務(wù)將忽略在事務(wù)開(kāi)始之后創(chuàng)建的更新的行版本。
快照隔離和行版本化的工作原理
啟用 SNAPSHOT 隔離級(jí)別時(shí),每次更新行時(shí),SQL Server 數(shù)據(jù)庫(kù)引擎在 tempdb 中存儲(chǔ)原始行的副本,并為該行添加事務(wù)序列號(hào)。
以下是發(fā)生的事件序列:
- 新的事務(wù)啟動(dòng),并為該事務(wù)分配一個(gè)事務(wù)序列號(hào)。
- 數(shù)據(jù)庫(kù)引擎在事務(wù)中讀取某行,并從 tempdb 中檢索其序列號(hào)與事務(wù)序列號(hào)最接近并且小于事務(wù)序列號(hào)的行版本。
- 數(shù)據(jù)庫(kù)引擎檢查事務(wù)編號(hào)是否不在未提交事務(wù)的事務(wù)編號(hào)列表中,這些未提交事務(wù)是在快照事務(wù)開(kāi)始時(shí)進(jìn)入活動(dòng)狀態(tài)的。
- 事務(wù)從 tempdb 中讀取自事務(wù)開(kāi)始以來(lái)最新的行版本。
事務(wù)不會(huì)看到事務(wù)開(kāi)始后插入的新行,因?yàn)檫@些序列號(hào)值將大于事務(wù)序列號(hào)的值。 - 當(dāng)前事務(wù)將看到事務(wù)開(kāi)始后刪除的行,因?yàn)?tempdb 中的行版本具有更低的序列號(hào)值。
快照隔離的實(shí)際效果是事務(wù)看到在事務(wù)開(kāi)始時(shí)存在的所有數(shù)據(jù),不會(huì)在基礎(chǔ)表上授予或放置任何鎖。 在存在爭(zhēng)用的情況下,這樣可以改進(jìn)性能。
快照事務(wù)始終使用開(kāi)放式并發(fā)控制,不賦予可能阻止其他事務(wù)更新行的任何鎖。 如果快照事務(wù)嘗試提交對(duì)事務(wù)開(kāi)始后已更改的行的更新,事務(wù)將回滾并引發(fā)錯(cuò)誤。