SQL Server 內存中OLTP內部機制概述(一)


----------------------------我是分割線-------------------------------

本文翻譯自微軟白皮書《SQL Server In-Memory OLTP Internals Overview》:http://technet.microsoft.com/en-us/library/dn720242.aspx

譯者水平有限,如有翻譯不當之處,歡迎指正。

----------------------------我是分割線-------------------------------

 

SQL Server 內存中OLTP內部機制概述

 

摘要:

內存中OLTP(項目名為“Hekaton”)是一個新的完全集成到SQL Server中的數據庫引擎組件。它專為訪問內存常駐數據的OLTP工作負荷而進行優化。內存中OLTP有助於OLTP工作負荷實現顯著的性能改進,並減少了處理時間。可以通過將表聲明成“內存中優化”來啟用內存中OLTP的功能。內存優化表完全支持事務,並且可以使用Transact-SQL進行訪問。 Transact-SQL存儲過程可以被編譯成機器代碼從而進一步提升內存優化表的性能。引擎針對高並發進行設計,並使阻塞最小化。

簡介

在最初設計SQL Server的時候假定主內存是非常昂貴的,因此除非當數據確實需要進行處理,否則數據都駐留在磁盤上。由於內存價格在過去的30年中已經大幅下降,這種假設已不再有效。與此同時,多核服務器也已不再昂貴,所以如今人們只需花費不到5萬美元就可以購買到擁有32個內核和1TB內存的服務器。由於生產環境的許多(盡管並不是絕大多數)OLTP數據庫能夠完全裝進1TB的內存中,我們需要重新評估將數據存儲在磁盤上的好處,以及當讀取數據到內存中進行處理時所導致的I/O開銷。此外,OLTP數據庫在更新數據並且需要將數據寫回到磁盤時,也會產生開銷。內存優化表的存儲方式與基於磁盤的表完全不同,並且這些新的數據結構有助於更加有效地訪問和處理數據。

由於更多可用內存和更多內核的這種趨勢,微軟SQL Server團隊開始構建一種針對大量主內存和多核CPU進行優化的數據庫引擎。本文給出了這個新的數據庫引擎功能:內存中OLTP的技術概述。

有關內存中OLTP的更多信息,請參閱在內存中 OLTP(內存中優化)

 

設計注意事項與目的

開發一個真正的內存數據庫的舉動由三種基本的需求所驅動:1)將工作負荷所需的大部分或全部數據放到內存中,2)對於數據操作更低的延遲時間,3)針對特定類型的工作負荷的專業數據庫引擎需要為那些工作負荷進行優化。摩爾定律已經影響了內存的成本,允許主內存足夠大以滿足需求(1)及部分滿足需求(2)。 (更大的內存降低了讀取的延遲,但並沒有影響傳統數據庫系統所需的寫入到磁盤需要的延遲)。內存中OLTP的其他功能大大提高了數據修改操作的延遲。專為特定類型工作負荷設計的系統能夠比通用系統表現優異10倍或者10倍以上,正是這樣的認知驅動了專業數據庫引擎的需求。最專業的系統,包括那些用於復雜事件處理(CEP, Complex Event Processing),DW/BI和OLTP的系統,都通過專注於內存中的結構來優化數據結構和算法。

微軟之所以開發了內存中OLTP的功能,主要來源於這樣一個事實即主內存大小正以極快的速度增長,並變得更加便宜。此外,由於64位架構和多核處理器的普及,有理由認為大多數(盡管並不是所有)OLTP數據庫或者對性能敏感的整個工作數據集可以完全在內存中駐留。最大的金融,在線零售和航空訂票系統中的許多系統的大小降低至500GB與5TB之間,工作集也顯著變小。截至2012年,即使是一個雙插槽服務器也可以通過采用32GB的DIMMs(Dual In-line Memory Module)來容納2TB 的DRAM(Dynamic Random Access Memory)(比如IBM x3680 X5)。展望未來,在未來幾年內,以不到5美元/GB的成本來構建擁有1-10 PB容量的基於DRAM的分布式系統,是完全有可能的。非易失性的RAM變為可行也只是一個時間問題。

如果一個應用程序的大多數或所有數據都能夠完全駐留在內存中,那么SQL Server優化器從最初版本就開始使用的成本計算規則將變得幾乎完全過時,因為規則假定所有訪問的數據頁都可能需要從磁盤進行物理讀。如果不需要從磁盤進行讀取,優化器則可以使用不同的成本計算算法。此外,如果沒有磁盤讀取所需的等待時間,其他等待的統計信息,比如等待鎖被釋放,等待閂鎖可用或者等待日志寫入完成,就會變得無比龐大。內存中OLTP解決了所有的這些問題。內存中OLTP消除了等待鎖釋放的問題,采用了一種新型的多版本樂觀並發控制。內存中OLTP產生比原先少得多的日志數據,並且只需要更少的日志寫入,從而減少了等待日志寫入的延遲。

 

術語

SQL Server 2014的內存中OLTP功能涉及到一系列與使用內存優化表相關的技術。與內存優化表相對的表將被稱為基於磁盤的表,這正是SQL Server一直所提供的。使用的術語包括:

  • 內存優化表:是指采用了新的數據結構的表,這種數據結構作為內存中OLTP的一部分,將在本文中詳細描述。
  • 基於磁盤的表:與內存優化表相對,采用SQL Server此前一直使用的數據結構,以從磁盤讀取和寫入所需的8K大小的數據頁作為一個單元。
  • 本機編譯的存儲過程:是指內存中OLTP功能支持的一種對象類型,被編譯為機器代碼,並且比起只使用內存優化表,本機編譯的存儲過程有進一步增進性能的潛力。與之對應的是SQL Server此前一直使用的解釋型的Transact-SQL存儲過程。本機編譯的存儲過程只能引用內存優化表。
  • 交叉容器事務:是指同時引用內存優化表和基於磁盤的表的事務。
  • 互操作:是指引用內存優化表的解釋型的Transact-SQL

 

功能概述

在使用內存中OLTP對數據進行大多數的處理操作時,你可能並不會察覺到正在使用的是內存優化表而不是基於磁盤的表。但是,如果數據存儲在內存優化表中,SQL Server處理數據的方式非常不同。在本節中,我們將看到與SQL Server中基於磁盤的操作和數據不同的內存中OLTP運作原理和數據處理方式的概述。我們也將簡單介紹來自於競爭對手的一些內存優化數據庫的解決方案,並指出SQL Server的內存中OLTP與它們的區別。

 

內存中OLTP有何特殊之處

雖然內存中OLTP與SQL Server關系引擎集成,並且可以使用相同的接口透明地進行訪問,但它的內部行為和功能有很大的不同。圖1給出了包含內存中OLTP組件的SQL Server引擎的概述。

圖1  包含內存中OLTP組件的SQL Server引擎

 

請注意,對於內存優化表或者基於磁盤的表,無論是將調用本地編譯的存儲過程或解釋型的Transact-SQL,客戶端應用程序連接到TDS處理程序都采用相同的方式。你可以看到,解釋型的Transact-SQL可以使用互操作功能來訪問內存優化表,但本地編譯存儲過程只能訪問內存優化表。

內存優化表

內存優化表和基於磁盤的表之間最重要的區別是,當訪問內存優化表時,數據頁不需要從磁盤讀入高速緩存。所有的數據都始終被存儲在內存中。僅用於恢復的目的的檢查點文件組(數據和差異文件對)創建在駐留在內存優化文件組中的文件中,記錄了數據更改的跟蹤,而檢查點文件是只能被附加的。

在內存優化表上的操作與在基於磁盤的表的操作使用同樣的事務日志,和往常一樣,事務日志被存儲在磁盤上。萬一系統崩潰或者服務器關機,內存優化表中的數據行可以通過檢查點文件和事務日志重建。

通過使用一個名為SCHEMA_ONLY的選項,內存中OLTP能夠選擇創建一個非持久的和不記錄日志的表。如這個選項名所示,即便數據是非持久的,表架構也將是持久的。這些表在事務處理期間不需要任何IO操作,但是只有當SQL Server運行時,數據在內存中可用。一旦SQL Server關機或者AlwaysOn可用性組進行故障轉移,這些表中的數據會丟失。當它們所屬的數據庫進行恢復時,表將會被重建,而表中沒有數據。這些表可能會是有用的,例如,ETL場景里的臨時表或者用於存儲Web服務器會話狀態的臨時表。雖然數據是非持久的,但這些表上的操作符合事務其他所有的要求:原子性,隔離性和一致性。我們將會在創建表的章節看到創建一個非持久表的語法。

內存優化表上的索引

