最近這段時間,我花了很多時間來更好的理解Hekaton——SQL Sever 2014里的全新內存表技術。我看了很多文章,了解了Haktaon的各種內部數據存儲結構(主要是哈希索引和Bw-tree)。另外我也看了不少關於這方面的講座。
但不止一次,有很多的誤報,神話和誤解出現,人們對Hektaton的認識發生了錯誤。從大家對Hekaton的提問就可以看出,我們需要整理Hekaton的知識,向大家重新傳達它的相關知識,讓大家更好的理解Hekaton,在Hekaton合適的場景來更好的使用它。
下面只是一些我羅列聽到的大家關於Hekaton的問題:
- “Hekaton是內存存儲技術,是否意味着數據不再永駐?”
- “Hekaton只在特定架構的CPU里可以運行?”
- “當你遷移到Hekaton,對於你的工作量,你會獲得100倍的性能提升?”
- “在Hekaton里,沒有鎖,阻塞,自旋鎖。”
這只是在過去我聽到的誤解中,頭條的一部分。因此這篇文章的目的澄清這些最大誤解和問題,我也會告訴你為什么它們是錯誤的。嗯,讓我們從我的最頭條開始(沒特定順序)!
“對於事務,Hekaton也提供ACID屬性么?”
當我開始討論Hekaton時,這個總是我第一個想澄清的:當你使用Hekaton時,對於你的事務,它還是有ACID屬性的!Hekaton里的事務一直是有原子性(Atomicity)、一致性(Consistency)、隔離性(Isolation)和持久性(Durability)的。SQL Server只是內部使用不同的概念和方法來保證這4個重要屬性。
- 原子性(Atomicity):對於你處理的事務,還是可以前滾(rolled forward)和后滾(rolled back),即使你的SQL Server崩潰,對於你啟用Hekaton的數據庫,SQL Server還是可以通過故障修復(crash recovery )(只要通過SQL Server重啟,你的數據還是持續的(persisting )——下面會詳細描述)
- 一致性(Consistency):Hekaton也提供“總是”有一致的數據。這個在當前很容易做到,因為Hekaton的最大局限性之一是不能創建外鍵(Foreign-Keys),只能本身約束。因此基本上啟用Hekaton的表,是在內部進行約束的。
- 隔離性(Isolation):幾天前,有人想說服我,Hekaton提供你臟讀(Dirty Reads),因為沒有鎖和阻塞(Locking and Blocking)。錯了!!!Hekaton使用所謂的多版本並發控制(Multi Version Concurrency Control ,MVCC)的方法,才有沒鎖的神奇可能。當你讀取數據的時候,你取回的是在你開始的語句/事務后不在有效的數據(取決於使用的事務隔離級別(Transaction Isolation Level))。因此在Hekaton里沒有臟讀(Dirty Reads)。
- 持久性(Durability):這個要分情況。當你使用架構和數據(DURABILITY=SCHEMA_AND_DATA)持久性創建你的Hekaton表,在SQL Server崩潰時,數據還是存在的。它會從事務日志里恢復,即Hekaton寫的檢查點文件。這點非常重要:當你想要你的數據持久時,Hekaton還是會使用事務日志,這也意味着你的事務日志是Hekaton的最后性能瓶頸之一。當和傳統基於傳統硬盤的表相比,Hekaton這里使用非常高效的日志模型。這里其中一個改變是,只有數據修改被日志,不是在索引級別。當你在基於傳統硬盤表里執行INSERT,SQL Server需要為“每個”索引(聚集和非聚集索引)的INSERT日志。在Hekaton里,SQL Server只日志一次INSERT,因為在SQL Server啟動期間,所有的Hekaton索引(哈希索引,范圍索引)都會重建。因此對事務日志的影響是盡可能小的。
當你使用只有架構,不包含數據(DURABILITY = SCHEMA_ONLY)的Hekaton表時,你的Hekaton事務的持久性就沒有了:當你的SQL Server崩潰了,或者你重啟SQL Server,在Hekaton表里的所有數據會丟失。因此沒有持久性。因此在事務日志里也沒有日志記錄。當然這個用法只在特殊場景里使用才有意義,例如為你的數據倉庫進行數據加載。如果SQL Server崩潰,你就要重啟你的ETL進程。你是可以重建你丟失的數據。
“Hekaton是微軟提供的No-SQL方法?”
這個誤解也非常有意思。有人想說服我說Hekaton是微軟開發的No-SQL的新方法。得了吧,使用Hekaton我們還是在討論有所有ACID屬性的關系數據庫(參閱剛才說的)。Hekaton和No-SQL是2個完全不同的東西,也沒有共同點。Hekaton內部使用優雅而快速的方法來實現關系數據庫的特性——就這樣!
“Hekaton只在特定架構的CPU上運行?”
哇哦,我想我站錯了地方!Hekaton只在特定模型/架構上運行,因為Hekaton內部使用所謂的原子CAS運算(原子比較與交換Atomic Compare & Swap, or Atomic Compare & Exchange)。那句話是完全錯誤的!當然,內部的Bw-tree使用CAS運算作為多個原子步驟做出樹里SMOs(結構修改運算,Structure Modification Operations)。Hekaton這里內部使用“InterlockedCompareExchange”的WIN32 API函數。這個函數只在特定內存位置的值和原始值比較,如果2個一樣的話,在內存位置會寫入新值。函數本身在CPU級別作為一個原子匯編指令執行,意味着沒有別的線程可以干涉那個匯編函數。它作為一個原子塊(atomic block)從開始到結束執行。
這里的神話是,需要的匯編函數只在特定CPU架構上支持。這沒錯,但是這個匯編函數從奔騰處理器開始就支持了!在386和486架構上,函數本身是不支持的……從剛才提到的MSDN文章里的要求部分看到,支持的最低系統版本是Windows XP!因此當你在前Windows XP系統里安裝SQL Server 2014時,這個神話倒是真的!
“在Hekaton里,沒有鎖,阻塞,自旋鎖。”
理論上這句話是對的。這句話可以從不同方面辯論。我們從第1個方面開始。Hekaton“本身”是沒有鎖,阻塞和自旋鎖的,但是你還是和SQL Server的傳統關系引擎打交道。這就意味着,當你離開Hekaton宇宙時,你還是和基於原始代碼的SQL Server打交道(對此,我表示遺憾……),例如事務日志管理器( Transaction Log Manager)。這些代碼還是有閂鎖(latches)和自旋鎖(spinlocks)用來保持不同線程訪問的同步。從這個角度來說,上述語句是部分正確的。
第2個方面在Hekaton里你還是有阻塞(blocking)的地方是,當你進行原子CAS操作時,一個原子CAS操作不能被不同的線程中斷。因此在Bw-Trees里SMOs(結構修改運算,Structure Modification Operations)可以以聰明、優雅的方式實現。着也意味着當你想在同個Bw-Tree里的同個頁里同時執行一個SMO是,一個線程會勝出,其他的線程需要重試原子CAS操作。同時發生了什么呢?線程會旋轉,再次嘗試CAS操作。我的基本理解是,原子CAS操作本身是就像一個Criticial Section同步概念包裝的匯編函數。這就意味着你的線程需要旋轉,你在丟失CPU周期,並增加了你事務的閉鎖性。當然,SMOs應該非常非常少見,因此這沒什么大不了的——但還是有線程旋轉的可能,當有為底層同步對象(或者匯編函數)的競爭時。
這是我在原子CAS操作上的基本理解。如果我對此的理解是錯誤的,歡迎隨時糾正我!
“因為在Hekaton里不支持INT IDENTITY值,使用序列(Sequences)?”
這句也非常有意思。我沒有在吹噓這句話!為什么?因為在你的SQL Server數據庫里,序列(Sequences)是一個共享對象,意味着訪問的當前值是由SQL Server同步的。這個同步在競爭中結束,意味着你不能延伸你的工作量,Hekaton的一切都是延伸工作量。
我用序列值在CTP1上做過一些測試,一旦你在你的Hekaton表/存儲過程上執行大量的並行線程,你就會觸發序列生成器(Sequence Generator)里的競爭。當然在一些內部頁,序列生成器(Sequence Generator)存儲着當前值,當序列生成器讀寫這些特定頁時,就會發生閂鎖(latch) 。在序列生成器里,你就用閂鎖競爭(Latch Contention)結束它了,你的Hekaton工作量也不會延伸。在我的測試里也沒太大區別,如果我請求整個范圍的序列值,或者當我使用緩存,也沒有區別。序列生成器始終是瓶頸。
那你如何克服這個特定問題?使用老好朋友UNIQUEIDENTIFIER。這些值彼此間是完全獨立生成的,意味着當你生成新值時,沒有涉及到共享資源,因此你可以剔除這個瓶頸,直到你觸發CPU 的100%的使用率(包括像事務日志,網絡帶寬等其他瓶頸)前,Hekaton的工作量是可以不斷延伸的。
“對於你的程序,Hekaton是完全透明的(is completely transparent)。”
這句話是對的,只要你對數據庫設計沒想法。我剛才已提過,在第1個發布里不能創建外鍵(Foreign-Keys)來檢查約束。我從沒看過任何基於磁盤的表,可以逐個遷移到內存優化表(Memory-Optimized table)。還記得么,INT IDENTITY 值目前尚不支持。當你遷移到Hekaton時,你做的不只是簡單的切換來獲得100倍的性能提升。抱歉!
“對於你的程序,Hekaton是完全透明的。”
這句話是對的,只要你對數據庫設計沒想法。我剛才已提過,在第1個發布里不能創建外鍵(Foreign-Keys)來檢查約束。我從沒看過任何基於磁盤的表,可以逐個遷移到內存優化表(Memory-Optimized table)。還記得么,INT IDENTITY 值目前尚不支持。當你遷移到Hekaton時,你做的不只是簡單的切換來獲得100倍的性能提升。抱歉!
“對於范圍索引(Range Indexes),Hekaton使用傳統的B+樹結構。”
錯!范圍索引使用所謂的Bw-Tree,這個當前SQL Server為聚集和非聚集索引使用的B+樹結構幾乎一樣。Bw-tree是基於B-Link tree——大家可能有點迷糊了,和傳統的B+樹相比,Bw-Tree有3個重大不同:
- 中間層的頁存儲大范圍的鍵值,在下一層的頁存儲小范圍的鍵值。因為在頁上存儲了大范圍的鍵值,Smos(例如頁分裂)可以在2個原子操作實現(通過2個原子CAS執行)。這個概念來自於B-Link tree的設計原則。
- 頁“從不”改變,因此這個會導致CPU緩存線無效,這個會穿越傳播到整個內存架構,這是非常昂貴的(在浪費CPU周期這方面)。當Hekaton需要改變內存中的頁,不會接觸到原始內存位置,Hekaton只創建一個新的Delta記錄,這就是修改操作。所謂的”頁面映射表(Page Mapping Table)“指向新的Delta記錄,Delta記錄對應的原始記錄並未修改。因為這個方式CPU緩存線無效可以避免。
- 頁大小是彈性的,並不是一直的8kb大小。
“在數據庫里,Hekaton提供你超快的業務邏輯。”
從微軟觀點來說這個是對的,因為自SQL Server 2012起SQL Server是在CPU核心層授權的。你用的CPU周期越多,需要的CPU就越多,你付給微軟的授權就越多。但從架構觀點來說是錯的!數據庫處理的是存儲和獲取數據,但是數據庫不是一個應用服務器,應用服務器才處理業務……想想看。當你有CPU競爭時,因為你在數據庫服務器里運行大量的業務邏輯,你應該重構你的數據庫,因此你把你的業務邏輯移向專用的應用服務器,作為SQL Server的授權完全不一樣——你為操作系統付錢,這就是錯的原因!
“我如何遷移我的整個SAP數據庫到Hekaton。微軟對此有提供工具么?”
當你想把整個數據庫遷移到Hekaton時,請先好好想想。Hekaton是用來解決特定問題的,像閂鎖競爭(Latch Contention)。只有把特定的表和存儲過程遷移到Hekaton時才有意義——並不是“所有”的數據庫!對於每個數據庫對象(表,存儲過程),SQL Server需要把它們編譯和鏈接成對應的DLL文件(然后載入sqlservr.exe的運行空間,這樣要花費時間)。當你重啟你的SQL Server時或者進行故障群集轉移時,也會執行編譯和鏈接。這會直接影響你HA方式的目標恢復時間(Recovery Time Objective,RTO)。
小結
我希望已經幫你澄清了Hekaton的一些神話,誤報和誤解。而且我一直強調的是:如果是“對的”問題,Hekaton可以幫解決;如果你有傳統的問題(錯誤的索引設計,糟糕的存儲性能)果斷放棄Hekaton吧,先做好你的家庭作業先。
Hekaton就像F1賽車:
你技術不好的話,F1賽車也幫不了你!
感謝關注,期待您的留言!
參考文章:
https://www.sqlpassion.at/archive/2013/11/30/myths-and-misconceptions-about-hekaton/