解讀SQL 記憶體資料庫的細節

srvking發表於2017-01-03

解讀SQL 記憶體資料庫的細節

相信大家對記憶體資料庫的 概念並不陌生,之前園子裡也有多位大牛介紹過SQL記憶體資料庫的建立方法,我曾仔細 拜讀過,有了大致瞭解,不過仍有很多細節不清晰,比如:

(1)記憶體資料庫是把整個資料庫放到記憶體中的嗎?

(2)資料都在記憶體裡面,那當機或者斷電了,資料不是沒有了嗎?

(3)據在記憶體是怎麼存放的,還是按照頁的方式嗎,一行的大小有限制嗎?

(4)記憶體資料庫號稱無鎖式設計,SQL是如何處理併發衝突的呢?

相信這些疑問也是大家在思考記憶體資料庫時經常遇到的難題,下文將為大家一一揭開這些問題的面紗,如有不對之處,還請各位看官幫我指出。

一、記憶體資料庫是如何儲存的,只放在記憶體嗎?是把整個資料庫放在記憶體嗎?

答案:不是。

如果你還沒有看過宋大俠的部落格,建議現在就看看。

http://www.cnblogs.com/CareySon/p/3155753.html

在這篇文章中,我想著重引用如下兩個資訊:

(1)記憶體資料庫其實就是將指定的表放到記憶體中,而不是整個資料庫;

(2)記憶體資料庫用檔案流的方式組織磁碟中的資料檔案;

我再補充一個資訊

(3)記憶體資料庫的資料檔案分data file和delta file,而且是成對出現;

1、記憶體資料庫其實就是將指定的表放到記憶體中,而不是整個資料庫;

從宋大俠的部落格中可以知道,記憶體資料庫的建立過程其實就是將表存放到記憶體中,而不是整個資料庫。下圖展示 了建立記憶體優化表的語法,紅色框標註了記憶體與傳統表建立時語法不相同的地方。

解讀SQL 記憶體資料庫的細節

記憶體優化表不僅僅是把資料存放到記憶體中,要不然跟傳統資料的快取沒有區別。在記憶體資料庫中,記憶體優化表也叫為" natively compile memory-optimized tables",翻譯過來就是本地編譯記憶體優化表,記憶體優化表在建立的同時被編譯成本地機器程式碼裝載到記憶體中,本地機器程式碼包含了能被CPU直接執行的機器指令,所以對記憶體優化表的訪問和操作將非常快。

解讀SQL 記憶體資料庫的細節

記憶體優化表分兩類,永續性表和非永續性表,對永續性表的改動會記錄日誌,即使資料庫重啟,資料也不會丟失;對非永續性表的操作不會記錄日誌,這些操作結果只保留在記憶體中,資料庫重啟後資料會丟失。

上文只是介紹了新建一張表的情況,在正常的業務環境中我們不可能對一個業務系統資料庫的每張表都去create,那對於已經存在的表,有沒有配置方法呢?答案恐怕不太令人滿意,目前SQL暫不支援遷移現有表到記憶體中,因此要想使用記憶體資料庫,現有的業務資料表必須重新建立。

2、記憶體資料庫用檔案流的方式組織磁碟中的資料檔案

在記憶體資料庫中,磁碟上儲存的資料檔案不在是區、頁的儲存方式,而是基於檔案流儲存。檔案流儲存的一個特點之一就是支援快速的讀操作,這在資料庫重啟時將檔案流中的資料load到記憶體中時很能提高效率。

3、記憶體資料庫的資料檔案分data file和delta file,而且是成對出現;

記憶體資料庫中插入、更新的資料和刪除的資料物理分開儲存的,分別用data file和delta file儲存。

(1)Data file

Data file用來儲存"插入"或者"更新"的資料行,data file中資料行的儲存順序嚴格按照事務執行的順序組織,比如data file中第一行的資料來自於事務1,第二行資料來自於事務2,這兩行可以是同一個表的資料,也可以是不同表的資料,取決於這兩個連續的事務操作的記憶體優化表是否相同。 這種方式的好處是保證了磁碟IO的連續性,避免隨機IO。

Data file的大小是固定的,為128MB,當一個data file被寫滿了後,SQL會自動新建一個data file。因為資料在data file中儲存的順序是按照事務的執行順序進行的,所以一張表的資料行(來自多個事務)可能跨越了多個data file,當對多行進行更新操作時,寫操作可以分配到多個檔案上,並且同時進行,這樣就可以加快更新的效率。(下文介紹delta file時會介紹)

