SQL Server讀提交快照隔離級別的注意事項

前言


當數據庫中存在大量面積LCK_M_S 共享鎖的時候,我們經常會選擇使用讀提交快照隔離級別,來解決阻塞的問題.但是所有技術都是使用場景的。在下面的場景下,使用讀提交快照,就可能出現錯誤。

正文


從SQL Server 2005開始,我們就有了READ COMMITTED SNAPSHOT隔離級別(RCSI)和快照隔離級別(SI)。當使用這些新的隔離級別時,SELECT語句在閱讀過程中不會獲得S共享鎖。 (UPDATE,DELETE語句)將他們正在更改的記錄的舊版本,存入爲TempDb。 他們正在創建一個版本鏈,其中記錄的實際版本(存儲在數據庫中的數據頁面上)指向存儲在TempDb頁面上的舊版本。 下圖顯示了這個概念。



爲了實現上面的功能,SQL Server必須爲數據庫中的數據頁面上的每個記錄添加一個14字節長的指針。 這意味着每個記錄的長度將延長14字節。 您可能已經知道,當您使用固定長度的數據類型時,SQL Server中的記錄不能超過8060字節。 這意味着啓用RCSI / SI可能導致這些記錄大於8060字節。 讓我們來看一個非常簡單的例子
USE master
GO
 
-- Create a new database
CREATE DATABASE VersionStoreRestrictions
GO
 
-- Enable RCSI
ALTER DATABASE VersionStoreRestrictions SET READ_COMMITTED_SNAPSHOT ON
GO
 
-- Use it
USE VersionStoreRestrictions
GO
 
-- Create a table where each record is 8047 bytes large
CREATE TABLE TableB
(
   Column1 CHAR(40),
   Column2 CHAR(8000)
)
GO



從前面的代碼中可以看到,我創建了一個表CHAR類型,加起來長度爲8040字節。對於每個記錄,SQL Server內部需要至少7個字節的開銷。在這種情況下,一行記錄需要8047字節的數據頁。因爲我們在數據庫級別上啓用了RCSI,所以SQL Server必須爲行版本指針添加額外的14個字節,將表中的每個記錄擴展到8061個字節。這意味着每個記錄對都會超出SQL Server 規定的8060 (1 byte)
首先,我們將一個記錄插入到表格中
-- Insert a initial row
INSERT INTO TableB VALUES (REPLICATE('A', 40), REPLICATE('A', 8000))
GO




然後,執行下面的update
UPDATE TableB
SET Column1 = REPLICATE('B', 40)
GO

Msg682,Level22,State214,Line2
Internalerror.Bufferprovidedtoreadcolumnvalueistoosmall.RunDBCCCHECKDBtocheckforanycorruption.

是一個內部錯誤,因爲SQL Server使用的緩衝區只有8060字節大,現在我們嘗試存儲在緩衝區8061字節 - SQL Server 2008上還可以重現。 有趣的是,SQL Server Denali CTP1(就是SQL Server 2012)已經修復了這個錯誤,其中頁面轉儲顯示SQL Server存儲預期的8061字節。
當您爲現有數據庫啓用RCSI / SI時,請記住這個錯誤,因爲這意味着RCSI / SI不是在任何場景都適用。 當您的數據庫中有一個超過8046字節限制的表格時,您將遇到真正的麻煩

總結

除了上述問題,讀提交快照還會增加tempdb的壓力。這也是需要考慮的一個因素。

發佈了76 篇原創文章 · 獲贊 101 · 訪問量 18萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章