內存優化表上的索引並不按照傳統的B樹結構進行存儲。內存優化表支持非聚集哈希索引,非聚集哈希索引以哈希表的方式存儲,哈希表中擁有將相同哈希值的所有數據行與內存優化的非聚集索引連接起來的鏈接列表,而內存優化的非聚集索引使用的是特殊的BW樹進行存儲。非聚集哈希索引針對點查找進行優化,而內存優化非聚集索引則為檢索值的范圍和行排序提供支持,並優化了使用不等謂詞的查詢的性能。

每個內存優化表都必須至少有一個索引,因為正是索引將所有數據行組合成一張表。內存優化表永遠不會像基於磁盤的堆表那樣存儲成無組織的數據行集合。

索引永遠不會存儲在磁盤上,在磁盤上的檢查點文件中也不會體現,並且在索引中的操作永遠不會被日志記錄。與基於磁盤的表上的B樹索引相同,在內存優化表上的所有修改操作發生時,索引是自動進行維護的,但一旦SQL Server重新啟動,由於數據會加載到內存中,則內存優化表上的索引會被重建。

並發性的改進

當訪問內存優化表時,SQL Server實現了一個樂觀的多版本並發控制。盡管SQL Server以前通過在SQL Server 2005中引入的基於快照的隔離級別,從而號稱支持樂觀並發控制,但這些所謂的樂觀方式在數據修改操作的過程中的確需要獲取鎖。對於內存優化表,不需要獲取鎖,從而沒有因為阻塞而產生的等待。

注意,這並不意味着在使用內存優化表時,不可能產生等待。仍會存在其他等待類型,比如在事務結束時等待日志寫入完成。不過,在對內存優化表進行更改時,內存優化表的日志記錄比起基於磁盤的表更有效率的多,因此等待時間會更短。而且從磁盤讀取數據永遠不會有等待,也沒有因為數據行上的鎖而產生的等待。

本地編譯的存儲過程

當使用擁有內存優化表的本機編譯的存儲過程時,能獲得最好的執行性能。不過,相對於解釋型的代碼可供使用的豐富的功能集,本地編譯存儲過程內部允許的Transact-SQL語言結構有一些限制。此外,本機編譯存儲過程只能訪問內存優化表,並不能引用基於磁盤的表。

內存中OLTP僅僅只是DBCC PINTABLE的改進?

DBCC PINTABLE是舊版本的SQL Server提供的功能,一旦數據頁從磁盤中讀取,這張被“固定”的表里的任何數據頁就不會從內存中移除。這些數據頁需要被初始化讀取,所以這樣的表被訪問總是會有第一次讀取數據頁的成本。這些固定的表與任何其他基於磁盤的表並無任何不同。它們需要相同數量的鎖、閂鎖和日志記錄,也使用相同的索引結構,這些索引同樣也需要鎖和日志記錄。內存中OLTP的內存優化表與SQL Server的基於磁盤的表則完全不同,它們使用不同的數據和索引結構,不使用鎖,並且日志記錄這些內存優化表的更改比起基於磁盤的表效率更高。

競爭對手的產品

對於處理OLTP數據,有兩種類型的專業引擎。第一類是主內存數據庫。 Oracle有TimesTen,IBM有solidDB,還有許多其它產品主要是針對嵌入式數據庫空間。第二類是應用程序高速緩存或者鍵值存儲(例如,Velocity–App Fabric Cache和GigaSpaces),利用應用程序和中間層的內存來降低數據庫系統的工作負荷。這些緩存逐漸變得更為復雜,並擁有類似事務、范圍索引和查詢這樣的數據庫功能(例如GigaSpaces已經擁有了這些功能)。同時,數據庫系統擁有緩存功能,比如高性能哈希索引和跨多服務器集群的擴展(比如VoltDB)。內存中OLTP引擎意在提供這兩種類型引擎中各自的優點。可以認為內存中OLTP擁有緩存的性能和數據庫的功能。它支持在內存中存儲表和索引,這樣你就可以將整個數據庫建成一個完整的內存中的系統。它也提供了高性能索引和日志記錄,以及其它特性以顯著提高查詢的執行性能。

SQL Server的內存中OLTP提供了極少競爭對手產品能夠提供的以下功能:

  • 內存優化表和基於磁盤的表之間的集成,遷移到內存駐留數據庫可以逐步進行,只將最關鍵的表和存儲過程創建成內存優化的對象。
  • 本機編譯的存儲過程為基本的數據處理操作的執行時間提供了數量級的改進
  • 內存優化的非聚集哈希索引和內存優化的非聚集索引都專門為主內存訪問進行了優化
  • 不在數據頁上存儲數據,不需要數據頁閂鎖。
  • 對任何操作都沒有鎖或者閂鎖的真正多版本樂觀並發控制

