圖解PostgreSQL--buffer的分配
一、資料結構
1、Buffer由陣列BufferDescriptor[]陣列進行管理。該陣列由函式InitBufferPool建立,大小為NBuffers個成員即BufferDesc。該陣列建立後由StrategyControl進行管理,firstFreeBuffer為連結串列頭,指向連結串列第一個成員;lastFreeBuffer指向連結串列尾;所有free list中成員由freeNext串起來,該值為陣列下標。
BufferDescriptor陣列是共享記憶體中申請,所有程式共享。可以看到兩個程式的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、同時還會通過一個環形區進行管理這些陣列成員。當進行大表掃描時使用。由strategy->buffers[]陣列管理,該陣列儲存的是BufferDescriptors[]陣列的下標+1後的值,而每次取buf描述符時,從strategy->current值開始進行選擇。選出的不可用後,依次向後進行遍歷,遍歷到頭後從頭再來進行選擇,即形成一個環。是否可用的標準後文詳述。
下面說下BufferDesc成員變數:
-
BufferTag tag為一個描述符對應磁碟物理頁的對映。即space ID+database ID+檔案ID -- forkNum(表檔案還是fsm檔案或者vm檔案)-- 頁號
-
buf_id為buffer陣列BufferBlocks[]的下標
-
state為狀態標記,包括該buffer的refcount和usagecount以及是否合法valid等待
-
wait_backend_pid:若程式A需要刪除的元組所在緩衝塊有其他程式訪問,即refcount>0時,程式A不能物理上刪除元組。系統將該程式的ID記錄在wait_backend_id上,然後對緩衝塊加pin,並阻塞自己。當refcount為1時最後一個使用該緩衝塊的程式釋放緩衝區時,會向wait_backend_id程式傳送訊息。
-
FreeNext為連結串列的下一個節點的下標
-
content_lock為buffer鎖,當程式訪問緩衝塊時加鎖,讀加LW_SHARE鎖,寫加LW_EXCLUSIVE鎖
二、共享buffer分配機制
1、前期準備:
1)該buffer分配有4種情況:從hash表SharedBufHash中查詢;從環形緩衝區查詢;從free list查詢以及驅逐策略進行分配。
2)hash表SharedBufHash同樣是共享記憶體全域性的,所有程式公有。下面分別是兩個會話連線的server端程式列印出的hash表。
(gdb) p SharedBufHash $1 = (HTAB *) 0x87f5b04 (gdb) p SharedBufHash $1 = (HTAB *) 0x87f5b04
3)該hash表同樣在InitBufferPool中進行建立:
StrategyInitialize->InitBufTable(NBuffers + NUM_BUFFER_PARTITIONS)-> SharedBufHash = ShmemInitHash
4)該hash表中條目為:[BufferTag,id]即key值為物理磁碟頁的標誌,id為對應buffer的ID
5)首先需要建立一個newTag,對應物理檔案的一個頁
6)通過newTag到函式BufTableHashCode中計算hash表的key值newHash
7)共有128個buffer partition鎖,通過hash的key值以輪詢的方式取鎖
8)此時對key值對應的buffer partition加LW_SHARED鎖
2、此時進入第一種獲取buffer描述符的方法:所有程式共享的SharedBufHash
1)根據newTag從hash表SharedBufHash中查詢對應的buffer
2)buf_id>0則表示資料頁在hash表中找到,即對應資料頁以載入到記憶體
3)根據buf_id獲取buffer的描述符BufferDescriptors[buf_id)].bufferdesc
4)通過函式PinBuffer將對應buffer pin住,然後就可以將buffer的partition鎖釋放
即,將buf的state的refcount+1,usagecount根據情況+1,具體流程下文分析。
5)pin失敗,通過StartBufferIO判斷,返回TRUE,緩衝區無效,此時foundPtr為false,並返回對應buf;返回false,表示別人正在使用,直接返回對應buf。foundPtr表示是否在緩衝區命中
3、若hash表中不存在,則需要從磁碟讀取。首先釋放buf的partition鎖,進入迴圈。
1)StrategyGetBuffer取出一個buf描述符,具體原理見下文。
2)PinBuffer_Locked將buf的refcount+1
3)此時該buf為髒塊BM_DIRTY,則對buf->content_lock加LW_SHARED鎖,加鎖失敗釋放pin,返回1)。加鎖成功根據strategy是否為空處理。
4)使用環形緩衝區,即strategy不為空:BM_LOCKED鎖內獲取buf髒頁的lsn,根據lsn判斷其日誌是否已經刷寫到磁碟,若未則將該buf從環形緩衝區刪除;釋放buf->content_lock鎖及pin,返回1)重新迴圈進行選擇。
5)使用環形緩衝區且日誌已刷或者未使用環形緩衝區,則呼叫FlushBuffer將髒資料刷寫磁碟,最後釋放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,緩衝區無效,此時foundPtr為false,並返回對應buf;返回false,表示別人正在使用,直接返回對應buf。foundPtr表示是否在緩衝區命中 5)buf_id<0,即未在hash表SharedBufHash:buf_state的refcount==1且不為BM_DIRTY,表示無人使用該buf,退出迴圈,將buf->tag=newTag,最後釋放相關鎖
6)否則,需要釋放相關鎖,並將newTag對應的條目從hash表刪除後,重新回到3進行選擇。
三、幾個子函式
1、PinBuffer
1、若buffer的state已為BM_LOCKED即已加鎖,則需等待,該鎖是pin鎖
2、GetPrivateRefCountEntry獲取ref,若ref不為NULL,則表示別人在使用,然後TRUE。是這樣理解嗎?需要理解這個函式
3、原子操作讀取state值old_buf_state,並將之儲存為buf_state
4、buf_state的refcount+1
5、預設策略下,即從free list中選擇空閒描述符,buf_state的usagecount+1;環形緩衝區策略下,buf_state的usagecount保持為1
6、通過CAS操作將buf->state的值替換為buf_state的值
7、函式返回TRUE表示該buffer的資料有效,即合法的資料已經載入到記憶體;返回false表示資料無效,即資料未載入到記憶體
2、StartBufferIO:開啟IO,將buf狀態置為BM_IO_IN_PROGRESS
1、每個buffer都有一個IO鎖(BufferIOLWLockArray[(bdesc)->buf_id]).lock
2、獲取buf_state狀態,需要先將其置為BM_LOCKED
3、該buf此時已為BM_IO_IN_PROGRESS,即正在讀寫,需要將上面兩個鎖釋放後WaitIO等待狀態變化
4、forInput為TRUE:要向裡面寫,需要其為!BM_VALID,若是BM_VALID表示有人已經向裡寫了合法資料;FALSE:需要向外讀,若為!BM_DIRTY表示已有人刷寫了。釋放兩個鎖返回
5、將buf_state置為BM_IO_IN_PROGRESS。
6、返回TRUE,表示buf中資料無效,可以使用。False,表示別人正在使用
3、StrategyGetBuffer
1、如果使用strategy,則從環形緩衝區取一個空閒的描述符:bufnum=strategy->buffers[strategy->current];buf = GetBufferDescriptor(bufnum - 1);,若沒有可用的則GetBufferFromRing返回NULL,否則直接返回該buf。
2、環形緩衝區取buffer失敗,則去free list取
3、StrategyControl->firstFreeBuffer>0,此時list不為空,
4、則先申請spin鎖StrategyControl->buffer_strategy_lock,再次判斷連結串列情況,若StrategyControl->firstFreeBuffer<0連結串列空了,則釋放鎖後退出迴圈,進入第8步
5、獲取StrategyControl->firstFreeBuffer指向的buffer描述符,並將該節點從free list刪除
6、釋放StrategyControl->buffer_strategy_lock鎖
7、該buf的refcount和usagecount都為0,則表示我們可以用,若strategy不為NULL,則現將該buf放到環形緩衝區,返回該buffer描述符;否則再次到第4步迴圈
8、此時free list都取了一遍,但是沒有可用的,通過時鐘演算法,即迴圈StrategyControl->nextVictimBuffer取該buf,看其是否可用。同樣如果找到後,根據strategy是否為NULL,將其放到環形緩衝區。將所有buf都取了一遍後,仍沒有可用的話就報錯:no unpinned buffers available
來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/31493717/viewspace-2671549/,如需轉載,請註明出處,否則將追究法律責任。
相關文章
- 圖解PostgreSQL--local buffer的分配圖解SQL
- 圖解Go語言記憶體分配圖解Go記憶體
- 無題號 分配問題 題解
- 帶你瞭解地址分配DHCP,IP地址管理方式及分配原則
- 有未分配的磁碟卻不能擴充套件卷怎麼解決 有未分配的磁碟卻不能擴充套件卷解決方法套件
- [CSP-S 2021] 廊橋分配 題解
- 作業系統綜合題之“銀行家演算法,畫出試分配後的資源分配狀態圖”作業系統演算法
- JVM的棧上分配JVM
- RMAN中的通道分配
- LMT下extent的分配
- RocketMq中MessageQueue的分配MQ
- Java中的地址分配Java
- 穩定的牛分配
- 物件的建立和分配物件
- 詳解flex佈局的元素如何分配容器的剩餘空間Flex
- 記憶體分配詳解 malloc, new, HeapAlloc, VirtualAlloc,GlobalAlloc記憶體
- SSR自動分配的規則
- SAP RETAIL 分配表功能的使用AI
- 解除分配UNASSIGN
- cache:寫直達、寫回、寫分配、寫不分配
- 【簡易圖解】『 OAuth2.0』 猴子都能懂的圖解圖解OAuth
- C語言的記憶體分配C語言記憶體
- C中的記憶體分配模型記憶體模型
- hbase啟動時分配region的流程
- 圖解 -- 樹的彙總圖解
- 類的繼承圖解繼承圖解
- 圖解js的繼承圖解JS繼承
- SAP UI5 SimpleForm M 和 L 型表單的 label 和 input 分配講解UIORM
- 請問什麼時候物件分配會不在 TLAB 內分配物件
- Netty 中的記憶體分配淺析Netty記憶體
- Google的70/20/10分配法則Go
- [20181229]關於字串的分配問題.txt字串
- 檔案系統的物理結構分配
- IPv6基於策略的地址分配
- CubeMx的部分配置顯示不出來
- SAP RETAIL分配表的查詢報表AI
- Go記憶體分配和GC的理解Go記憶體GC
- odoo 許可權分配Odoo