張振龍
(廈門大學(xué)計(jì)算機(jī)與信息學(xué)院,福建廈門360000)
近幾年,越來(lái)越多的非關(guān)鍵性業(yè)務(wù)開始嘗試使用開源數(shù)據(jù)庫(kù)以節(jié)省成本,在開源數(shù)據(jù)庫(kù)中PostgreSQL是功能最為完善的關(guān)系型數(shù)據(jù)庫(kù)。本文選取PostgreSQL作為原型數(shù)據(jù)庫(kù)。
SSD(固態(tài)硬盤)是摒棄傳統(tǒng)磁介質(zhì),其存儲(chǔ)優(yōu)勢(shì)是高速的順序讀取和順序?qū)懭耄咚俚碾S機(jī)讀取性能;劣勢(shì)是價(jià)格偏高,壽命較短,以及寫入放大,本文將結(jié)合SSD的特點(diǎn),將其使用在數(shù)據(jù)庫(kù)系統(tǒng)中,以提升數(shù)據(jù)庫(kù)系統(tǒng)的整體效率。
目前許多知名數(shù)據(jù)庫(kù)廠商也針對(duì)SSD提出了自己產(chǎn)品的改進(jìn)方案,許多學(xué)術(shù)研究也把SSD中使用數(shù)據(jù)庫(kù)優(yōu)化作為研究方向。研究點(diǎn)大概集中在以下幾點(diǎn):
(1)將整個(gè)數(shù)據(jù)庫(kù)放置在SSD上,提出一種新的數(shù)據(jù)庫(kù)存儲(chǔ)概念,為閃存數(shù)據(jù)庫(kù),并圍繞閃存數(shù)據(jù)庫(kù)對(duì)數(shù)據(jù)存儲(chǔ)方式提出各種改進(jìn)算法。但是,由于SSD本身的壽命問(wèn)題,以及成本問(wèn)題,該數(shù)據(jù)庫(kù)只能應(yīng)用于小型的非關(guān)鍵性應(yīng)用。
(2)在關(guān)系型數(shù)據(jù)庫(kù)中,將日志,索引,臨時(shí)表空間,回滾段這些信息放置到SSD中,以提高數(shù)據(jù)庫(kù)整體執(zhí)行效率。日志的特點(diǎn)是大量的順序?qū)懖僮鳎饕R時(shí)表空間,回滾段的特點(diǎn)是順序?qū)懀S機(jī)讀。而且這些數(shù)據(jù)都屬于是非關(guān)鍵性數(shù)據(jù),可重建,因此,如果一時(shí)的SSD損壞并不影響數(shù)據(jù)的一致性。因此完全符合SSD的特點(diǎn),可以在一定程度上提升數(shù)據(jù)庫(kù)的存儲(chǔ)效率。
(3)使用SSD作為二級(jí)緩存,也就是我們熟知的Oracle的 flashcache,這點(diǎn) Oracle已經(jīng)成功地商用,并為其數(shù)據(jù)庫(kù)帶來(lái)了可觀的性能提升。利用SSD的特性,把從一級(jí)緩存中置換出來(lái)的數(shù)據(jù)存儲(chǔ)到SSD中,將SSD其作為二級(jí)緩存,由于緩存中的數(shù)據(jù)也不是關(guān)鍵數(shù)據(jù),且在使用時(shí)制定SSD讀寫策略,使其符合隨機(jī)讀順序?qū)懙脑瓌t。但是,由于Oracle是商業(yè)軟件,其對(duì)SSD的使用的具體策略并沒(méi)有公開,本文將使用SSD作為PostgreSQL的二級(jí)緩存。
數(shù)據(jù)庫(kù)對(duì)磁盤數(shù)據(jù)進(jìn)行訪問(wèn)時(shí),如果是首次,首先將數(shù)據(jù)從磁盤中取出,然后放置到共享緩存,再?gòu)墓蚕砭彺嬷蝎@取數(shù)據(jù)進(jìn)行處理。如果要取的數(shù)據(jù)在共享緩存中已經(jīng)存在,則直接從共享緩存中獲取該數(shù)據(jù)進(jìn)行處理。因此,有共享緩存的存在,大大提升了數(shù)據(jù)庫(kù)的執(zhí)行效率,減少了數(shù)據(jù)庫(kù)的IO瓶頸。而且,只要是數(shù)據(jù)庫(kù)需要的數(shù)據(jù),都必須經(jīng)過(guò)共享緩存。
本文先分析了目前PostgreSQL的共享緩存管理方法,然后設(shè)計(jì)一套策略,并對(duì)其進(jìn)行實(shí)現(xiàn)。
PostgreSQL對(duì)共享緩存的管理方式采用哈希表以及描述符的方式進(jìn)行管理。首先在初始化階段,PostgreSQL開辟出一塊空間,緩沖區(qū)描述符合緩沖區(qū)塊的存儲(chǔ)空間是連續(xù)分配的,而哈希表則分配在另一個(gè)空間中。
在分配完空間后,描述符與共享緩存空間塊是一個(gè)一一對(duì)應(yīng)的關(guān)系,其使用關(guān)系則通過(guò)哈希表保存,一個(gè)緩存塊的大小在 PostgreSQL中默認(rèn)為8 kbyte。而描述符是以一個(gè)數(shù)據(jù)結(jié)構(gòu)的方式存在,具體的數(shù)據(jù)結(jié)構(gòu)名稱叫BufferDesc,在Buf_internals.h中定義,主要的字段有
refcount:用于下文提到的時(shí)鐘算法輪尋;
bufid:表示所指向的緩沖的塊號(hào),默認(rèn)為4096個(gè)塊,以整形方式標(biāo)識(shí);
freenext:下一個(gè)空閑塊的地址,用于下文中提到的Freelist的管理;
緩沖區(qū)管理的核心就是其對(duì)緩沖塊的分配方式,上層接口請(qǐng)求數(shù)據(jù)時(shí),發(fā)送一個(gè)BufferTag的數(shù)據(jù)結(jié)構(gòu),該數(shù)據(jù)結(jié)構(gòu)包含三個(gè)字段;
RelFileNode rnode:物理文件的編號(hào);
ForkNumber forkNum:當(dāng)前的文件分支,PG文件有三種,在這不詳細(xì)描述;
BlockNumber blockNum:當(dāng)前請(qǐng)求的文件塊號(hào)有了這三個(gè)值,我們就可以找到對(duì)應(yīng)文件的內(nèi)容。這個(gè)時(shí)候,PostgreSQL并不是馬上去磁盤讀取數(shù)據(jù)。而是將這三個(gè)值作為上文中提到的哈希表的Key,看是否能在哈希表中找到,而哈希表的Value正是bufid。如果能找到,則表示將要請(qǐng)求的這個(gè)數(shù)據(jù)正在緩沖區(qū)中,直接返回該緩沖區(qū)的內(nèi)容即可;如果不能找到,再分配一個(gè)新的bufid,并維護(hù)哈希表記錄下這次磁盤讀取。
在共享緩沖區(qū)滿的時(shí)候,又有新的請(qǐng)求需要分配共享緩沖區(qū)塊時(shí),觸發(fā)緩沖區(qū)分配算法,而經(jīng)典的也是最簡(jiǎn)單的緩沖區(qū)管理算法就是FIFO,但是一般在實(shí)際應(yīng)用中會(huì)采用改進(jìn)的 FIFO算法,例如在PostgreSQL中所采用的算法就是時(shí)鐘掃描算法。該算法在BufferAlloc.c中調(diào)用,時(shí)鐘掃描算法的基本原理就是,依次掃描所有的共享緩存描述符,在描述符中記錄了一個(gè)refcount的值,如果該值為0,這將這個(gè)共享緩存返回,并使用這個(gè)共享緩存塊;如果這個(gè)值不為0,則把這個(gè)值減1,繼續(xù)找尋下一個(gè)。而refcount這個(gè)計(jì)數(shù)器是當(dāng)這個(gè)共享緩存中的數(shù)據(jù)被訪問(wèn)時(shí)累加1,但是最大值為5。
時(shí)鐘掃描算法的弊端就是要遍歷所有的共享緩存塊,這樣帶來(lái)了較大的開銷。特別是當(dāng)數(shù)據(jù)庫(kù)進(jìn)行大規(guī)模讀寫,或者數(shù)據(jù)庫(kù)進(jìn)行VACUUM的時(shí)候就會(huì)對(duì)緩存造成頻繁的換入換出。因此,在Post greSQL中,對(duì)以上場(chǎng)景采用了一種稱為環(huán)的策略在上層接口中,可以指定共享緩存的分配方式,如果需要進(jìn)行環(huán)的方式分配,則在分配過(guò)程中記錄事先設(shè)定好的環(huán)大小,將分配過(guò)程中涉及的緩存塊形成一個(gè)環(huán),之后如果再需要分配,只在環(huán)中進(jìn)行分配而不會(huì)動(dòng)用環(huán)之外的緩存塊,從而避免整個(gè)共享緩沖區(qū)被大量換入換出。
在對(duì)共享緩存的操作過(guò)程中,必然要涉及到鎖PostgreSQL中控制共享緩沖區(qū)訪問(wèn)有兩種鎖。
(1)自旋鎖:又稱為pin鎖,利用互斥量實(shí)現(xiàn)的底層鎖,用于內(nèi)存塊訪問(wèn)。
(2)內(nèi)容鎖:分為共享鎖和排他鎖,利用pin鎖實(shí)現(xiàn)的上層鎖,用于進(jìn)程間并發(fā)的排他和共享操作
將SSD作為PostgreSQL的二級(jí)緩存一般有三種方式。一種是作為讀數(shù)據(jù)時(shí)的緩存(緩存非臟數(shù)據(jù)),也可以作為寫數(shù)據(jù)時(shí)的緩存(只緩存臟數(shù)據(jù))最后可以作為讀寫共存的緩存。而本文對(duì)SSD作為讀數(shù)據(jù)的緩存進(jìn)行詳細(xì)描述,作為寫或者讀寫緩存的場(chǎng)景在這里不考慮,有興趣的讀者可以自己研究實(shí)現(xiàn)。
作為二級(jí)緩存的基本思想就是將一級(jí)緩存中存放不下的數(shù)據(jù)轉(zhuǎn)移到二級(jí)緩沖中進(jìn)行存放,而這里的一級(jí)緩存指的就是共享緩存。當(dāng)發(fā)生置換時(shí),就是共享緩存滿的時(shí)候,數(shù)據(jù)在共享緩存中已經(jīng)無(wú)法存放,這個(gè)時(shí)候,我們就可以對(duì)置換出來(lái)的數(shù)據(jù)進(jìn)行數(shù)據(jù)轉(zhuǎn)移,轉(zhuǎn)移到二級(jí)緩存中,也就是SSD中,當(dāng)下次請(qǐng)求這個(gè)數(shù)據(jù)時(shí),我們不需要再?gòu)拇疟P中讀取,而直接從SSD中讀取即可。但是,并不是所有從共享緩存中置換的數(shù)據(jù)都是值得緩存的數(shù)據(jù),本文定義了一種篩選規(guī)則,如果該數(shù)據(jù)是隨機(jī)讀磁盤的操作,索引數(shù)據(jù)或者是新寫入的數(shù)據(jù)則緩存;如果是一個(gè)順序讀寫磁盤的操作,或者數(shù)據(jù)庫(kù)正處在大量讀寫,鏡像,備份等操作時(shí)則不緩存。二級(jí)緩存基本思想如圖1所示。

