張振龍
(廈門大學(xué)計算機與信息學(xué)院,福建廈門360000)
近幾年,越來越多的非關(guān)鍵性業(yè)務(wù)開始嘗試使用開源數(shù)據(jù)庫以節(jié)省成本,在開源數(shù)據(jù)庫中PostgreSQL是功能最為完善的關(guān)系型數(shù)據(jù)庫。本文選取PostgreSQL作為原型數(shù)據(jù)庫。
SSD(固態(tài)硬盤)是摒棄傳統(tǒng)磁介質(zhì),其存儲優(yōu)勢是高速的順序讀取和順序?qū)懭?,高速的隨機讀取性能;劣勢是價格偏高,壽命較短,以及寫入放大,本文將結(jié)合SSD的特點,將其使用在數(shù)據(jù)庫系統(tǒng)中,以提升數(shù)據(jù)庫系統(tǒng)的整體效率。
目前許多知名數(shù)據(jù)庫廠商也針對SSD提出了自己產(chǎn)品的改進(jìn)方案,許多學(xué)術(shù)研究也把SSD中使用數(shù)據(jù)庫優(yōu)化作為研究方向。研究點大概集中在以下幾點:
(1)將整個數(shù)據(jù)庫放置在SSD上,提出一種新的數(shù)據(jù)庫存儲概念,為閃存數(shù)據(jù)庫,并圍繞閃存數(shù)據(jù)庫對數(shù)據(jù)存儲方式提出各種改進(jìn)算法。但是,由于SSD本身的壽命問題,以及成本問題,該數(shù)據(jù)庫只能應(yīng)用于小型的非關(guān)鍵性應(yīng)用。
(2)在關(guān)系型數(shù)據(jù)庫中,將日志,索引,臨時表空間,回滾段這些信息放置到SSD中,以提高數(shù)據(jù)庫整體執(zhí)行效率。日志的特點是大量的順序?qū)懖僮鳎饕?,臨時表空間,回滾段的特點是順序?qū)?,隨機讀。而且這些數(shù)據(jù)都屬于是非關(guān)鍵性數(shù)據(jù),可重建,因此,如果一時的SSD損壞并不影響數(shù)據(jù)的一致性。因此完全符合SSD的特點,可以在一定程度上提升數(shù)據(jù)庫的存儲效率。
(3)使用SSD作為二級緩存,也就是我們熟知的Oracle的 flashcache,這點 Oracle已經(jīng)成功地商用,并為其數(shù)據(jù)庫帶來了可觀的性能提升。利用SSD的特性,把從一級緩存中置換出來的數(shù)據(jù)存儲到SSD中,將SSD其作為二級緩存,由于緩存中的數(shù)據(jù)也不是關(guān)鍵數(shù)據(jù),且在使用時制定SSD讀寫策略,使其符合隨機讀順序?qū)懙脑瓌t。但是,由于Oracle是商業(yè)軟件,其對SSD的使用的具體策略并沒有公開,本文將使用SSD作為PostgreSQL的二級緩存。
數(shù)據(jù)庫對磁盤數(shù)據(jù)進(jìn)行訪問時,如果是首次,首先將數(shù)據(jù)從磁盤中取出,然后放置到共享緩存,再從共享緩存中獲取數(shù)據(jù)進(jìn)行處理。如果要取的數(shù)據(jù)在共享緩存中已經(jīng)存在,則直接從共享緩存中獲取該數(shù)據(jù)進(jìn)行處理。因此,有共享緩存的存在,大大提升了數(shù)據(jù)庫的執(zhí)行效率,減少了數(shù)據(jù)庫的IO瓶頸。而且,只要是數(shù)據(jù)庫需要的數(shù)據(jù),都必須經(jīng)過共享緩存。
本文先分析了目前PostgreSQL的共享緩存管理方法,然后設(shè)計一套策略,并對其進(jìn)行實現(xiàn)。
PostgreSQL對共享緩存的管理方式采用哈希表以及描述符的方式進(jìn)行管理。首先在初始化階段,PostgreSQL開辟出一塊空間,緩沖區(qū)描述符合緩沖區(qū)塊的存儲空間是連續(xù)分配的,而哈希表則分配在另一個空間中。
在分配完空間后,描述符與共享緩存空間塊是一個一一對應(yīng)的關(guān)系,其使用關(guān)系則通過哈希表保存,一個緩存塊的大小在 PostgreSQL中默認(rèn)為8 kbyte。而描述符是以一個數(shù)據(jù)結(jié)構(gòu)的方式存在,具體的數(shù)據(jù)結(jié)構(gòu)名稱叫BufferDesc,在Buf_internals.h中定義,主要的字段有
refcount:用于下文提到的時鐘算法輪尋;
bufid:表示所指向的緩沖的塊號,默認(rèn)為4096個塊,以整形方式標(biāo)識;
freenext:下一個空閑塊的地址,用于下文中提到的Freelist的管理;
緩沖區(qū)管理的核心就是其對緩沖塊的分配方式,上層接口請求數(shù)據(jù)時,發(fā)送一個BufferTag的數(shù)據(jù)結(jié)構(gòu),該數(shù)據(jù)結(jié)構(gòu)包含三個字段;
RelFileNode rnode:物理文件的編號;
ForkNumber forkNum:當(dāng)前的文件分支,PG文件有三種,在這不詳細(xì)描述;
BlockNumber blockNum:當(dāng)前請求的文件塊號有了這三個值,我們就可以找到對應(yīng)文件的內(nèi)容。這個時候,PostgreSQL并不是馬上去磁盤讀取數(shù)據(jù)。而是將這三個值作為上文中提到的哈希表的Key,看是否能在哈希表中找到,而哈希表的Value正是bufid。如果能找到,則表示將要請求的這個數(shù)據(jù)正在緩沖區(qū)中,直接返回該緩沖區(qū)的內(nèi)容即可;如果不能找到,再分配一個新的bufid,并維護(hù)哈希表記錄下這次磁盤讀取。
在共享緩沖區(qū)滿的時候,又有新的請求需要分配共享緩沖區(qū)塊時,觸發(fā)緩沖區(qū)分配算法,而經(jīng)典的也是最簡單的緩沖區(qū)管理算法就是FIFO,但是一般在實際應(yīng)用中會采用改進(jìn)的 FIFO算法,例如在PostgreSQL中所采用的算法就是時鐘掃描算法。該算法在BufferAlloc.c中調(diào)用,時鐘掃描算法的基本原理就是,依次掃描所有的共享緩存描述符,在描述符中記錄了一個refcount的值,如果該值為0,這將這個共享緩存返回,并使用這個共享緩存塊;如果這個值不為0,則把這個值減1,繼續(xù)找尋下一個。而refcount這個計數(shù)器是當(dāng)這個共享緩存中的數(shù)據(jù)被訪問時累加1,但是最大值為5。
時鐘掃描算法的弊端就是要遍歷所有的共享緩存塊,這樣帶來了較大的開銷。特別是當(dāng)數(shù)據(jù)庫進(jìn)行大規(guī)模讀寫,或者數(shù)據(jù)庫進(jìn)行VACUUM的時候就會對緩存造成頻繁的換入換出。因此,在Post greSQL中,對以上場景采用了一種稱為環(huán)的策略在上層接口中,可以指定共享緩存的分配方式,如果需要進(jìn)行環(huán)的方式分配,則在分配過程中記錄事先設(shè)定好的環(huán)大小,將分配過程中涉及的緩存塊形成一個環(huán),之后如果再需要分配,只在環(huán)中進(jìn)行分配而不會動用環(huán)之外的緩存塊,從而避免整個共享緩沖區(qū)被大量換入換出。
在對共享緩存的操作過程中,必然要涉及到鎖PostgreSQL中控制共享緩沖區(qū)訪問有兩種鎖。
(1)自旋鎖:又稱為pin鎖,利用互斥量實現(xiàn)的底層鎖,用于內(nèi)存塊訪問。
(2)內(nèi)容鎖:分為共享鎖和排他鎖,利用pin鎖實現(xiàn)的上層鎖,用于進(jìn)程間并發(fā)的排他和共享操作
將SSD作為PostgreSQL的二級緩存一般有三種方式。一種是作為讀數(shù)據(jù)時的緩存(緩存非臟數(shù)據(jù)),也可以作為寫數(shù)據(jù)時的緩存(只緩存臟數(shù)據(jù))最后可以作為讀寫共存的緩存。而本文對SSD作為讀數(shù)據(jù)的緩存進(jìn)行詳細(xì)描述,作為寫或者讀寫緩存的場景在這里不考慮,有興趣的讀者可以自己研究實現(xiàn)。
作為二級緩存的基本思想就是將一級緩存中存放不下的數(shù)據(jù)轉(zhuǎn)移到二級緩沖中進(jìn)行存放,而這里的一級緩存指的就是共享緩存。當(dāng)發(fā)生置換時,就是共享緩存滿的時候,數(shù)據(jù)在共享緩存中已經(jīng)無法存放,這個時候,我們就可以對置換出來的數(shù)據(jù)進(jìn)行數(shù)據(jù)轉(zhuǎn)移,轉(zhuǎn)移到二級緩存中,也就是SSD中,當(dāng)下次請求這個數(shù)據(jù)時,我們不需要再從磁盤中讀取,而直接從SSD中讀取即可。但是,并不是所有從共享緩存中置換的數(shù)據(jù)都是值得緩存的數(shù)據(jù),本文定義了一種篩選規(guī)則,如果該數(shù)據(jù)是隨機讀磁盤的操作,索引數(shù)據(jù)或者是新寫入的數(shù)據(jù)則緩存;如果是一個順序讀寫磁盤的操作,或者數(shù)據(jù)庫正處在大量讀寫,鏡像,備份等操作時則不緩存。二級緩存基本思想如圖1所示。
圖1 二級緩存基本思想
將SSD作為二級緩存時,首先模仿共享緩存的管理方式,設(shè)置一個二級緩存描述符,以及用于保存使用情況的哈希表。當(dāng)共享緩存發(fā)生置換時,具體就是在BufferAlloc這個函數(shù)的末尾,也就是已經(jīng)成功分配了一個bufid作為即將使用的共享緩存塊的時候。在原本的邏輯中,數(shù)據(jù)庫將對這個緩存塊進(jìn)行寫入覆蓋的操作,但是本文在寫入覆蓋前,對這個共享緩存塊的數(shù)據(jù)進(jìn)行判斷,如果符合進(jìn)入二級緩存的條件,則將其放置到二級緩存中,等待下次讀取。在這里,由于一個PostgreSQL的緩存塊只有8K,為了避免對SSD的頻繁操作造成寫入放大,因此可以在這里做個處理,將多個置換出的數(shù)據(jù)拼成大塊,一下寫入到SSD中。
當(dāng)下次數(shù)據(jù)請求到來時,具體就是在ReadBuffer_common這個函數(shù)中,從函數(shù)邏輯中明顯可以看出其讀磁盤的操作,具體就是smgrread的函數(shù)調(diào)用之前在這里我們可以添加代碼邏輯,使其先到SSD中去尋找想要的數(shù)據(jù),而不用直接讀取磁盤。
本文詳細(xì)表述了SSD緩存PostgreSQL中非臟數(shù)據(jù)的方法,并自己利用TPC-C進(jìn)行測試,性能約有10%左右的提升。本文中所使用的方法,還有很大的改進(jìn)空間,如果將臟數(shù)據(jù)考慮在內(nèi),則緩存情況更加復(fù)雜,這個可以作為下一步的研究方向。
SSD的特性隨著技術(shù)的發(fā)展,其優(yōu)勢將越來越明顯,最終可能取代磁盤作為主要的存儲介質(zhì)。但是,本文的意義不僅在于針對SSD的優(yōu)化,未來的硬件存儲的發(fā)展必然是多層次的,例如IBM目前已經(jīng)推出PCM存儲設(shè)備。而本文所描述的方法,只要存在多層次存儲,便可以借鑒。
[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ù)庫應(yīng)用系統(tǒng)中的運用[J].電子器件2000,23(3):185-190.
[5]姜承堯,陳慶奎.基于固態(tài)硬盤數(shù)據(jù)庫在OLTP應(yīng)用中的優(yōu)化研究[J].計算機時代,2010(8):42-44.
[6]湯顯,孟小峰.FClock:一種面向SSD的自適應(yīng)緩沖區(qū)管理算法[C]//NDBC2010第27屆中國數(shù)據(jù)庫學(xué)術(shù)會議論文集A輯一,2010:148-159.
[7]李峰,劉曉潔,林翰翮.基于Oracle數(shù)據(jù)庫的容災(zāi)系統(tǒng)[J].計算機工程與設(shè)計,2011(11):9-12.
[8]徐得智.Oracle海量數(shù)據(jù)庫系統(tǒng)的優(yōu)化策略[J].企業(yè)技術(shù)開發(fā),2009(8):44-45.