在今天的文章里,我想詳細討論下內存中OLTP里的事務日志如何寫入事務日志。我們都知道,對于你的內存優化表(Memory Optimized Tables),內存中OLTP提供你2個持久性(durability)選項:
今天我不想更多討論SCHEMA_ONLY,因為使用這個選項,在事務日志里沒有發生任何日志(SQL Server 重啟后你的數據會丟失)。今天我們會專門講解下SCHEMA_AND_DATA選項的持久性。
SCHEMA_AND_DATA使用SCHEMA_AND_DATA持久性選項,SQL Server必須記錄你的事務到事務日志,因為每個內存中OLTP事務必須總是持久的。這個和傳統基于硬盤表是一樣的。但是內存中OLTP里寫入事務日志比傳統表更優化。內存中OLTP支持多個并發日志流(在SQL Server 2014里當前未實現),內存中OLTP只記錄發生的邏輯事務(logical transaction)。
邏輯事務是什么意思呢?假設你有5個非聚集索引定義的聚集表。如果你往表里插入1條記錄,SQL Server必須記錄插入到聚集索引,還有5個額外的插入非聚集索引。在你表上定義的非聚集索引越多,SQL Server需要的日志越多。而且SQL Server只能和事務日志一樣快。
使用內存中OLTP事情就變了。內存中OLTP,SQL Server只記錄在你事務里發生的邏輯修改。SQL Server對在你哈?;蚍秶饕锏男薷牟挥涗洝R虼?條日志記錄只描述發生的邏輯INSERT/UPDATE/DELETE語句。結果是,內存中OLTP寫入更少的數據到你的事務日志,因此你的事務可以更快的提交。
我們來驗證它!我想用1個簡單的例子向你展示下,當你首先插入10000條記錄到傳統基于硬盤表(Disk Based Table),然后插入內存優化表(Memory Optimized Table),SQL Server會有多少的數據寫入你的事務日志。下列代碼創建1個簡單表,在while循環里插入10000條記錄。然后我用sys.fn_dblog系統函數(未文檔公開)來看事務日志。
1 -- Create a Disk Based table 2 CREATE TABLE TestTable_DiskBased 3 ( 4 Col1 INT NOT NULL PRIMARY KEY, 5 Col2 VARCHAR(100) NOT NULL INDEX idx_Col2 NONCLUSTERED, 6 Col3 VARCHAR(100) NOT NULL 7 ) 8 GO 9 10 -- Insert 10000 records into the table11 DECLARE @i INT = 012 13 BEGIN TRANSACTION14 WHILE (@i < 10000)15 BEGIN16 INSERT INTO TestTable_DiskBased VALUES (@i, @i, @i)17 18 SET @i += 1 19 END20 COMMIT21 GO22 23 -- SQL Server logged more than 20000 log records, because we have 2 indexes24 -- defined on the table (Clustered Index, Non-Clustered Index)25 SELECT * FROM sys.fn_dblog(NULL, NULL)26 WHERE PartitionId IN27 (28 SELECT partition_id FROM sys.partitions29 WHERE object_id = OBJECT_ID('TestTable_DiskBased')30 )31 GO
從系統函數的輸出你可以看到,你有比20000多一點的日志記錄。這是正確的,因為我們在表上有2個索引定義(1個聚集索引,1個非聚集索引)。
現在我們來看下用內存優化表(Memory Optimized Table)日志記錄有啥改變。下列代碼展示了為內存中OLTP數據庫必備工作:我們只加了1個新的內存優化文件組(Memory Optimized File Group),給它加了個容器:
1 --Add MEMORY_OPTIMIZED_DATA filegroup to the database. 2 ALTER DATABASE InMemoryOLTP 3 ADD FILEGROUP InMemoryOLTPFileGroup CONTAINS MEMORY_OPTIMIZED_DATA 4 GO 5 6 USE InMemoryOLTP 7 GO 8 9 -- Add a new file to the previously created file group10 ALTER DATABASE InMemoryOLTP ADD FILE11 (12 NAME = N'InMemoryOLTPContainer', 13 FILENAME = N'C:/Program Files/Microsoft SQL Server/MSSQL12.MSSQLSERVER/MSSQL/DATA/InMemoryOLTPContainer'14 )15 TO FILEGROUP [InMemoryOLTPFileGroup]16 GO
下一步我創建了1個新的內存優化表(Memory Optimized Table)。這里我在哈希索引上選擇了16384的桶數來避免哈希碰撞(hash collisions)的可能。另外我在Col2和Col3列上創建了2個范圍索引(Range Indexes)。
1 -- Creates a Memory Optimized table 2 CREATE TABLE TestTable_MemoryOptimized 3 ( 4 Col1 INT NOT NULL PRIMARY KEY NONCLUSTERED HASH WITH (BUCKET_COUNT = 16384), 5 Col2 VARCHAR(100) COLLATE Latin1_General_100_Bin2 NOT NULL INDEX idx_Col2, 6 Col3 VARCHAR(100) COLLATE Latin1_General_100_Bin2 NOT NULL INDEX idx_Col3 7 ) WITH 8 ( 9 MEMORY_OPTIMIZED = ON, 10 DURABILITY = SCHEMA_AND_DATA11 )12 GO
在那個表上合計有3個索引(1個哈希索引(Hash Index)和2個范圍索引(Range Indexes))。當你往表里插入10000條記錄,傳統表會生成近30000的日志記錄——每個索引里每個插入1條日志。
1 -- Copy out the highest 'Current LSN' 2 SELECT * FROM sys.fn_dblog(NULL, NULL) 3 GO 4 5 -- Insert 10000 records into the table 6 DECLARE @i INT = 0 7 8 BEGIN TRANSACTION 9 WHILE (@i < 10000)10 BEGIN11 INSERT INTO TestTable_MemoryOptimized VALUES (@i, @i, @i)12 13 SET @i += 1 14 END15 COMMIT16 GO17 18 -- Just a few log records!19 SELECT * FROM sys.fn_dblog(NULL, NULL)20 WHERE [Current LSN] > '0000002f:000001c9:0032' -- Highest LSN from above21 GO
但現在當你看事務日志時,你會看到10000條插入只生成了很少日志記錄——這里是17條!
魔法發生在LOP_HK日志記錄里。在這個特定日志記錄里,內存中OLTP捆綁了多個修改到你的內存優化表(Memory Optimized Table)。你也可以通過使用sys.fn_dblog_xtp系統函數分解LOP_HK日志記錄:
1 -- Let's look into a LOP_HK log record2 SELECT * FROM sys.fn_dblog_xtp(NULL, NULL)3 WHERE [Current LSN] > '0000002f:000001c9:0032' AND Operation = 'LOP_HK'4 GO
從圖中你可以看到,內存中OLTP只生成了近10000條LOP_HK日志記錄——在這個表上發生的每個邏輯插入對應1條記錄。
小結內存中OLTP提供你驚艷的性能提升,因為它是基于全新原理,例如MVCC和Lock-Free Data Structrues。另外它只生成少量事務日志記錄,因為只有邏輯改變被記錄寫入事務日志。我希望這個文章已給你更好的理解:內存中OLTP如何提升你的事務日志吞吐量。
感謝關注!
新聞熱點
疑難解答