西西軟件園多重安全檢測(cè)下載網(wǎng)站、值得信賴的軟件下載站!
軟件
軟件
文章
搜索

首頁(yè)西西教程數(shù)據(jù)庫(kù)教程 → SQL 內(nèi)存數(shù)據(jù)庫(kù)的細(xì)節(jié)、SQL Server 2014內(nèi)存數(shù)據(jù)庫(kù)新特性

SQL 內(nèi)存數(shù)據(jù)庫(kù)的細(xì)節(jié)、SQL Server 2014內(nèi)存數(shù)據(jù)庫(kù)新特性

相關(guān)軟件相關(guān)文章發(fā)表評(píng)論 來(lái)源:西西整理時(shí)間:2014/2/14 21:18:59字體大。A-A+

作者:西西點(diǎn)擊:141次評(píng)論:0次標(biāo)簽: SQLServer

相信大家對(duì)內(nèi)存數(shù)據(jù)庫(kù)的 概念并不陌生,之前園子里也有多位大牛介紹過(guò)SQL內(nèi)存數(shù)據(jù)庫(kù)的創(chuàng)建方法,我曾仔細(xì) 拜讀過(guò),有了大致了解,不過(guò)仍有很多細(xì)節(jié)不清晰,比如:

(1)內(nèi)存數(shù)據(jù)庫(kù)是把整個(gè)數(shù)據(jù)庫(kù)放到內(nèi)存中的嗎?

(2)數(shù)據(jù)都在內(nèi)存里面,那宕機(jī)或者斷電了,數(shù)據(jù)不是沒(méi)有了嗎?

(3)據(jù)在內(nèi)存是怎么存放的,還是按照頁(yè)的方式嗎,一行的大小有限制嗎?

(4)內(nèi)存數(shù)據(jù)庫(kù)號(hào)稱無(wú)鎖式設(shè)計(jì),SQL是如何處理并發(fā)沖突的呢?

相信這些疑問(wèn)也是大家在思考內(nèi)存數(shù)據(jù)庫(kù)時(shí)經(jīng)常遇到的難題,下文將為大家一一揭開這些問(wèn)題的面紗,如有不對(duì)之處,還請(qǐng)各位看官幫我指出。

一、內(nèi)存數(shù)據(jù)庫(kù)是如何存儲(chǔ)的,只放在內(nèi)存嗎?是把整個(gè)數(shù)據(jù)庫(kù)放在內(nèi)存嗎?

答案:不是。

SQL Server 2014提供了眾多激動(dòng)人心的新功能,但其中我想最讓人期待的特性之一就要算內(nèi)存數(shù)據(jù)庫(kù)了。去年我再西雅圖參加SQL PASS Summit 2012的開幕式時(shí),微軟就宣布了將在下一個(gè)SQL Server版本中附帶代號(hào)為Hekaton的內(nèi)存數(shù)據(jù)庫(kù)引擎。現(xiàn)在隨著2014CTP1的到來(lái),我們終于可以一窺其面貌。

內(nèi)存數(shù)據(jù)庫(kù)

在傳統(tǒng)的數(shù)據(jù)庫(kù)表中,由于磁盤的物理結(jié)構(gòu)限制,表和索引的結(jié)構(gòu)為B-Tree,這就使得該類索引在大并發(fā)的OLTP環(huán)境中顯得非常乏力,雖然有很多辦法來(lái)解決這類問(wèn)題,比如說(shuō)樂(lè)觀并發(fā)控制,應(yīng)用程序緩存,分布式等。但成本依然會(huì)略高。而隨著這些年硬件的發(fā)展,現(xiàn)在服務(wù)器擁有幾百G內(nèi)存并不罕見(jiàn),此外由于NUMA架構(gòu)的成熟,也消除了多CPU訪問(wèn)內(nèi)存的瓶頸問(wèn)題,因此內(nèi)存數(shù)據(jù)庫(kù)得以出現(xiàn)。

內(nèi)存的學(xué)名叫做Random Access Memory(RAM),因此如其特性一樣,是隨機(jī)訪問(wèn)的,因此對(duì)于內(nèi)存,對(duì)應(yīng)的數(shù)據(jù)結(jié)構(gòu)也會(huì)是Hash-Index,而并發(fā)的隔離方式也對(duì)應(yīng)的變成了MVCC,因此內(nèi)存數(shù)據(jù)庫(kù)可以在同樣的硬件資源下,Handle更多的并發(fā)和請(qǐng)求,并且不會(huì)被鎖阻塞,而SQL Server 2014集成了這個(gè)強(qiáng)大的功能,并不像Oracle的TimesTen需要額外付費(fèi),因此結(jié)合SSD AS Buffer Pool特性,所產(chǎn)生的效果將會(huì)非常值得期待。

SQL Server內(nèi)存數(shù)據(jù)庫(kù)的表現(xiàn)形式

在SQL Server的Hekaton引擎由兩部分組成:內(nèi)存優(yōu)化表和本地編譯存儲(chǔ)過(guò)程。雖然Hekaton集成進(jìn)了關(guān)系數(shù)據(jù)庫(kù)引擎,但訪問(wèn)他們的方法對(duì)于客戶端是透明的,這也意味著從客戶端應(yīng)用程序的角度來(lái)看,并不會(huì)知道Hekaton引擎的存在。如圖1所示。

圖1.客戶端APP不會(huì)感知Hekaton引擎的存在

首先內(nèi)存優(yōu)化表完全不會(huì)再存在鎖的概念(雖然之前的版本有快照隔離這個(gè)樂(lè)觀并發(fā)控制的概念,但快照隔離仍然需要在修改數(shù)據(jù)的時(shí)候加鎖),此外內(nèi)存優(yōu)化表Hash-Index結(jié)構(gòu)使得隨機(jī)讀寫的速度大大提高,另外內(nèi)存優(yōu)化表可以設(shè)置為非持久內(nèi)存優(yōu)化表,從而也就沒(méi)有了日志(適合于ETL中間結(jié)果操作,但存在數(shù)據(jù)丟失的危險(xiǎn))

在這篇文章中,我想著重引用如下兩個(gè)信息:

(1)內(nèi)存數(shù)據(jù)庫(kù)其實(shí)就是將指定的表放到內(nèi)存中,而不是整個(gè)數(shù)據(jù)庫(kù);

(2)內(nèi)存數(shù)據(jù)庫(kù)用文件流的方式組織磁盤中的數(shù)據(jù)文件;

我再補(bǔ)充一個(gè)信息

(3)內(nèi)存數(shù)據(jù)庫(kù)的數(shù)據(jù)文件分data file和delta file,而且是成對(duì)出現(xiàn);

1、內(nèi)存數(shù)據(jù)庫(kù)其實(shí)就是將指定的表放到內(nèi)存中,而不是整個(gè)數(shù)據(jù)庫(kù);

從宋大俠的博客中可以知道,內(nèi)存數(shù)據(jù)庫(kù)的創(chuàng)建過(guò)程其實(shí)就是將表存放到內(nèi)存中,而不是整個(gè)數(shù)據(jù)庫(kù)。下圖展示 了創(chuàng)建內(nèi)存優(yōu)化表的語(yǔ)法,紅色框標(biāo)注了內(nèi)存與傳統(tǒng)表創(chuàng)建時(shí)語(yǔ)法不相同的地方。

內(nèi)存優(yōu)化表不僅僅是把數(shù)據(jù)存放到內(nèi)存中,要不然跟傳統(tǒng)數(shù)據(jù)的緩存沒(méi)有區(qū)別。在內(nèi)存數(shù)據(jù)庫(kù)中,內(nèi)存優(yōu)化表也叫為" natively compile memory-optimized tables",翻譯過(guò)來(lái)就是本地編譯內(nèi)存優(yōu)化表,內(nèi)存優(yōu)化表在創(chuàng)建的同時(shí)被編譯成本地機(jī)器代碼裝載到內(nèi)存中,本地機(jī)器代碼包含了能被CPU直接執(zhí)行的機(jī)器指令,所以對(duì)內(nèi)存優(yōu)化表的訪問(wèn)和操作將非?。

內(nèi)存優(yōu)化表分兩類,持久性表和非持久性表,對(duì)持久性表的改動(dòng)會(huì)記錄日志,即使數(shù)據(jù)庫(kù)重啟,數(shù)據(jù)也不會(huì)丟失;對(duì)非持久性表的操作不會(huì)記錄日志,這些操作結(jié)果只保留在內(nèi)存中,數(shù)據(jù)庫(kù)重啟后數(shù)據(jù)會(huì)丟失。

上文只是介紹了新建一張表的情況,在正常的業(yè)務(wù)環(huán)境中我們不可能對(duì)一個(gè)業(yè)務(wù)系統(tǒng)數(shù)據(jù)庫(kù)的每張表都去create,那對(duì)于已經(jīng)存在的表,有沒(méi)有配置方法呢?答案恐怕不太令人滿意,目前SQL暫不支持遷移現(xiàn)有表到內(nèi)存中,因此要想使用內(nèi)存數(shù)據(jù)庫(kù),現(xiàn)有的業(yè)務(wù)數(shù)據(jù)表必須重新創(chuàng)建。

2、內(nèi)存數(shù)據(jù)庫(kù)用文件流的方式組織磁盤中的數(shù)據(jù)文件

在內(nèi)存數(shù)據(jù)庫(kù)中,磁盤上存儲(chǔ)的數(shù)據(jù)文件不在是區(qū)、頁(yè)的存儲(chǔ)方式,而是基于文件流存儲(chǔ)。文件流存儲(chǔ)的一個(gè)特點(diǎn)之一就是支持快速的讀操作,這在數(shù)據(jù)庫(kù)重啟時(shí)將文件流中的數(shù)據(jù)load到內(nèi)存中時(shí)很能提高效率。

3、內(nèi)存數(shù)據(jù)庫(kù)的數(shù)據(jù)文件分data file和delta file,而且是成對(duì)出現(xiàn);

內(nèi)存數(shù)據(jù)庫(kù)中插入、更新的數(shù)據(jù)和刪除的數(shù)據(jù)物理分開存儲(chǔ)的,分別用data file和delta file保存。

(1)Data file

Data file用來(lái)保存"插入"或者"更新"的數(shù)據(jù)行,data file中數(shù)據(jù)行的存儲(chǔ)順序嚴(yán)格按照事務(wù)執(zhí)行的順序組織,比如data file中第一行的數(shù)據(jù)來(lái)自于事務(wù)1,第二行數(shù)據(jù)來(lái)自于事務(wù)2,這兩行可以是同一個(gè)表的數(shù)據(jù),也可以是不同表的數(shù)據(jù),取決于這兩個(gè)連續(xù)的事務(wù)操作的內(nèi)存優(yōu)化表是否相同。 這種方式的好處是保證了磁盤IO的連續(xù)性,避免隨機(jī)IO。

Data file的大小是固定的,為128MB,當(dāng)一個(gè)data file被寫滿了后,SQL會(huì)自動(dòng)新建一個(gè)data file。因?yàn)閿?shù)據(jù)在data file中保存的順序是按照事務(wù)的執(zhí)行順序進(jìn)行的,所以一張表的數(shù)據(jù)行(來(lái)自多個(gè)事務(wù))可能跨越了多個(gè)data file,當(dāng)對(duì)多行進(jìn)行更新操作時(shí),寫操作可以分配到多個(gè)文件上,并且同時(shí)進(jìn)行,這樣就可以加快更新的效率。(下文介紹delta file時(shí)會(huì)介紹)

如下圖,一共有4個(gè)data files(淺藍(lán)色),第一個(gè)data file的事務(wù)范圍為100-200,第二個(gè)data file的事務(wù)范圍為200-300……(100、200表示時(shí)間戳)

在Data file中,如果一行被刪除或者更新了,這行不會(huì)從data file中移除,而是通過(guò)delta file(上圖黃色框)來(lái)標(biāo)記刪除的行,(update的本質(zhì)是delete和insert的集合,所以執(zhí)行update時(shí)也會(huì)有刪除的動(dòng)作),這樣可以消除不必要的磁盤IO。

如果data file的數(shù)據(jù)永不刪除,那文件豈不是無(wú)限制的增大,以后備份不是得用很大的磁盤才行?當(dāng)然不是,SQL在處理這個(gè)問(wèn)題用到方法其實(shí)很簡(jiǎn)單——"合并",根據(jù)合并策略,將多個(gè)data file和delta file合并起來(lái),依據(jù)delta file的內(nèi)容刪除data file中的多余記錄,然后將多個(gè)data file合并成一個(gè)文件,從而減小數(shù)據(jù)文件占用的磁盤空間大小。

(2) Delta file

每個(gè)data file都有一個(gè)與之匹配的Delta File,這個(gè)匹配是指事務(wù)范圍上的匹配,兩者記錄的是同一段事務(wù)(包括一個(gè)或者多個(gè)事務(wù))上的數(shù)據(jù),Delta File中記錄了data file中被刪除行的標(biāo)記,這個(gè)標(biāo)記其實(shí)就是一個(gè)關(guān)聯(lián)信息{inserting_tx_id, row_id, deleting_tx_id }。它跟data file一樣,也是嚴(yán)格按照事務(wù)操作的順序來(lái)保存刪除的行的信息。

如上圖,該內(nèi)存數(shù)據(jù)庫(kù)有5個(gè)data file,分別存放了事務(wù)范圍在100-200、200-300、300-400、400-500及500的數(shù)據(jù)。如果有一個(gè)時(shí)間戳為501的事務(wù)需要?jiǎng)h除時(shí)間戳為150、250、450的事務(wù)所產(chǎn)生的數(shù)據(jù)和增加一些新數(shù)據(jù)時(shí),相應(yīng)的IO請(qǐng)求就會(huì)被分配到第1、2、4的 delta file上和第5的data file上。刪除操作可以分配到多個(gè)文件上,并且同時(shí)進(jìn)行,這樣就可以加快刪除的效率。

二、數(shù)據(jù)都在內(nèi)存里面,那宕機(jī)或者斷電了,數(shù)據(jù)不是沒(méi)有了嗎?

答案:不是。

內(nèi)存數(shù)據(jù)庫(kù)通過(guò)兩種方式保證數(shù)據(jù)的持久性:事務(wù)日志和chcekpoint。

(1)事務(wù)日志

內(nèi)存數(shù)據(jù)庫(kù)的"寫日志"和"寫數(shù)據(jù)"在一個(gè)事務(wù)中進(jìn)行,在事務(wù)執(zhí)行期間,SQL會(huì)先"寫數(shù)據(jù)"然后在才"寫日志",這點(diǎn)與傳統(tǒng)數(shù)據(jù)庫(kù)不同,在傳統(tǒng)數(shù)據(jù)庫(kù)中,不管是在內(nèi)存中還是磁盤中,"寫數(shù)據(jù)"總是在"寫日志"之后,也就是通常所說(shuō)的WAL(Write-Ahead Transaction Log)。但是,在事務(wù)提交時(shí),內(nèi)存數(shù)據(jù)庫(kù)和傳統(tǒng)數(shù)據(jù)庫(kù)在"寫日志"上沒(méi)有什么區(qū)別:日志會(huì)先于數(shù)據(jù)寫入到磁盤中。

因此,即使服務(wù)器發(fā)生了宕機(jī)或者斷電,下次數(shù)據(jù)庫(kù)重啟時(shí)會(huì)按照已經(jīng)保存在磁盤中事務(wù)日志將業(yè)務(wù)redo(重做),所以不要擔(dān)心數(shù)據(jù)會(huì)丟失。

另外,需要補(bǔ)充的是,內(nèi)存數(shù)據(jù)庫(kù)只會(huì)對(duì)持久性表將已提交的事物日志保存到磁盤中。這樣做的好處可以減少寫磁盤的次數(shù)。內(nèi)存數(shù)據(jù)庫(kù)支持頻繁、快速的增、刪、改等操作,這個(gè)強(qiáng)度遠(yuǎn)遠(yuǎn)高于傳統(tǒng)數(shù)據(jù)庫(kù),數(shù)據(jù)庫(kù)需要為每筆操作寫日志,這樣就會(huì)產(chǎn)生大量磁盤IO,寫日志操作將有可能成為性能瓶頸,不記錄未提交的事務(wù)日志就減少寫日志的數(shù)量,從而可以提高數(shù)據(jù)庫(kù)的性能。

有同學(xué)會(huì)想,不記錄未提交事務(wù)的日志會(huì)不會(huì)導(dǎo)致數(shù)據(jù)不一致呢?

肯定不會(huì),因?yàn)槿罩驹趯懭氪疟P前不可能發(fā)生先把"臟數(shù)據(jù)"寫入到磁盤的現(xiàn)象(下面介紹checkpoint的時(shí)候會(huì)介紹原因)。

(2)CheckPoint

在內(nèi)存數(shù)據(jù)庫(kù)中,CheckPoint的主要目的就是將內(nèi)存中的"數(shù)據(jù)"寫入到磁盤中,從而在數(shù)據(jù)庫(kù)崩潰或者重啟時(shí)減少數(shù)據(jù)恢復(fù)的時(shí)間。不需要數(shù)據(jù)庫(kù)逐條讀取所有的日志來(lái)恢復(fù)數(shù)據(jù)。默認(rèn)情況下Checkpoint是周期性進(jìn)行的,當(dāng)日志至上次checkpoint后增加了512M時(shí)會(huì)觸發(fā)新一輪CheckPoint。

在傳統(tǒng)數(shù)據(jù)庫(kù)這種,Checkpoint可以將未提交的數(shù)據(jù)flush到磁盤的mdf文件中,這個(gè)現(xiàn)象在內(nèi)存數(shù)據(jù)庫(kù)中不會(huì)發(fā)生,因?yàn)閮?nèi)存數(shù)據(jù)庫(kù)只將已提交事務(wù)的日志,而在寫日志(到磁盤)之前不可能將數(shù)據(jù)先寫到磁盤中,因此可以保證寫到磁盤中的數(shù)據(jù)一定是已提交事務(wù)的數(shù)據(jù)。

三、數(shù)據(jù)在內(nèi)存是怎么存放的,還是按照頁(yè)的方式嗎,一行的大小有限制嗎?

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

內(nèi)存優(yōu)化表是基于行版本存儲(chǔ)的,同一行在內(nèi)存中會(huì)有多個(gè)版本,可以將內(nèi)存優(yōu)化表的存儲(chǔ)結(jié)構(gòu)看作是該表中 所有行的多個(gè)行版本的集合。

內(nèi)存優(yōu)化表中的行跟傳統(tǒng)數(shù)據(jù)庫(kù)的行結(jié)構(gòu)是不一樣的,下圖描述了內(nèi)存優(yōu)化表中一行的數(shù)據(jù)結(jié)構(gòu):

在內(nèi)存優(yōu)化表中,一行有兩個(gè)大部分組成:Row header和Row body,

Row header記錄這個(gè)行的有效期(開始時(shí)間戳和結(jié)束時(shí)間戳)和索引指針

Row body記錄了一行的實(shí)際數(shù)據(jù)。

在內(nèi)存優(yōu)化表中,行版本的數(shù)量是由針對(duì)該行的操作次數(shù)決定的,比如:每更新一次,就會(huì)新產(chǎn)生一行,增加一個(gè)行版本,新行有新的開始時(shí)間戳,新行產(chǎn)生后,原來(lái)的數(shù)據(jù)行會(huì)自動(dòng)填充結(jié)束時(shí)間戳,意味這行已經(jīng)過(guò)期。

備注:上圖實(shí)際上只有3行,第1行有3個(gè)行版本,第2行有2個(gè)行版本,第3行有4個(gè)行版本。

既然同一行在內(nèi)存中存在這么多的行版本,那數(shù)據(jù)庫(kù)在訪問(wèn)時(shí)是怎么控制的呢?

在傳統(tǒng)數(shù)據(jù)庫(kù)中,表中每一行都是唯一的,一個(gè)事務(wù)如想找到一行,通過(guò)文件號(hào)、頁(yè)號(hào)、槽位就可以了。

在內(nèi)存數(shù)據(jù)庫(kù)中,每一行有多個(gè)行版本,一個(gè)事務(wù)不可能對(duì)將每個(gè)行版本都操作一遍,實(shí)際上,一個(gè)事物只能操作同一行的一個(gè)行版本,至于它能對(duì)哪個(gè)行版本進(jìn)行操作,取決于事務(wù)執(zhí)行時(shí)間是否在這行的兩個(gè)時(shí)間戳之間。除此之外的其他行版本對(duì)該事務(wù)而言是不可見(jiàn)的。