SQL Server內存中OLTP與競爭對手產品設計最顯著的區別是“互操作”的集成。在一個典型的高端OLTP工作負荷中,性能瓶頸主要集中在一些特定的區域,比如一小部分的表和存儲過程。迫使將整個數據庫駐留在內存中將是昂貴和低效的。但到目前為止,其他主要的競爭產品都采用這種方法。對於SQL Server,高性能和高競爭區域可以遷移到內存中OLTP,那么在這些內存優化表上的操作(存儲過程)可以在本地進行編譯從而達到最大的業務處理性能。

內存中OLTP改進的另一個關鍵是移除了內存優化表的數據頁結構。這從根本上將數據操作算法從基於磁盤優化改變成基於內存和緩存優化。正如前面提到的,關於內存中OLTP的困惑之一是,它只像“DBCC PINTABLE”那樣簡單的將表鎖定在緩沖池中。然而,即使數據頁被強制駐留內存中時,許多競爭產品仍然采用數據頁結構。例如SAP的HANA仍使用16KB大小的數據頁來處理內存中數據行的存儲,在高性能環境下,這從本質上仍需要忍受數據頁閂鎖爭用的影響。

 

使用內存中OLTP

自從2013年6月發布的CTP版本以來,內存中OLTP引擎已經作為SQL Server 2014的一部分可供使用。內存中OLTP的安裝是SQL Server安裝應用程序的一部分。內存中OLTP組件只能在SQL Server 2014的64位版本中安裝,在所有32位版本中都不可用。

創建數據庫

包含內存優化表的任何數據庫都必須有一個MEMORY_OPTIMIZED_DATA文件組。這個文件組用於存儲SQL Server恢復內存優化表所需的檢查點文件,雖然創建這個文件組的語法與創建一個普通的FILESTREAM文件組幾乎是一樣的,但還必須指定CONTAINS MEMORY_OPTIMIZED_DATA選項。下面是創建可支持內存優化表的數據庫的一條CREATE DATABASE語句的示例:

CREATE DATABASE HKDB

    ON

    PRIMARY(NAME = [HKDB_data],

           FILENAME = 'Q:\data\HKDB_data.mdf', size=500MB),

    FILEGROUP [SampleDB_mod_fg] CONTAINS MEMORY_OPTIMIZED_DATA

           (NAME = [HKDB_mod_dir],

           FILENAME = 'R:\data\HKDB_mod_dir'),

           (NAME = [HKDB_mod_dir],

           FILENAME = 'S:\data\HKDB_mod_dir')

LOG ON (name = [SampleDB_log], Filename='L:\log\HKDB_log.ldf', size=500MB)

COLLATE Latin1_General_100_BIN2;

 

請注意,上面的示例代碼在三個不同的驅動器(Q:,R:和S:)上創建了數據庫文件,因此如果想運行這段代碼,可能需要編輯路徑名稱來與系統相匹配。在S:和R:上的文件名是相同的,所以如果在相同的驅動器上創建所有文件,這兩個文件需要使用不同的文件名。

還要注意的是指定的二進制排序規則。這時,內存優化表上的所有索引只能位於采用Windows(非SQL)BIN2排序規則的列上,並且本地編譯存儲過程只支持在這些相同排序規則上的比較,排序和分組。它可以為整個數據庫指定(如上面的CREATE DATABASE語句所做的)使用默認的二進制排序規則,也可以在CREATE TABLE語句中為任何字符數據指定排序規則。 (也可以為任何比較,排序或分組操作,在一個查詢中指定排序規則。)

另外,也可以添加一個MEMORY_OPTIMIZED_DATA文件組到現有的數據庫,然后添加文件到該文件組中。例如:

ALTER DATABASE AdventureWorks2012 ADD FILEGROUP hk_mod CONTAINS MEMORY_OPTIMIZED_DATA;

GO

ALTER DATABASE AdventureWorks2012 ADD FILE (NAME='hk_mod', FILENAME='c:\data\hk_mod')

TO FILEGROUP hk_mod;

GO

 

創建表

