聊聊 PG 的 Buffer 相關鎖,你懂了嗎?
?昨天剛剛下飛機就接到電話說一個長輩去世,今天一早坐高鐵回老家參加喪禮。所以這篇前兩天寫了個頭的文章今天是在高鐵上完成的,有些實驗不方便做,就只能簡化了。
前兩天一個朋友說PG的熱塊沖突比Oracle更容易產生,并會產生比較嚴重的性能問題,特別是當系統中的一些大型的熱表存在大量UPDATE操作的時候。確實PG的ASTORE機制使用多個版本的TUPLE來保存某一行的歷史版本,這種機制導致了PG的SHARED BUFFERS的鎖會比較復雜。和朋友討論問題后,我根據以前學習過的一些關于PG BUFFER的知識,畫了一個思維導圖。
PG的BUFFER 居然和三種鎖有關,一種是SPINLOCK,用于管理BUFFER的空閑鏈的,如果要分配空閑緩沖區,則需要通過一個SPINLOCK(Buffer Strategy Lock)來獲得。另外兩類異類是我們比較容易理解的用于保護PG內存結構的鎖LWLOCK。最后異類就比較令人費解了,如果我們看PG的等待事件,里面有一類獨特的分類。
這類等待事件稱為BufferPin,而這個等待事件大類里面只有一種等待事件,BufferPIN。這些鎖之間都是什么關系呢?我們可以看上面的思維導圖。
比如我們模擬一個BUFFER的一生,首先當要訪問某個PG PAGE的時候,先要從FREE的BUFFER中找到一個,此時需要一個SPINLOCK(Buffer Strategy Lock),然后從FREELIST上取下BUFFER,準備給新的PAGE使用,此時我們需要PIN住這個BUFFER,使之不能被BUFFER替換等操作使用。
然后需要申請一個BUFFER CONTENT鎖,來修改這個BUFFER,通過加buffer header lock來修改BUFFER頭上的訪問指針計數器等信息。然后就要開始讀取PAGE的IO操作了,此時需要獲得一個BUFFER IO鎖,指示該BUFFER正在進行IO操作,從而避免在同一個BUFFER上的多個IO并發進行。
IO結束后,這個BUFFER中已經包含了我們所需要的PAGE,此時我們需要把這個BUFFER加入到HASH CHAINS里,此時就需要一個buffer mapping鎖,從而便于今后BUFFER掃描定位,這個鎖有點類似Oracle的CBC閂鎖,也是多個鎖分區管理的,PG使用多個分區來提高并行效率。
下面我們來看一個例子:
此時我們在另外一個會話里查看一下BUFFER PIN的情況:
可以看到一個BUFFER是被PIN住了。此時我們如果執行VACCUM會發生什么呢?
可以看到VACUUM跳過了被PIN住的BUFFER,因為針對PIN住的BUFFER,PG無法對其中的PAGE做VACUUM這樣的不兼容的操作。
此時如果做不兼容的vacuum freeze操作就會被鎖住,要等待BUFFER PIN被移除。BUFFER PIN是一個共享鎖,不會阻塞同一個PAGE上的并發寫操作,不過這個共享鎖還是會產生一些并發互斥的操作,比如會阻止VACUUM對這個PAGE進行回收整理操作,使VACUUM操作跳過這個PAGE,會阻止FREEZE操作,直到PIN住該BUFFER的所有鎖全部移除。
因為PG數據庫采用的是APPEND STORE模式,因此一個行的UPDATE會產生多個行副本,這對于PG的數據行的訪問操作來說會增加額外的成本,在這里我們還需要考慮索引訪問的成本問題。如果這些記錄副本都存儲在同一個PAGE里,那么處理起來成本相對還比較低,PG采用HOT來降低索引的維護和訪問成本。
如果多個TUPLE是分布在多個PAGE中,那么這個成本的增加就不可避免了。如果我們的應用系統中的某些表上的UPDATE十分頻繁,那么這種額外的成本就會更大。再加上PG在訪問數據時的各種鎖的開銷,這個疊加成本就更大了。
以VACUUM為例,如果我們的應用出現了BUG,打開一個CURSOR后忘記關閉了,或者一個死會話沒有釋放相關的CURSOR,那么某個或者某些BUFFER會被長時間PIN住,VACUUM每次都會跳過這些PAGE,時間長了,就會引發一些莫名其妙的問題。
希望今天看了這篇文章后,我們再去看PG等待事件中關于BUFFER的事件,可以更準確的了解到哪些等待事件代表什么含義,從而可以更好的定位問題。
正是因為PG的這種特性,在使用PG數據庫的時候我們不能像使用Oracle那樣肆無忌憚,如果做UPDATE操作,盡可能優化應用邏輯,讓一條數據的UPDATE次數盡可能的減少。另外對于UPDATE十分頻繁的表,或者需要對很多列進行UPDATE的寬表,其表的FILLFACTOR參數要適當減少,盡可能利用HOT來優化訪問性能。另外,如果某張經常UPDATE的寬表是可以分拆的,那么盡可能把這張表分拆為多張表。
我和很多使用PG數據庫的人交流過,有些人就說PG很好用,我們用了PG后系統一直都很穩定。有些朋友就說經常踩坑。實際上很多數據庫都是有各種各樣的坑的,如果你知道坑的存在,那就不容易踩坑了。有坑不可怕,不知道前面有坑才更可怕。