由于一行可能存在多個(gè)行版本,大家可能會(huì)提出這樣一個(gè)疑問(wèn):每行都有這么多行版本,一張上百萬(wàn)行的表,內(nèi)存哪夠呀。不用擔(dān)心,前文介紹過(guò)了,每個(gè)行實(shí)際上是有時(shí)間戳的,對(duì)于已經(jīng)打上結(jié)束時(shí)間戳且沒(méi)有活動(dòng)事務(wù)訪問(wèn)的行,SQL Server會(huì)通過(guò)garbage collection機(jī)制回收它占用的內(nèi)存,從而節(jié)省內(nèi)存。所以不要擔(dān)心內(nèi)存不夠。

四、內(nèi)存數(shù)據(jù)庫(kù)號(hào)稱無(wú)鎖式設(shè)計(jì),那如果發(fā)生了并發(fā)沖突怎么辦,SQL是如何處理沖突的呢?

答案:內(nèi)存數(shù)據(jù)庫(kù)用行版本來(lái)處理沖突。

鎖的一個(gè)重要作用就是避免多個(gè)進(jìn)程同時(shí)修改數(shù)據(jù),從而造成數(shù)據(jù)不一致。常見(jiàn)的沖突現(xiàn)象包括讀寫互鎖和寫寫互鎖。那內(nèi)存數(shù)據(jù)庫(kù)是如何通過(guò)行版本來(lái)解決這兩種鎖定現(xiàn)象的呢?

(1)讀寫互鎖

在內(nèi)存數(shù)據(jù)庫(kù)中,所有對(duì)內(nèi)存優(yōu)化表的事務(wù)隔離都是基于快照的,準(zhǔn)確的說(shuō)是基于行的快照。從上文行的 結(jié)構(gòu)可以知道,每行的行頭包括開始時(shí)間戳和結(jié)束時(shí)間戳的,一個(gè)事務(wù)能不能訪問(wèn)到這行關(guān)鍵在于事務(wù)的啟動(dòng)時(shí)間是不是在這行的兩個(gè)時(shí)間戳內(nèi)。

如果某個(gè)事務(wù)正在修改一行(快照),但還未提交到內(nèi)存優(yōu)化表中,也就是說(shuō)"新行"還沒(méi)有結(jié)束時(shí)間戳,對(duì)"讀事務(wù)"而言,它讀還是是原來(lái)行(快照),因此不會(huì)存在臟讀的現(xiàn)象。

(2)寫寫互鎖

兩個(gè)事務(wù)同時(shí)更新一行時(shí),就會(huì)發(fā)生寫寫互鎖。

內(nèi)存數(shù)據(jù)庫(kù)沖突發(fā)生的概率比傳統(tǒng)數(shù)據(jù)庫(kù)小很多,但如果實(shí)在遇到了沖突,只能調(diào)整應(yīng)用程序,在應(yīng)用程序中加入"重試邏輯"(等待一會(huì),然后再重新發(fā)起事務(wù))來(lái)解決。

或許有同學(xué)覺(jué)得這種方式好像也沒(méi)有什么大的性能改變。其實(shí)不然,舉個(gè)例子,在傳統(tǒng)數(shù)據(jù)庫(kù)中一個(gè)鎖可能將整個(gè)表都管住了,在表鎖期間只能等待這個(gè)事務(wù)做完才能執(zhí)行其他事務(wù),而實(shí)際上這個(gè)事務(wù)可能只是修改了小部分行,因?yàn)楸礞i的存在,其他行那些不需要被這個(gè)事務(wù)操作的行。但內(nèi)存數(shù)據(jù)庫(kù)中寫寫沖突總是發(fā)生在行級(jí)別的,這個(gè)粒度小多了,影響沒(méi)這么大。

    相關(guān)評(píng)論

    閱讀本文后您有什么感想? 已有人給出評(píng)價(jià)!

    • 8 喜歡喜歡
    • 3 頂
    • 1 難過(guò)難過(guò)
    • 5 囧
    • 3 圍觀圍觀
    • 2 無(wú)聊無(wú)聊

    熱門評(píng)論

    最新評(píng)論

    發(fā)表評(píng)論 查看所有評(píng)論(0)

    昵稱:
    表情: 高興 可 汗 我不要 害羞 好 下下下 送花 屎 親親
    字?jǐn)?shù): 0/500 (您的評(píng)論需要經(jīng)過(guò)審核才能顯示)