如下圖,一共有4個data files(淺藍色),第一個data file的事務範圍為100-200,第二個data file的事務範圍為200-300……(100、200表示時間戳)

解讀SQL 記憶體資料庫的細節

在Data file中,如果一行被刪除或者更新了,這行不會從data file中移除,而是通過delta file(上圖黃色框)來標記刪除的行,(update的本質是delete和insert的集合,所以執行update時也會有刪除的動作),這樣可以消除不必要的磁碟IO。

如果data file的資料永不刪除,那檔案豈不是無限制的增大,以後備份不是得用很大的磁碟才行?當然不是,SQL在處理這個問題用到方法其實很簡單——"合併",根據合併策略,將多個data file和delta file合併起來,依據delta file的內容刪除data file中的多餘記錄,然後將多個data file合併成一個檔案,從而減小資料檔案佔用的磁碟空間大小。

(2) Delta file

每個data file都有一個與之匹配的Delta File,這個匹配是指事務範圍上的匹配,兩者記錄的是同一段事務(包括一個或者多個事務)上的資料,Delta File中記錄了data file中被刪除行的標記,這個標記其實就是一個關聯資訊{inserting_tx_id, row_id, deleting_tx_id }。它跟data file一樣,也是嚴格按照事務操作的順序來儲存刪除的行的資訊。

解讀SQL 記憶體資料庫的細節

如上圖,該記憶體資料庫有5個data file,分別存放了事務範圍在100-200、200-300、300-400、400-500及500的資料。如果有一個時間戳為501的事務需要刪除時間戳為150、250、450的事務所產生的資料和增加一些新資料時,相應的IO請求就會被分配到第1、2、4的 delta file上和第5的data file上。刪除操作可以分配到多個檔案上,並且同時進行,這樣就可以加快刪除的效率。

二、資料都在記憶體裡面,那當機或者斷電了,資料不是沒有了嗎?

答案:不是。

記憶體資料庫通過兩種方式保證資料的永續性:事務日誌和chcekpoint。

(1)事務日誌

記憶體資料庫的"寫日誌"和"寫資料"在一個事務中進行,在事務執行期間,SQL會先"寫資料"然後在才"寫日誌",這點與傳統資料庫不同,在傳統資料庫中,不管是在記憶體中還是磁碟中,"寫資料"總是在"寫日誌"之後,也就是通常所說的WAL(Write-Ahead Transaction Log)。但是,在事務提交時,記憶體資料庫和傳統資料庫在"寫日誌"上沒有什麼區別:日誌會先於資料寫入到磁碟中。

解讀SQL 記憶體資料庫的細節

因此,即使伺服器發生了當機或者斷電,下次資料庫重啟時會按照已經儲存在磁碟中事務日誌將業務redo(重做),所以不要擔心資料會丟失。

另外,需要補充的是,記憶體資料庫只會對永續性表將已提交的事物日誌儲存到磁碟中。這樣做的好處可以減少寫磁碟的次數。記憶體資料庫支援頻繁、快速的增、刪、改等操作,這個強度遠遠高於傳統資料庫,資料庫需要為每筆操作寫日誌,這樣就會產生大量磁碟IO,寫日誌操作將有可能成為效能瓶頸,不記錄未提交的事務日誌就減少寫日誌的數量,從而可以提高資料庫的效能。

有同學會想,不記錄未提交事務的日誌會不會導致資料不一致呢?

肯定不會,因為日誌在寫入磁碟前不可能發生先把"髒資料"寫入到磁碟的現象(下面介紹checkpoint的時候會介紹原因)。

(2)CheckPoint

在記憶體資料庫中,CheckPoint的主要目的就是將記憶體中的"資料"寫入到磁碟中,從而在資料庫崩潰或者重啟時減少資料恢復的時間。不需要資料庫逐條讀取所有的日誌來恢復資料。預設情況下Checkpoint是週期性進行的,當日志至上次checkpoint後增加了512M時會觸發新一輪CheckPoint。

在傳統資料庫這種,Checkpoint可以將未提交的資料flush到磁碟的mdf檔案中,這個現象在記憶體資料庫中不會發生,因為記憶體資料庫只將已提交事務的日誌,而在寫日誌(到磁碟)之前不可能將資料先寫到磁碟中,因此可以保證寫到磁碟中的資料一定是已提交事務的資料。

三、資料在記憶體是怎麼存放的,還是按照頁的方式嗎,一行的大小有限制嗎?

答案:不是按照頁的方式,一行的限制大小為8060Bytes。