創建內存優化表的語法與創建基於磁盤的表的語法幾乎相同,只有一些限制以及一些所需的擴展。通過使用MEMORY_OPTIMIZED= ON子句來將表指定為內存優化表。內存優化表只能擁有這些支持數據類型的列:

  • bit
  • 所有整數類型: tinyint, smallint, int, bigint
  • 所有貨幣類型: money, smallmoney
  • 所有浮點類型: float, real
  • 日期/時間類型: datetime, smalldatetime, datetime2, date, time
  • numeric 和decimal
  • 所有非LOB字符類型: char(n), varchar(n), nchar(n), nvarchar(n), sysname
  • 非LOB二進制類型: binary(n), varbinary(n)
  • Uniqueidentifier

需要注意的是不允許有LOB數據類型;不能有XML類型,CLR或max數據類型的列,並且所有行的長度被限制在8060字節,且沒有行外數據。事實上,8060字節限制在表創建時就已強制執行,因此與基於磁盤的表不同,擁有兩個VARCHAR(5000)列的內存優化表是不能被創建的。

內存優化表可以用兩個DURABILITY值中的一個來進行定義,SCHEMA_AND_DATA或SCHEMA_ONLY,前者是默認值。采用DURABILITY = SCHEMA_ONLY定義的內存優化表,意味着表數據的修改不進行日志記錄,並且表中的數據不保留在磁盤上。但是,架構會被持久化成數據庫元數據的一部分,所以SQL Server重新啟動數據庫恢復后,空表將可供使用。

正如前面提到的,內存優化表必須至少擁有一個索引,這一要求可以通過自動創建支持主鍵約束的索引的方式來實現。除了那些采用SCHEMA_ONLY選項創建的表外,其他所有表都必須聲明一個主鍵。至少必須聲明一個索引來支持PRIMARY KEY約束。下面的示例演示了一個創建成哈希索引的PRIMARY KEY索引,必須為其指定存儲桶的數量。在討論哈希索引存儲的細節時,將提到為存儲桶選擇一個值的幾個准則。

單列索引可以與在CREATE TABLE語句中的列定義行被創建,如下圖所示。BUCKET_COUNT屬性將在哈希索引的章節進行討論。

CREATE TABLE T1

(

[Name] varchar(32) not null PRIMARY KEY NONCLUSTERED HASH WITH (BUCKET_COUNT = 100000),

[City] varchar(32) null,

[State_Province] varchar(32) null,

[LastModified] datetime not null,

) WITH (MEMORY_OPTIMIZED = ON, DURABILITY = SCHEMA_AND_DATA);

 

或者,如下面的例子所示,在所有的列已被定義之后,可以創建復合索引。下面的示例將一個非聚集索引添加到表定義中。需要注意的是對於這兩種類型的索引在定義中的區別在於,一個使用了關鍵字HASH,而另一個沒有。這兩種類型的索引都被指定為NONCLUSTERED,但Hash和存儲桶數量表明了兩個索引定義之間的不同之處。

CREATE TABLE T2

(

[Name] varchar(32) not null PRIMARY KEY NONCLUSTERED HASH WITH (BUCKET_COUNT = 100000),

[City] varchar(32) null,

[State_Province] varchar(32) null,

[LastModified] datetime not null,

INDEX T1_ndx_c2c3 NONCLUSTERED ([City],[State_Province])

) WITH (MEMORY_OPTIMIZED = ON, DURABILITY = SCHEMA_AND_DATA);

 

當創建一個內存優化表時,內存中OLTP引擎為訪問該表將生成並編譯DML例程,並將這些例程加載為DLL文件。 SQL Server本身不執行在內存優化表上實際的數據操作(記錄分裂),而是當訪問內存優化表時為所需的操作調用對應的DLL。

創建內存優化表時,除了已經列出的數據類型的限制之外,還有一些限制。

  • 沒有DML觸發器
  • 沒有外鍵或者CHECK約束
  • 除主鍵外沒有唯一索引
  • 包括支持主鍵的索引在內,最多只有8個索引

此外,一旦表被創建后,不允許更改表的架構。與使用ALTER TABLE不同,你需要刪除並重新創建表。另外,並沒有具體的索引DDL命令(如CREATE INDEX,ALTER INDEX,DROP INDEX)。索引總是作為創建表的一部分進行創建。

 

 

---------------------------全文完-------------------------------

SQL Server 內存中OLTP內部機制概述(一)

SQL Server 內存中OLTP內部機制概述(二)

SQL Server 內存中OLTP內部機制概述(三)

SQL Server 內存中OLTP內部機制概述(四)

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM