----------------------------我是分割線-------------------------------
本文翻譯自微軟白皮書《SQL Server In-Memory OLTP Internals Overview》:http://technet.microsoft.com/en-us/library/dn720242.aspx
譯者水平有限,如有翻譯不當之處,歡迎指正。
----------------------------我是分割線-------------------------------
事務隔離和并發管理正如之前所介紹的 ,所有對 內存優化表數據的訪問都是使用完全的樂觀并發控制實現的,但也允許使用其他多個事務隔離級別進行訪問。然而,在什么樣的情況下允許什么樣的隔離級別,看起來似乎有點混亂和不直觀。需要考慮的隔離級別是那些涉及到交叉容器事務的隔離級別,這意味著任何引用內存優化表的解釋型查詢是來自于一個顯式還是隱式的事務,或者是否處于自動提交模式下執行。在交叉容器事務中可以用于內存優化表的隔離級別取決于事務已經為SQL Server事務定義了什么隔離級別。大部分的限制都與一個事實有關,也就是即使是在同一個Transact-SQL事務中訪問基于磁盤的表和內存優化表,基于磁盤的表上的操作與內存優化表上的操作各自都有它們自己的事務序列號。你可以把這種行為看作是一個較大的事務中具有兩個子事務:一個子事務用于基于磁盤的表,一個子事務用于內存優化表。
首先,先籠統的看一下隔離級別的一些背景。這里不會完整的對隔離級別進行討論,這樣的討論已經超出了本文的范圍。隔離級別可以從保證的一致性屬性來進行定義。最重要的屬性如下:
然后,我們可以基于這些屬性來定義事務隔離級別。以下列出的第一個事務隔離級別(SNAPSHOT)沒有提到這些屬性,但后兩個都提到了。
這個隔離級別規定,在一個事務中任何語句讀取的數據與事務開始時存在的數據是事務一致的版本。事務只能夠識別出在事務開始前已提交數據的修改。在當前事務開始后由其他事務進行的數據修改對于在當前事務中執行的語句都是不可見的。事務中的語句獲得一個已提交數據的快照與事務開始時所存在的相同。
這個隔離級別包括了SNAPSHOT隔離級別所提供的保證。此外,REPEATABLE READ還保證了讀可靠性。對于事務讀取的任何行,在事務提交時行還沒有被其他事務更改。事務中的每一個讀操作在事務結束前都是可重復的。
這個隔離級別包括了REPEATABLE READ隔離級別所提供的保證。此外,SERIALIZABLE還保證了幻影回避。在事務中的操作不會錯過任何行。在快照時間和事務結束之間不會出現幻影行?;糜靶蟹蟂ELECT/UPDATE/DELETE的過濾條件。如果我們能夠保證,在事務結束時所有重復的讀取都可以看到完全相同的數據,那么這個事務則是可序列化的。
最簡單和最廣泛使用的多版本并發控制的方法是快照隔離(snapshot isolation ,SI),但快照隔離并不保證可序列化,因為邏輯上讀取和寫入在不同時間發生,讀取是在事務開始時發生,而寫入是在事務結束時發生。
訪問基于磁盤的表還支持READ COMMITTED隔離,它只是保證該事務將不會讀取任何臟(未提交)數據。訪問內存優化表需要使用上述的三種隔離級別之一。表1列出了在一個交叉容器事務中可以一起使用的隔離級別。
基于磁盤的表 | 內存優化表 | 建議 |
READ COMMITTED | SNAPSHOT | 這是基準組合,應該用于當前使用 READ COMMITTED 的大多數場景。 |
READ COMMITTED | REPEATABLE READ / SERIALIZABLE | 這個組合可以在數據遷移期間使用,以及用于在互操作模式下(而不是在一個本地編譯存儲過程中)對內存優化表的訪問。 |
REPEATABLE READ / SERIALIZABLE | SNAPSHOT | 對內存優化表的訪問只有插入操作。這個組合在進行遷移并且如果在內存優化表上沒有進行并發寫操作時也是有用的。 |
SNAPSHOT | - | 不允許訪問內存優化表(參見注釋1) |
REPEATABLE READ / SERIALIZABLE | REPEATABLE READ / SERIALIZABLE | 不允許這種組合 (參見注釋2) |
表1 交叉容器事務中的兼容隔離級別
注釋1:對于SNAPSHOT隔離,所有的操作都需要看到從事務開始時就存在的數據的版本。對于SNAPSHOT隔離,事務的開始是以訪問第一個表的時間來計算的。但在交叉容器事務中,由于每個子事務可以在不同的時間點開始,在兩個子事務的開始時間之間另一個事務可能已經改變了數據。交叉容器事務則沒有快照所依據的一個時間點。
注釋2:兩個子事務(一個在基于磁盤的表和一個在內存優化表)不能都使用REPEATABLE READ或SERIALZABLE的原因是因為這兩個系統以不同的方式實現隔離級別。設想一下圖9中所示的兩個交叉容器事務。
時間點 | 事務1 (SERIALIZBLE) | 事務2 (任何隔離級別) |
1 | 開始SQL或內存中的子事務 | |
2 | 讀取RHk1 | |
3 | 開始SQL或內存中的子事務 | |
4 | 讀取RSql1 并更新為RSql2 | |
5 | 讀取RHk1 并更新為RHk2 | |
6 | 提交 | |
7 | 讀取RSql2 |
表9 兩個并發的交叉容器事務
事務1將首先從內存優化表中讀取數據行,并且將不會保持任何鎖,因此事務2可以完成并更改兩個數據行。當事務1恢復時,當它從基于磁盤的表讀取數據行,現在它將得到關于這兩行的一組值,如果事務是被隔離運行的(即,如果事務是真正的可序列化。)那么這組值則應該不曾存在過。因此這個組合是不允許的。
有關隔離級別的詳細信息,請參閱以下參考材料:
http://en.wikipedia.org/wiki/Isolation_(database_systems)
http://research.microsoft.com/apps/pubs/default.aspx?id=69541
內存優化表的持久性和存儲SQL Server必須確保內存優化表的事務持久性,使得所有已提交的事務的影響可以在發生故障后恢復。內存中OLTP通過采用檢查點進程和事務日志記錄進程寫入到持久存儲來實現這一點。雖然本文并不討論,內存中OLTP還與AlwaysOn可用性組功能集成,這個功能維護了支持故障轉移的高可用性副本。
寫入磁盤的信息由檢查點流和事務日志流所組成。
檢查點流不定時進行。一對檢查點文件,數據和增量文件,代表了事務日志的一個段,通常約含100MB的新數據行版本。
這些檢查點文件對(checkpoint file pairs,CFP)積累并形成了一個完整的檢查點(本節稍后會詳細進行介紹)。最近完成的檢查點加上從檢查點以來最近的事務日志,就足以將內存優化表的內存中狀態恢復到包括所有已提交事務的事務一致時間點。在詳細介紹日志和檢查點文件如何生成并使用之前,這里有幾個需要注意的關鍵點:
內存中OLTP的事務日志記錄針對可擴展性和高性能進行了設計。每個事務都記錄在最少數量的日志記錄中,這些可能很龐大的日志記錄被寫入到SQL Server常規事務日志中。日志記錄包含了關于事務插入和刪除的所有版本的信息。更新則被記錄為舊版本數據行的刪除和新版本數據行的插入。利用這些信息,在恢復過程中可以重做事務。
對于內存中OLTP的事務,只在提交時生成日志記錄。比如,內存中OLTP并不使用處理基于磁盤的表上的操作時使用的預寫日志(WAL)協議。使用預寫日志, SQL Server在將任何更改的數據寫入到磁盤之前先寫日志,甚至寫出在檢查點期間未提交的數據也是有可能發生的。而對于內存中OLTP,臟數據(即未提交的更改)絕不會被寫入到磁盤。此外,內存中OLTP試圖將多個日志記錄組成一個最大到24KB的日志記錄,這能導致實際上寫入更少的日志記錄,以及更大的I/O操作。
與基于磁盤的表上的操作相比,內存中OLTP的操作可以產生更少的日志數據和更少的日志寫入。下面簡單的腳本說明了內存優化表極大地減少了日志記錄。這個腳本將創建一個可以容納內存優化表的數據庫,然后創建兩個類似的表。一個是內存優化表,一個是基于磁盤的表。
USE masterGOIF EXISTS (SELECT * FROM sys.databases WHERE name='LoggingDemo')DROP DATABASE LoggingDemo;GOCREATE DATABASE LoggingDemo ONPRIMARY (NAME = [LoggingDemo_data], FILENAME = 'C:/DataHK/LoggingDemo_data.mdf'),FILEGROUP [LoggingDemo_FG] CONTAINS MEMORY_OPTIMIZED_DATA(NAME = [LoggingDemo_container1], FILENAME = 'C:/DataHK/StorageDemo_mod_container1')LOG ON (name = [hktest_log], Filename='C:/DataHK/StorageDemo.ldf', size=100MB);GOUSE LoggingDemoGOIF EXISTS (SELECT * FROM sys.objects WHERE name='t1_inmem')DROP TABLE [dbo].[t1_inmem]GO-- create a simple memory-optimized tableCREATE TABLE [dbo].[t1_inmem]( [c1] int NOT NULL,[c2] char(100) NOT NULL,CONSTRAINT [pk_index91] PRIMARY KEY NONCLUSTERED HASH ([c1]) WITH(BUCKET_COUNT = 1000000)) WITH (MEMORY_OPTIMIZED = ON,DURABILITY = SCHEMA_AND_DATA);GOIF EXISTS (SELECT * FROM sys.objects WHERE name='t1_disk')DROP TABLE [dbo].[t1_disk]GO-- create a similar disk-based tableCREATE TABLE [dbo].[t1_disk]( [c1] int NOT NULL,[c2] char(100) NOT NULL)GOCREATE UNIQUE NONCLUSTERED INDEX t1_disk_index on t1_disk(c1);GO
接下來,將基于磁盤的表填充了100行數據,并使用未公開的(和不受支持的)函數fn_dblog()檢查事務日志的內容。
BEGIN TRANDECLARE @i int = 0WHILE (@i < 100)BEGIN33INSERT INTO t1_disk VALUES (@i, replicate ('1', 100))SET @i = @i + 1EN
新聞熱點
疑難解答