溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

圖解PostgreSQL--buffer的分配

發(fā)布時間:2020-08-10 18:00:32 來源:ITPUB博客 閱讀:200 作者:yzs87 欄目:關(guān)系型數(shù)據(jù)庫

一、數(shù)據(jù)結(jié)構(gòu)

圖解PostgreSQL--buffer的分配

1、Buffer由數(shù)組BufferDescriptor[]數(shù)組進行管理。該數(shù)組由函數(shù)InitBufferPool創(chuàng)建,大小為NBuffers個成員即BufferDesc。該數(shù)組創(chuàng)建后由StrategyControl進行管理,firstFreeBuffer為鏈表頭,指向鏈表第一個成員;lastFreeBuffer指向鏈表尾;所有free list中成員由freeNext串起來,該值為數(shù)組下標。

BufferDescriptor數(shù)組是共享內(nèi)存中申請,所有進程共享??梢钥吹絻蓚€進程的BufferDescriptors地址相同:

進程1:
(gdb) p BufferDescriptors
$1 = (BufferDescPadded *) 0xa615fb80
(gdb) p *BufferDescriptors
$2 = {bufferdesc = {tag = {rnode = {spcNode = 1664, dbNode = 0, 
        relNode = 1262}, forkNum = MAIN_FORKNUM, blockNum = 0}, buf_id = 0, 
    state = {value = 2199126016}, wait_backend_pid = 0, freeNext = -2, 
    content_lock = {tranche = 53, state = {value = 536870912}, waiters = {
        head = 2147483647, tail = 2147483647}}}, pad = "\200"}
進程2:
(gdb) p BufferDescriptors
$1 = (BufferDescPadded *) 0xa615fb80
(gdb) p *BufferDescriptors
$2 = {bufferdesc = {tag = {rnode = {spcNode = 1664, dbNode = 0, 
        relNode = 1262}, forkNum = MAIN_FORKNUM, blockNum = 0}, buf_id = 0, 
    state = {value = 2199126016}, wait_backend_pid = 0, freeNext = -2, 
    content_lock = {tranche = 53, state = {value = 536870912}, waiters = {
        head = 2147483647, tail = 2147483647}}}, pad = "\200"}

2、同時還會通過一個環(huán)形區(qū)進行管理這些數(shù)組成員。當進行大表掃描時使用。由strategy->buffers[]數(shù)組管理,該數(shù)組存儲的是BufferDescriptors[]數(shù)組的下標+1后的值,而每次取buf描述符時,從strategy->current值開始進行選擇。選出的不可用后,依次向后進行遍歷,遍歷到頭后從頭再來進行選擇,即形成一個環(huán)。是否可用的標準后文詳述。

下面說下BufferDesc成員變量:

  • BufferTag tag為一個描述符對應磁盤物理頁的映射。即space ID+database ID+文件ID -- forkNum(表文件還是fsm文件或者vm文件)-- 頁號

  • buf_id為buffer數(shù)組BufferBlocks[]的下標

  • state為狀態(tài)標記,包括該buffer的refcount和usagecount以及是否合法valid等待

  • wait_backend_pid:若進程A需要刪除的元組所在緩沖塊有其他進程訪問,即refcount>0時,進程A不能物理上刪除元組。系統(tǒng)將該進程的ID記錄在wait_backend_id上,然后對緩沖塊加pin,并阻塞自己。當refcount為1時最后一個使用該緩沖塊的進程釋放緩沖區(qū)時,會向wait_backend_id進程發(fā)送消息。

  • FreeNext為鏈表的下一個節(jié)點的下標

  • content_lock為buffer鎖,當進程訪問緩沖塊時加鎖,讀加LW_SHARE鎖,寫加LW_EXCLUSIVE鎖

二、共享buffer分配機制

圖解PostgreSQL--buffer的分配

1、前期準備:

1)該buffer分配有4種情況:從hash表SharedBufHash中查找;從環(huán)形緩沖區(qū)查找;從free list查找以及驅(qū)逐策略進行分配。