圖1 二級(jí)緩存基本思想
將SSD作為二級(jí)緩存時(shí),首先模仿共享緩存的管理方式,設(shè)置一個(gè)二級(jí)緩存描述符,以及用于保存使用情況的哈希表。當(dāng)共享緩存發(fā)生置換時(shí),具體就是在BufferAlloc這個(gè)函數(shù)的末尾,也就是已經(jīng)成功分配了一個(gè)bufid作為即將使用的共享緩存塊的時(shí)候。在原本的邏輯中,數(shù)據(jù)庫(kù)將對(duì)這個(gè)緩存塊進(jìn)行寫入覆蓋的操作,但是本文在寫入覆蓋前,對(duì)這個(gè)共享緩存塊的數(shù)據(jù)進(jìn)行判斷,如果符合進(jìn)入二級(jí)緩存的條件,則將其放置到二級(jí)緩存中,等待下次讀取。在這里,由于一個(gè)PostgreSQL的緩存塊只有8K,為了避免對(duì)SSD的頻繁操作造成寫入放大,因此可以在這里做個(gè)處理,將多個(gè)置換出的數(shù)據(jù)拼成大塊,一下寫入到SSD中。
當(dāng)下次數(shù)據(jù)請(qǐng)求到來(lái)時(shí),具體就是在ReadBuffer_common這個(gè)函數(shù)中,從函數(shù)邏輯中明顯可以看出其讀磁盤的操作,具體就是smgrread的函數(shù)調(diào)用之前在這里我們可以添加代碼邏輯,使其先到SSD中去尋找想要的數(shù)據(jù),而不用直接讀取磁盤。
本文詳細(xì)表述了SSD緩存PostgreSQL中非臟數(shù)據(jù)的方法,并自己利用TPC-C進(jìn)行測(cè)試,性能約有10%左右的提升。本文中所使用的方法,還有很大的改進(jìn)空間,如果將臟數(shù)據(jù)考慮在內(nèi),則緩存情況更加復(fù)雜,這個(gè)可以作為下一步的研究方向。
SSD的特性隨著技術(shù)的發(fā)展,其優(yōu)勢(shì)將越來(lái)越明顯,最終可能取代磁盤作為主要的存儲(chǔ)介質(zhì)。但是,本文的意義不僅在于針對(duì)SSD的優(yōu)化,未來(lái)的硬件存儲(chǔ)的發(fā)展必然是多層次的,例如IBM目前已經(jīng)推出PCM存儲(chǔ)設(shè)備。而本文所描述的方法,只要存在多層次存儲(chǔ),便可以借鑒。
[1]Gray J.Tape is Dead,Disk is Tape,F(xiàn)lash is Disk,RAM Locality is King[EB/OL].[2009-07-19].http://research.microsoft.com/en-us/um/people/gray/talks/flash_is_good.ppt.
[2]Mtron.Solid State Drive Msd-Sata 3035 Product Specification[EB/OL].[2009-07-19].http://mtron.net/Upload_Data/Spec/ASiC/MOBI/SATA/MSD-SATA3035_rev0.4.pdf.
[3]Shah M A,Harizopoulos S,Wiener J L,et al.Fast Scans and Joins Using Flash Drives[C]//Luo Q,Ross K A.Proceedings of 4th Workshop on Data Management on New Hardware.New York ACM Press,2008:17-24.
[4]錢學(xué)忠.SQL在數(shù)據(jù)庫(kù)應(yīng)用系統(tǒng)中的運(yùn)用[J].電子器件2000,23(3):185-190.
[5]姜承堯,陳慶奎.基于固態(tài)硬盤數(shù)據(jù)庫(kù)在OLTP應(yīng)用中的優(yōu)化研究[J].計(jì)算機(jī)時(shí)代,2010(8):42-44.
[6]湯顯,孟小峰.FClock:一種面向SSD的自適應(yīng)緩沖區(qū)管理算法[C]//NDBC2010第27屆中國(guó)數(shù)據(jù)庫(kù)學(xué)術(shù)會(huì)議論文集A輯一,2010:148-159.
[7]李峰,劉曉潔,林翰翮.基于Oracle數(shù)據(jù)庫(kù)的容災(zāi)系統(tǒng)[J].計(jì)算機(jī)工程與設(shè)計(jì),2011(11):9-12.
[8]徐得智.Oracle海量數(shù)據(jù)庫(kù)系統(tǒng)的優(yōu)化策略[J].企業(yè)技術(shù)開發(fā),2009(8):44-45.