記憶體優化表是基於行版本儲存的,同一行在記憶體中會有多個版本,可以將記憶體優化表的儲存結構看作是該表中 所有行的多個行版本的集合。

記憶體優化表中的行跟傳統資料庫的行結構是不一樣的,下圖描述了記憶體優化表中一行的資料結構:

解讀SQL 記憶體資料庫的細節

在記憶體優化表中,一行有兩個大部分組成:Row header和Row body,

Row header記錄這個行的有效期(開始時間戳和結束時間戳)和索引指標

Row body記錄了一行的實際資料。

在記憶體優化表中,行版本的數量是由針對該行的操作次數決定的,比如:每更新一次,就會新產生一行,增加一個行版本,新行有新的開始時間戳,新行產生後,原來的資料行會自動填充結束時間戳,意味這行已經過期。

解讀SQL 記憶體資料庫的細節

備註:上圖實際上只有3行,第1行有3個行版本,第2行有2個行版本,第3行有4個行版本。

既然同一行在記憶體中存在這麼多的行版本,那資料庫在訪問時是怎麼控制的呢?

在傳統資料庫中,表中每一行都是唯一的,一個事務如想找到一行,通過檔案號、頁號、槽位就可以了。

在記憶體資料庫中,每一行有多個行版本,一個事務不可能對將每個行版本都操作一遍,實際上,一個事物只能操作同一行的一個行版本,至於它能對哪個行版本進行操作,取決於事務執行時間是否在這行的兩個時間戳之間。除此之外的其他行版本對該事務而言是不可見的。

由於一行可能存在多個行版本,大家可能會提出這樣一個疑問:每行都有這麼多行版本,一張上百萬行的表,記憶體哪夠呀。不用擔心,前文介紹過了,每個行實際上是有時間戳的,對於已經打上結束時間戳且沒有活動事務訪問的行,SQL Server會通過garbage collection機制回收它佔用的記憶體,從而節省記憶體。所以不要擔心記憶體不夠。

四、記憶體資料庫號稱無鎖式設計,那如果發生了併發衝突怎麼辦,SQL是如何處理衝突的呢?

答案:記憶體資料庫用行版本來處理衝突。

鎖的一個重要作用就是避免多個程式同時修改資料,從而造成資料不一致。常見的衝突現象包括讀寫互鎖和寫寫互鎖。那記憶體資料庫是如何通過行版本來解決這兩種鎖定現象的呢?

(1)讀寫互鎖

在記憶體資料庫中,所有對記憶體優化表的事務隔離都是基於快照的,準確的說是基於行的快照。從上文行的 結構可以知道,每行的行頭包括開始時間戳和結束時間戳的,一個事務能不能訪問到這行關鍵在於事務的啟動時間是不是在這行的兩個時間戳內。

如果某個事務正在修改一行(快照),但還未提交到記憶體優化表中,也就是說"新行"還沒有結束時間戳,對"讀事務"而言,它讀還是是原來行(快照),因此不會存在髒讀的現象。

(2)寫寫互鎖

兩個事務同時更新一行時,就會發生寫寫互鎖。

記憶體資料庫衝突發生的概率比傳統資料庫小很多,但如果實在遇到了衝突,只能調整應用程式,在應用程式中加入"重試邏輯"(等待一會,然後再重新發起事務)來解決。

或許有同學覺得這種方式好像也沒有什麼大的效能改變。其實不然,舉個例子,在傳統資料庫中一個鎖可能將整個表都管住了,在表鎖期間只能等待這個事務做完才能執行其他事務,而實際上這個事務可能只是修改了小部分行,因為表鎖的存在,其他行那些不需要被這個事務操作的行。但記憶體資料庫中寫寫衝突總是發生在行級別的,這個粒度小多了,影響沒這麼大。






About Me

...............................................................................................................................

● 本文來自於轉載文章

● 小麥苗雲盤地址:http://blog.itpub.net/26736162/viewspace-1624453/
● 
原文地址:http://www.uml.org.cn/sjjm/201611242.asp

● QQ群:230161599     微信群:私聊

● 聯絡我請加QQ好友(642808185),註明新增緣由

● 版權所有,歡迎分享本文,轉載請保留出處

...............................................................................................................................

手機長按下圖識別二維碼或微信客戶端掃描下邊的二維碼來關注小麥苗的微信公眾號:xiaomaimiaolhr,免費學習最實用的資料庫技術。

wpsF8C8.tmp

 

解讀SQL 記憶體資料庫的細節 資料庫技術交流

來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/26736162/viewspace-2131690/,如需轉載,請註明出處,否則將追究法律責任。

相關文章