2)hash表SharedBufHash同樣是共享內(nèi)存全局的,所有進程公有。下面分別是兩個會話連接的server端進程打印出的hash表。

(gdb) p SharedBufHash
$1 = (HTAB *) 0x87f5b04
 
(gdb) p SharedBufHash
$1 = (HTAB *) 0x87f5b04

3)該hash表同樣在InitBufferPool中進行創(chuàng)建:

StrategyInitialize->InitBufTable(NBuffers + NUM_BUFFER_PARTITIONS)->
SharedBufHash = ShmemInitHash

4)該hash表中條目為:[BufferTag,id]即key值為物理磁盤頁的標志,id為對應buffer的ID

5)首先需要創(chuàng)建一個newTag,對應物理文件的一個頁

6)通過newTag到函數(shù)BufTableHashCode中計算hash表的key值newHash

7)共有128個buffer partition鎖,通過hash的key值以輪詢的方式取鎖

8)此時對key值對應的buffer partition加LW_SHARED鎖

2、此時進入第一種獲取buffer描述符的方法:所有進程共享的SharedBufHash

1)根據(jù)newTag從hash表SharedBufHash中查找對應的buffer

2)buf_id>0則表示數(shù)據(jù)頁在hash表中找到,即對應數(shù)據(jù)頁以加載到內(nèi)存

3)根據(jù)buf_id獲取buffer的描述符BufferDescriptors[buf_id)].bufferdesc

4)通過函數(shù)PinBuffer將對應buffer pin住,然后就可以將buffer的partition鎖釋放

   即,將buf的state的refcount+1,usagecount根據(jù)情況+1,具體流程下文分析。

5)pin失敗,通過StartBufferIO判斷,返回TRUE,緩沖區(qū)無效,此時foundPtr為false,并返回對應buf;返回false,表示別人正在使用,直接返回對應buf。foundPtr表示是否在緩沖區(qū)命中

3、若hash表中不存在,則需要從磁盤讀取。首先釋放buf的partition鎖,進入循環(huán)。

1)StrategyGetBuffer取出一個buf描述符,具體原理見下文。

2)PinBuffer_Locked將buf的refcount+1

3)此時該buf為臟塊BM_DIRTY,則對buf->content_lock加LW_SHARED鎖,加鎖失敗釋放pin,返回1)。加鎖成功根據(jù)strategy是否為空處理。

4)使用環(huán)形緩沖區(qū),即strategy不為空:BM_LOCKED鎖內(nèi)獲取buf臟頁的lsn,根據(jù)lsn判斷其日志是否已經(jīng)刷寫到磁盤,若未則將該buf從環(huán)形緩沖區(qū)刪除;釋放buf->content_lock鎖及pin,返回1)重新循環(huán)進行選擇。

5)使用環(huán)形緩沖區(qū)且日志已刷或者未使用環(huán)形緩沖區(qū),則調(diào)用FlushBuffer將臟數(shù)據(jù)刷寫磁盤,最后釋放buf->content_lock鎖。

6)接著進入4,當該頁不為臟時也進入4

4、替換為自己的tag

1)先獲取buf的oldTag,是誰用過。其oldPartitionLock和newTag的newPartitionLock按順序加鎖,若同一個則只加一個鎖。LW_EXCUSIVE

2)將newTag對應的條目插入到hash表SharedBufHash

3)buf_id>=0,表示該條目已在hash表,那么unpin、oldPartitionLock鎖釋放后,獲取老buf,pin后釋放newPartitionLock

4)pin失敗,通過StartBufferIO判斷,返回TRUE,緩沖區(qū)無效,此時foundPtr為false,并返回對應buf;返回false,表示別人正在使用,直接返回對應buf。foundPtr表示是否在緩沖區(qū)命中 5)buf_id<0,即未在hash表SharedBufHash:buf_state的refcount==1且不為BM_DIRTY,表示無人使用該buf,退出循環(huán),將buf->tag=newTag,最后釋放相關(guān)鎖

6)否則,需要釋放相關(guān)鎖,并將newTag對應的條目從hash表刪除后,重新回到3進行選擇。

三、幾個子函數(shù)

1、PinBuffer

圖解PostgreSQL--buffer的分配

1、若buffer的state已為BM_LOCKED即已加鎖,則需等待,該鎖是pin鎖

2、GetPrivateRefCountEntry獲取ref,若ref不為NULL,則表示別人在使用,然后TRUE。是這樣理解嗎?需要理解這個函數(shù)

3、原子操作讀取state值old_buf_state,并將之保存為buf_state

4、buf_state的refcount+1

5、默認策略下,即從free list中選擇空閑描述符,buf_state的usagecount+1;環(huán)形緩沖區(qū)策略下,buf_state的usagecount保持為1

6、通過CAS操作將buf->state的值替換為buf_state的值

7、函數(shù)返回TRUE表示該buffer的數(shù)據(jù)有效,即合法的數(shù)據(jù)已經(jīng)加載到內(nèi)存;返回false表示數(shù)據(jù)無效,即數(shù)據(jù)未加載到內(nèi)存

2、StartBufferIO:開啟IO,將buf狀態(tài)置為BM_IO_IN_PROGRESS

圖解PostgreSQL--buffer的分配

1、每個buffer都有一個IO鎖(BufferIOLWLockArray[(bdesc)->buf_id]).lock

2、獲取buf_state狀態(tài),需要先將其置為BM_LOCKED

3、該buf此時已為BM_IO_IN_PROGRESS,即正在讀寫,需要將上面兩個鎖釋放后WaitIO等待狀態(tài)變化

4、forInput為TRUE:要向里面寫,需要其為!BM_VALID,若是BM_VALID表示有人已經(jīng)向里寫了合法數(shù)據(jù);FALSE:需要向外讀,若為!BM_DIRTY表示已有人刷寫了。釋放兩個鎖返回

5、將buf_state置為BM_IO_IN_PROGRESS。

6、返回TRUE,表示buf中數(shù)據(jù)無效,可以使用。False,表示別人正在使用

3、StrategyGetBuffer

圖解PostgreSQL--buffer的分配

1、如果使用strategy,則從環(huán)形緩沖區(qū)取一個空閑的描述符:bufnum=strategy->buffers[strategy->current];buf = GetBufferDescriptor(bufnum - 1);,若沒有可用的則GetBufferFromRing返回NULL,否則直接返回該buf。

2、環(huán)形緩沖區(qū)取buffer失敗,則去free list取

3、StrategyControl->firstFreeBuffer>0,此時list不為空,

4、則先申請spin鎖StrategyControl->buffer_strategy_lock,再次判斷鏈表情況,若StrategyControl->firstFreeBuffer<0鏈表空了,則釋放鎖后退出循環(huán),進入第8步

5、獲取StrategyControl->firstFreeBuffer指向的buffer描述符,并將該節(jié)點從free list刪除

6、釋放StrategyControl->buffer_strategy_lock鎖

7、該buf的refcount和usagecount都為0,則表示我們可以用,若strategy不為NULL,則現(xiàn)將該buf放到環(huán)形緩沖區(qū),返回該buffer描述符;否則再次到第4步循環(huán)

8、此時free list都取了一遍,但是沒有可用的,通過時鐘算法,即循環(huán)StrategyControl->nextVictimBuffer取該buf,看其是否可用。同樣如果找到后,根據(jù)strategy是否為NULL,將其放到環(huán)形緩沖區(qū)。將所有buf都取了一遍后,仍沒有可用的話就報錯:no unpinned buffers available

向AI問一下細節(jié)

免責聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點不代表本網(wǎng)站立場,如果涉及侵權(quán)請聯(lián)系站長郵箱:is@yisu.com進行舉報,并提供相關(guān)證據(jù),一經(jīng)查實,將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI