解鎖 Postgres 鎖管理器。 布魯斯·莫吉安

Bruce Momjian 2020 年演講「解鎖 Postgres 鎖定管理器」的文字記錄。

解鎖 Postgres 鎖管理器。 布魯斯·莫吉安

(註:幻燈片中的所有 SQL 查詢均可從此連結取得: http://momjian.us/main/writings/pgsql/locking.sql)

你好! 很高興再次來到俄羅斯。 很抱歉去年我沒能來,但今年伊凡和我有宏偉的計劃。 我希望能更頻繁地來這裡。 我喜歡來俄羅斯。 我將訪問 秋明, 特維爾 。 我很高興能夠造訪這些城市。

我叫布魯斯‧莫吉安。 我在 EnterpriseDB 工作,使用 Postgres 已經超過 23 年了。 我住在美國費城。 我每年大約有 90 天旅行。 我參加了大約 40 個會議。 我的 網站,其中包含我現在向您展示的幻燈片。 因此,會後大家可以到我的個人網站下載。 它還包含大約 30 個簡報。 還有影片和大量部落格文章,超過 500 個。這是一個資訊量相當大的資源。 如果您對此材料感興趣,那麼我邀請您使用它。

在開始使用 Postgres 之前,我曾經是一名教師、教授。 我很高興現在能夠告訴你們我將要告訴你們的事。 這是我最有趣的演講之一。 本簡報包含 110 張投影片。 我們會從簡單的事情開始講,到最後報告會變得越來越複雜,變得相當複雜。

解鎖 Postgres 鎖管理器。 布魯斯·莫吉安

這是一次相當不愉快的談話。 阻止並不是最受歡迎的主題。 我們希望它在某個地方消失。 這就像去看牙醫一樣。

解鎖 Postgres 鎖管理器。 布魯斯·莫吉安

  1. 對於許多從事資料庫工作並且同時運行多個進程的人來說,鎖定是一個問題。 他們需要阻止。 也就是今天我給大家科普一下阻塞的基礎。
  2. 交易 ID。 這是演示中相當無聊的部分,但需要理解它們。
  3. 接下來我們將討論阻塞的類型。 這是一個相當機械的部分。
  4. 下面我們將舉一些阻塞的例子。 這將是相當難以理解的。

解鎖 Postgres 鎖管理器。 布魯斯·莫吉安

我們來談談阻塞。

解鎖 Postgres 鎖管理器。 布魯斯·莫吉安

我們的術語相當複雜。 有多少人知道這段話出自哪裡? 兩個人。 這是來自一款名為《巨大洞穴冒險》的遊戲。 我認為這是 80 年代的一款基於文字的電腦遊戲。 在那裡你必須進入一個山洞,進入一個迷宮,文字發生了變化,但內容每次都大致相同。 這就是我對這場比賽的記憶。

解鎖 Postgres 鎖管理器。 布魯斯·莫吉安

在這裡我們看到了來自 Oracle 的鎖的名稱。 我們使用它們。

解鎖 Postgres 鎖管理器。 布魯斯·莫吉安

在這裡我們看到了令我困惑的術語。 例如,分享更新獨家。 下一頁 分享 RAW EXXLUSIVE。 說實話,這些名字都不是很清楚。 我們將嘗試更詳細地考慮它們。 有些帶有“分享”一詞,意思是分離。 有些含有“獨家”一詞。 有些包含這兩個字。 我想從這些鎖的工作原理開始。

解鎖 Postgres 鎖管理器。 布魯斯·莫吉安

而「訪問」這個詞也很重要。 「row」這個字是一個字串。 即訪問分配、行分配。

解鎖 Postgres 鎖管理器。 布魯斯·莫吉安

Postgres 中另一個需要理解的問題是 MVCC,遺憾的是我無法在演講中討論這個問題。 我的網站上有關於這個主題的單獨演示。 如果你認為這個演示很難,MVCC 可能是我最難的。 如果您有興趣,可以在網站上觀看。 您可以觀看影片。

解鎖 Postgres 鎖管理器。 布魯斯·莫吉安

我們需要了解的另一件事是交易 ID。 如果沒有唯一標識符,許多交易就無法進行。 在這裡我們解釋了什麼是交易。 Postgres 有兩個事務編號系統。 我知道這不是一個非常漂亮的解決方案。

解鎖 Postgres 鎖管理器。 布魯斯·莫吉安

另請記住,投影片將很難理解,因此以紅色突出顯示的內容是您需要注意的內容。

解鎖 Postgres 鎖管理器。 布魯斯·莫吉安

http://momjian.us/main/writings/pgsql/locking.sql

讓我們來看看。 交易編號以紅色突出顯示。 此處顯示了 SELECT pg_back 函數。 它返回我的交易和交易 ID。

還有一件事,如果您喜歡此簡報並希望在您的資料庫上運行它,那麼您可以轉到此粉紅色連結並下載此簡報的 SQL。 您只需在 PSQL 中運行它,整個簡報就會立即顯示在您的螢幕上。 它不會包含鮮花,但至少我們可以看到它。

解鎖 Postgres 鎖管理器。 布魯斯·莫吉安

在本例中,我們看到交易 ID。 這是我們分配給她的號碼。 而Postgres中還有另一種類型的事務ID,稱為虛擬事務ID

我們必須理解這一點。 這一點非常重要,否則我們將無法理解Postgres中的鎖定。

虛擬事務ID是不包含持久值的事務ID。 例如,如果我執行 SELECT 命令,那麼我很可能不會更改資料庫,也不會鎖定任何內容。 因此,當我們執行簡單的 SELECT 時,我們不會為該交易提供持久 ID。 我們只給她一個虛擬身分證。

而這提高了Postgres的效能,並提高了清理能力,因此虛擬事務ID由兩個數字組成。 斜線之前的第一個數字是後端 ID。 在右邊我們只看到一個櫃檯。

解鎖 Postgres 鎖管理器。 布魯斯·莫吉安

因此,如果我執行一個請求,它會說後端 ID 是 2。

解鎖 Postgres 鎖管理器。 布魯斯·莫吉安

如果我運行一系列此類事務,那麼我們會看到每次執行查詢時計數器都會增加。 例如,當我執行查詢 2/10、2/11、2/12 等。

解鎖 Postgres 鎖管理器。 布魯斯·莫吉安

請記住,這裡有兩列。 在左側我們看到虛擬交易 ID – 2/12。 在右邊我們有一個永久的交易ID。 並且這個欄位是空的。 並且這個事務不會修改資料庫。 所以我沒有給它一個永久的交易ID。

解鎖 Postgres 鎖管理器。 布魯斯·莫吉安

一旦我執行分析指令 ((ANALYZE)),相同的查詢就會給我一個永久的交易 ID。 看看這對我們來說有何改變。 我以前沒有這個ID,但現在我有了。

解鎖 Postgres 鎖管理器。 布魯斯·莫吉安

這是另一個請求,另一個交易。 虛擬交易數量為2/13。 如果我請求持久性事務 ID,那麼當我執行查詢時,我會得到它。

解鎖 Postgres 鎖管理器。 布魯斯·莫吉安

那麼,再來一次吧。 我們有一個虛擬事務ID和一個持久事務ID。 只要理解這一點就可以理解 Postgres 的行為。

解鎖 Postgres 鎖管理器。 布魯斯·莫吉安

我們繼續第三部分。 這裡我們將簡單介紹一下 Postgres 中不同類型的鎖。 這不是很有趣。 最後一部分會更有趣。 但我們必須考慮基本的事情,否則我們將無法理解接下來會發生什麼。

我們將瀏覽本節,我們將了解每種類型的鎖。 我將向您展示它們如何安裝、如何運作的範例,我將向您展示一些查詢,您可以使用它們來了解鎖定在 Postgres 中的工作原理。

解鎖 Postgres 鎖管理器。 布魯斯·莫吉安

要建立查詢並查看 Postgres 中發生的情況,我們需要在系統視圖中發出查詢。 在這種情況下,pg_lock 以紅色突出顯示。 pg_lock 是一個系統表,它告訴我們 Postgres 目前正在使用哪些鎖。

然而,我很難向您展示 pg_lock 本身,因為它非常複雜。 所以我建立了一個顯示 pg_locks 的視圖。 它也為我做了一些工作,讓我更能理解。 也就是說,它排除了我的鎖、我自己的會話等。它只是標準 SQL,它可以讓您更好地向您展示發生了什麼。

解鎖 Postgres 鎖管理器。 布魯斯·莫吉安

另一個問題是這個視圖非常寬,所以我必須建立第二個視圖 - lockview2。

解鎖 Postgres 鎖管理器。 布魯斯·莫吉安 它向我顯示了表中的更多列。 另一篇向我展示了其餘的專欄。 這是相當複雜的,所以我試著盡可能簡單地介紹它。

解鎖 Postgres 鎖管理器。 布魯斯·莫吉安

因此我們建立了一個名為 Lockdemo 的表。 我們在那裡創建了一條線。 這是我們的範例表。 我們將創建一些部分只是為了向您展示鎖的範例。

解鎖 Postgres 鎖管理器。 布魯斯·莫吉安

所以,一行,一列。 第一種類型的鎖稱為 ACCESS SHARE。 這是限制最少的阻止。 這意味著它實際上不會與其他鎖衝突。

如果我們想明確定義鎖,我們可以執行“lock table”命令。 它顯然會阻塞,即在 ACCESS SHARE 模式下我們啟動鎖定表。 如果我在背景執行 PSQL,那麼我會以這種方式從第一個會話開始第二個會話。 也就是說,我在這裡做什麼? 我轉到另一個會話並告訴它“顯示此請求的鎖定視圖”。 在這裡,我在這個表中有 AccessShareLock。 這正是我所要求的。 他說該區塊已被分配。 很簡單。

解鎖 Postgres 鎖管理器。 布魯斯·莫吉安

此外,如果我們看第二列,那麼那裡什麼都沒有。 他們是空的。

解鎖 Postgres 鎖管理器。 布魯斯·莫吉安

如果我執行「SELECT」命令,那麼這就是請求 AccessShareLock 的隱式(顯式)方式。 因此,我釋放表並運行查詢,查詢返回多行。 在其中一行我們看到了 AccessShareLock。 因此,SELECT 對錶呼叫 AccessShareLock。 而且它幾乎不會與任何東西衝突,因為它是一個低階鎖。

解鎖 Postgres 鎖管理器。 布魯斯·莫吉安

如果我運行 SELECT 並擁有三個不同的表怎麼辦? 以前我只運行一個表,現在我運行三個表:pg_class、pg_namespace 和 pg_attribute。

解鎖 Postgres 鎖管理器。 布魯斯·莫吉安

現在,當我查看查詢時,我在三個表中看到 9 個 AccessShareLock。 為什麼? 三個表以藍色突出顯示:pg_attribute、pg_class、pg_namespace。 但您也可以看到,透過這些表定義的所有索引也都具有 AccessShareLock。

而且這是一把幾乎不會與其他鎖衝突的鎖。 它所做的只是阻止我們在選擇表時重置表。 這說得通。 也就是說,如果我們選擇一個表,那一刻它就消失了,那麼這是錯誤的,所以 AccessShare 是一個低階鎖,它告訴我們“在我工作時不要刪除此表”。 本質上,這就是她所做的一切。

解鎖 Postgres 鎖管理器。 布魯斯·莫吉安

ROW SHARE - 這個鎖有點不同。

解鎖 Postgres 鎖管理器。 布魯斯·莫吉安

讓我們舉個例子。 單獨鎖定每一行的 SELECT ROW SHARE 方法。 這樣,當我們觀看它們時,沒有人可以刪除它們或更改它們。

解鎖 Postgres 鎖管理器。 布魯斯·莫吉安那麼SHARE LOCK有什麼作用呢? 我們看到 SELECT 的交易 ID 是 681。 這很有趣。 這裡發生了什麼事? 我們第一次看到這個數字是在「鎖定」欄位中。 我們取得交易 ID,它表示它正在以獨佔模式阻止它。 它所做的只是說我有一行在技術上被鎖定在表中的某個位置。 但他沒有說具體在哪裡。 稍後我們將更詳細地討論這一點。

解鎖 Postgres 鎖管理器。 布魯斯·莫吉安

這裡說鎖是我們自己用的。

解鎖 Postgres 鎖管理器。 布魯斯·莫吉安

因此,獨佔鎖明確表示它是獨佔的。 而且,如果您刪除該表中的一行,那麼將會發生這種情況,如您所見。

解鎖 Postgres 鎖管理器。 布魯斯·莫吉安

SHARE EXCLUSIVE 是一個較長的鎖。

解鎖 Postgres 鎖管理器。 布魯斯·莫吉安

這是將使用的 (ANALYZE) 分析器命令。

解鎖 Postgres 鎖管理器。 布魯斯·莫吉安

共享鎖定 – 您可以在共享模式下明確鎖定。

解鎖 Postgres 鎖管理器。 布魯斯·莫吉安

您也可以建立唯一索引。 在那裡你可以看到共享鎖,它是它們的一部分。 它鎖定表並在其上放置共享鎖。

預設情況下,表上的 SHARE LOCK 表示其他人可以讀取該表,但沒有人可以修改它。 這正是創建唯一索引時發生的情況。

如果我建立一個唯一的並發索引,那麼我將擁有不同類型的鎖定,因為如您所知,使用並發索引會減少鎖定要求。 如果我使用普通鎖定、普通索引,那麼我將在建立表索引時防止寫入該表索引。 如果我使用並發索引,那麼我需要使用不同類型的鎖定。

解鎖 Postgres 鎖管理器。 布魯斯·莫吉安

SHARE ROW EXCLUSIVE – 同樣可以明確設定(明確)。

解鎖 Postgres 鎖管理器。 布魯斯·莫吉安

或者我們可以建立一個規則,即採用將使用該規則的特定情況。

解鎖 Postgres 鎖管理器。 布魯斯·莫吉安

獨佔鎖定意味著沒有其他人可以更改該表。

解鎖 Postgres 鎖管理器。 布魯斯·莫吉安

在這裡我們看到不同類型的鎖。

解鎖 Postgres 鎖管理器。 布魯斯·莫吉安

例如,ACCESS EXCLUSIVE 就是一個阻塞指令。 例如,如果你這樣做 CLUSTER table,那麼這將意味著沒有人能夠在那裡書寫。 它不僅鎖定表本身,還鎖定索引。

解鎖 Postgres 鎖管理器。 布魯斯·莫吉安

這是 ACCESS EXCLUSIVE 阻塞的第二頁,我們可以在其中準確地看到它在表中阻塞的內容。 它鎖定各個表行,這非常有趣。

這就是我想提供的所有基本資訊。 我們討論了鎖、事務 ID、虛擬事務 ID、永久事務 ID。

解鎖 Postgres 鎖管理器。 布魯斯·莫吉安

現在我們將介紹一些阻塞範例。 這是最有趣的部分。 我們將研究非常有趣的案例。 我在本次演示中的目標是讓您更好地了解 Postgres 在嘗試阻止某些事情時實際上在做什麼。 我認為他非常擅長遮擋零件。

讓我們來看一些具體的例子。

解鎖 Postgres 鎖管理器。 布魯斯·莫吉安

我們將從表格和表格中的一行開始。 當我插入某些內容時,表上會顯示 ExclusiveLock、交易 ID 和 ExclusiveLock。

解鎖 Postgres 鎖管理器。 布魯斯·莫吉安

如果我再插入兩行會發生什麼事? 現在我們的表格有三行。 我插入一行並將其作為輸出。 如果我再插入兩行,有什麼奇怪的? 這裡有一件奇怪的事情,因為我已經向該表添加了三行,但鎖定表中仍然有兩行。 這本質上就是 Postgres 的基本行為。

許多人認為,如果在資料庫中鎖定 100 行,那麼您將需要建立 100 個鎖定條目。 如果我一次封鎖 1​​,000 行,那麼我將需要 1 個這樣的查詢。 如果我需要一百萬或十億來阻止。 但如果我們這樣做的話,效果不會很好。 如果您使用的系統為每個單獨的行建立阻塞條目,那麼您會發現這很複雜。 因為你需要立即定義一個可以溢位的鎖表,但 Postgres 不會這麼做。

這張投影片真正重要的是,它清楚地表明了在 MVCC 內部運行的另一個系統可以鎖定各個行。 因此,當您鎖定數十億行時,Postgres 不會建立十億個單獨的鎖定命令。 而這對生產力有著非常好的影響。

解鎖 Postgres 鎖管理器。 布魯斯·莫吉安

更新怎麼樣? 我現在正在更新該行,您可以看到它同時執行了兩個不同的操作。 它同時鎖定了表,但也鎖定了索引。 而且他需要鎖定索引,因為這個表有唯一約束。 我們希望確保沒有人改變它,所以我們阻止它。

解鎖 Postgres 鎖管理器。 布魯斯·莫吉安

如果我想更新兩行會發生什麼事? 我們看到他也有同樣的行為方式。 我們進行兩倍的更新,但鎖定行的數量完全相同。

如果您想知道 Postgres 如何做到這一點,您需要聽我關於 MVCC 的演講,以了解 Postgres 如何在內部標記它更改的這些行。 Postgres 有一種方法可以做到這一點,但它不是在表格鎖定層級上執行的,而是在較低且更有效的層級上執行的。

解鎖 Postgres 鎖管理器。 布魯斯·莫吉安

如果我想刪除某些內容怎麼辦? 例如,如果我刪除一行,但仍然有兩個阻塞輸入,即使我想將它們全部刪除,它們仍然存在。

解鎖 Postgres 鎖管理器。 布魯斯·莫吉安

並且,例如,我想插入 1 行,然後刪除或添加 000 行,然後我添加或更改的那些單獨的行,它們不會記錄在這裡。 它們是在系列本身的較低層級編寫的。 在 MVCC 演講中我詳細談到了這一點。 但是,當您分析鎖定時,請確保您在表格層級進行鎖定並且您看不到此處如何記錄各個行,這一點非常重要。

解鎖 Postgres 鎖管理器。 布魯斯·莫吉安

顯式阻止怎麼樣?

解鎖 Postgres 鎖管理器。 布魯斯·莫吉安

如果我單擊刷新,則會鎖定兩行。 如果我全部選擇並單擊“到處更新”,那麼我仍然有兩個阻止記錄。

解鎖 Postgres 鎖管理器。 布魯斯·莫吉安

我們不會為每一行建立單獨的記錄。 因為這樣生產力就會下降,所以可能會出現太多的狀況。 我們可能會發現自己處於一種不愉快的境地。

解鎖 Postgres 鎖管理器。 布魯斯·莫吉安

同樣的事情,如果我們共享的話,我們可以做 30 次。

解鎖 Postgres 鎖管理器。 布魯斯·莫吉安

我們恢復表,刪除所有內容,然後再次插入一行。

解鎖 Postgres 鎖管理器。 布魯斯·莫吉安

您在 Postgres 中看到的另一個眾所周知且理想的行為是您可以執行更新或選擇。 您可以同時執行此操作。 並且 select 不會阻止更新,相反的方向也是如此。 我們告訴讀者不要封鎖作者,而作者並沒有封鎖讀者。

我將向您展示一個例子。 我現在就做出選擇。 然後我們將進行插入。 然後您可以看到 - 694。您可以看到執行此插入的交易的 ID。 這就是它的工作原理。

解鎖 Postgres 鎖管理器。 布魯斯·莫吉安

如果我現在查看我的後端 ID,現在是 695。

解鎖 Postgres 鎖管理器。 布魯斯·莫吉安

我可以看到 695 出現在我的表格中。

解鎖 Postgres 鎖管理器。 布魯斯·莫吉安

如果我像這樣在這裡更新,那麼我會得到不同的情況。 在這種情況下,695是排它鎖,並且update具有相同的行為,但它們之間沒有衝突,這是很不尋常的。

並且可以看到頂部是ShareLock,底部是ExclusiveLock。 兩筆交易都成功了。

您需要聽我在 MVCC 的演講來了解這是如何發生的。 但這是一個可以同時執行的範例,即同時執行 SELECT 和 UPDATE。

解鎖 Postgres 鎖管理器。 布魯斯·莫吉安

讓我們重置並再做一次操作。

解鎖 Postgres 鎖管理器。 布魯斯·莫吉安

如果您嘗試在同一行上同時執行兩個更新,它將被阻止。 請記住,我說讀者不會阻止寫入者,寫入者也不會阻止讀者,但一個寫入者會阻止另一個寫入者。 也就是說,我們不能讓兩個人同時更新同一行。 你必須等到其中一個完成。

解鎖 Postgres 鎖管理器。 布魯斯·莫吉安

為了說明這一點,我將查看 Lockdemo 表。 我們將看一行。 每筆交易 698。

我們已將其更新為 2。 699是第一次更新。 並且已成功或處於待處理交易狀態,正在等待我們確認或取消。

解鎖 Postgres 鎖管理器。 布魯斯·莫吉安

但看看別的東西 - 2/51 是我們的第一筆交易,我們的第一個會話。 3/112 是來自頂部的第二個請求,該請求將該值變更為 3。如果您注意到,頂部的請求鎖定了自己,即 699。但是 3/112 沒有授予鎖定。 Lock_mode 欄位說明它正在等待什麼。 它預計為 699。如果您查看 699 的位置,您會發現它更高。 第一次會議做了什麼? 她對自己的交易 ID 創建了獨佔鎖。 Postgres 就是這樣做的。 它會阻止自己的交易 ID。 如果您想等待某人確認或取消,那麼您需要在有待處理的交易時等待。 這就是為什麼我們可以看到一條奇怪的線。

我們再看一下。 在左側我們可以看到我們的處理 ID。 在第二列我們看到虛擬事務 ID,在第三列中我們看到 lock_type。 這是什麼意思? 本質上,它所說的是它正在阻止交易 ID。 但請注意,底部的所有行都表示關係。 所以表上有兩種類型的鎖。 有一個關係鎖。 然後是 transactionid 阻塞,你自己阻塞,這正是第一行或最底部發生的情況,即 transactionid 所在的位置,我們等待 699 完成其操作。

我會看看這裡會發生什麼。 這裡有兩件事同時發生。 您正在查看第一行中鎖定自身的交易 ID 鎖定。 她也封鎖自己,讓人們等待。

如果您查看第 6 行,它與第一行是相同的條目。 因此交易 699 被阻止。 700也是自鎖的。 然後在底行中您將看到我們正在等待 699 完成其操作。

解鎖 Postgres 鎖管理器。 布魯斯·莫吉安

在 lock_type 元組中您可以看到數字。

解鎖 Postgres 鎖管理器。 布魯斯·莫吉安

您可以看到它是 0/10。 這是頁碼,也是該特定行的偏移量。

解鎖 Postgres 鎖管理器。 布魯斯·莫吉安

當我們更新時你會看到它變成了 0/11。

解鎖 Postgres 鎖管理器。 布魯斯·莫吉安

但實際上它是0/10,因為這個操作需要等待。 我們有機會看到這是我正在等待確認的系列。

解鎖 Postgres 鎖管理器。 布魯斯·莫吉安

一旦我們確認並按下提交,當更新完成時,這就是我們再次得到的。 交易 700 是唯一的鎖,它不會等待其他任何人,因為它已提交。 它只是等待事務完成。 一旦699用完,我們就不再等待。 現在交易 700 表示一切都很好,它在所有允許的表上擁有所需的所有鎖。

解鎖 Postgres 鎖管理器。 布魯斯·莫吉安

為了使整個事情變得更加複雜,我們創建了另一個視圖,這次將為我們提供層次結構。 我不指望你能理解這個請求。 但這將使我們更清楚地了解正在發生的事情。

解鎖 Postgres 鎖管理器。 布魯斯·莫吉安

這是一個遞歸視圖,還有另一個部分。 然後它又將一切重新組合在一起。 我們就用這個吧。

解鎖 Postgres 鎖管理器。 布魯斯·莫吉安

如果我們同時進行三個更新並說該行現在是三個怎麼辦? 我們將 3 改為 4。

解鎖 Postgres 鎖管理器。 布魯斯·莫吉安

在這裡我們看到 4。交易 ID 702。

解鎖 Postgres 鎖管理器。 布魯斯·莫吉安

然後我會將 4 更改為 5,將 5 更改為 6,將 6 更改為 7。我將讓許多人排隊等待這筆交易結束。

解鎖 Postgres 鎖管理器。 布魯斯·莫吉安

一切都變得清晰起來。 第一行是什麼? 這是702。這是最初設定該值的事務ID。 我的授予專欄寫了什麼? 我有標記 f。 這些是我的更新,(5) 無法獲得批准,因為我們正在等待交易 ID 6 結束。 我們有交易 ID 阻塞。 這會導致 7 個交易 ID 鎖定。

如果你看看 704,在 705,那裡還沒有寫任何東西,因為他們還不知道發生了什麼事。 他們只是寫道,他們不知道發生了什麼事。 他們會去睡覺,因為他們在等待有人完成並在有機會換行時被叫醒。

解鎖 Postgres 鎖管理器。 布魯斯·莫吉安

這就是它的樣子。 顯然,他們都在等待12號線。

解鎖 Postgres 鎖管理器。 布魯斯·莫吉安

這就是我們在這裡看到的。 這裡是 0 點 12 分。

解鎖 Postgres 鎖管理器。 布魯斯·莫吉安

因此,一旦第一筆交易獲得批准,您就可以在此處查看層次結構的工作原理。 現在一切都變得清楚了。 他們都變乾淨了。 而他們其實還在等待。

解鎖 Postgres 鎖管理器。 布魯斯·莫吉安

這就是發生的事情。 702 提交。 現在703獲得了這個行鎖,然後704開始等待703提交。 705 也在等待這一點。 當這一切完成後,他們就會清理自己。 我想指出的是,每個人都在排隊。 而這與塞車時大家都在等待第一輛車的情況非常相似。 第一輛車停了下來,每個人都排起了長隊。 然後它移動,然後下一輛車可以向前行駛並獲得其阻擋,等等。

解鎖 Postgres 鎖管理器。 布魯斯·莫吉安

如果這對您來說還不夠複雜,那麼我們現在將與您討論死鎖。 不知道你們誰遇過。 這是資料庫系統中相當常見的問題。 但死鎖是指一個會話正在等待另一個會話做某事。 此時另一個會話正在等待第一個會話做某事。

舉例來說,如果伊凡說:“給我一些東西”,我說:“不,只有你給我別的東西,我才會給你。” 他說:“不,如果你不給我,我就不給你。” 我們最終陷入了僵局。 我確信伊凡不會這樣做,但你明白這意味著我們有兩個人想要得到一些東西,但他們不准備放棄它,直到另一個人給他們他們想要的東西。 並且沒有解決辦法。

從本質上講,您的資料庫需要檢測到這一點。 然後您需要刪除或關閉其中一個會話,因為否則它們將永遠保留在那裡。 我們在資料庫中看到它,我們在作業系統中看到它。 在所有具有並行流程的地方,這種情況都可能發生。

解鎖 Postgres 鎖管理器。 布魯斯·莫吉安

現在我們將安裝兩個死鎖。 我們將輸入 50 和 80。在第一行中,我將從 50 更新到 50。我將獲得交易編號 710。

解鎖 Postgres 鎖管理器。 布魯斯·莫吉安

然後我會將 80 更改為 81,將 50 更改為 51。

解鎖 Postgres 鎖管理器。 布魯斯·莫吉安

這就是它的樣子。 因此710有一行被阻塞,711正在等待確認。 我們更新的時候就看到了這一點。 710是我們系列的主人。 並且711等待710完成交易。

解鎖 Postgres 鎖管理器。 布魯斯·莫吉安

它甚至指出死鎖發生在哪一行。 這就是事情開始變得奇怪的地方。

解鎖 Postgres 鎖管理器。 布魯斯·莫吉安

現在我們將80更新為80。

解鎖 Postgres 鎖管理器。 布魯斯·莫吉安

這就是僵局開始的地方。 710在等待711的回應,711也在等待710。而這不會有好結果。 並且沒有辦法擺脫這種情況。 他們會期待彼此的回應。

解鎖 Postgres 鎖管理器。 布魯斯·莫吉安

它只會開始推遲一切。 我們不希望這樣。

解鎖 Postgres 鎖管理器。 布魯斯·莫吉安

當這種情況發生時,Postgres 有辦法注意到。 當發生這種情況時,您會收到此錯誤。 由此可以清楚看出,某個進程正在等待另一個進程的共享鎖,也就是被 711 進程阻塞。 該進程正在等待某個交易 ID 上的共享鎖,並被某個進程阻塞。 因此,這裡就出現了僵局的情況。

解鎖 Postgres 鎖管理器。 布魯斯·莫吉安

是否存在三向僵局? 是否可以? 是的。

解鎖 Postgres 鎖管理器。 布魯斯·莫吉安

我們將這些數字輸入到表格中。 我們把40改成40,我們做阻塞。

解鎖 Postgres 鎖管理器。 布魯斯·莫吉安

我們將 60 更改為 61,將 80 更改為 81。

解鎖 Postgres 鎖管理器。 布魯斯·莫吉安

然後我們改變80然後繁榮!

解鎖 Postgres 鎖管理器。 布魯斯·莫吉安

而714現在正在等待715。716正在等待715。 對此我們無能為力。

解鎖 Postgres 鎖管理器。 布魯斯·莫吉安

這裡已經不是兩個人了,已經是三個人了。 我想要從你那裡得到一些東西,這個人想要從第三者那裡得到一些東西,第三個人想要從我這裡得到一些東西。 我們最終會陷入三向等待,因為我們都在等待對方完成他們需要做的事情。

解鎖 Postgres 鎖管理器。 布魯斯·莫吉安

Postgres 知道這件事發生在哪一行。 因此,它會給您以下訊息,這表明您遇到了三個輸入相互阻塞的問題。 這裡沒有任何限制。 這可能是 20 個條目互相阻塞的情況。

解鎖 Postgres 鎖管理器。 布魯斯·莫吉安

下一個問題是可序列化的。

解鎖 Postgres 鎖管理器。 布魯斯·莫吉安

如果有特殊的可序列化鎖。

解鎖 Postgres 鎖管理器。 布魯斯·莫吉安

我們回到719。它的輸出很正常。

解鎖 Postgres 鎖管理器。 布魯斯·莫吉安

您可以按一下使交易可序列化。

解鎖 Postgres 鎖管理器。 布魯斯·莫吉安

您意識到您現在擁有不同類型的 SA 鎖定 - 它意味著可序列化。

解鎖 Postgres 鎖管理器。 布魯斯·莫吉安

解鎖 Postgres 鎖管理器。 布魯斯·莫吉安

因此,我們有一種名為 SARieadLock 的新型鎖,它是一個序列鎖,可讓您輸入序列。

解鎖 Postgres 鎖管理器。 布魯斯·莫吉安

您也可以插入唯一索引。

解鎖 Postgres 鎖管理器。 布魯斯·莫吉安

在這個表中我們有唯一索引。

解鎖 Postgres 鎖管理器。 布魯斯·莫吉安

所以如果我在這裡輸入數字 2,那麼我就有一個 2。但是在最頂部,我又輸入了另一個 2。你可以看到 721 有一個獨佔鎖。 但現在 722 正在等待 721 完成其操作,因為它無法插入 2,直到它知道 721 會發生什麼。

解鎖 Postgres 鎖管理器。 布魯斯·莫吉安

如果我們進行子事務。

解鎖 Postgres 鎖管理器。 布魯斯·莫吉安

這裡有 723 個。

解鎖 Postgres 鎖管理器。 布魯斯·莫吉安

如果我們保存該點然後更新它,那麼我們就會得到一個新的交易 ID。 這是您需要注意的另一種行為模式。 如果我們回傳這個,那麼交易 ID 就會消失。 724要走了。 但現在我們有 725 個。

那我在這裡想做什麼? 我試圖向您展示您可能會發現的異常鎖的範例:無論是可序列化鎖定還是 SAVEPOINT,這些都是將出現在鎖定表中的不同類型的鎖定。

解鎖 Postgres 鎖管理器。 布魯斯·莫吉安

這是顯式(explicit)鎖的創建,其中有pg_advisory_lock。

解鎖 Postgres 鎖管理器。 布魯斯·莫吉安

您會看到封鎖類型被列為建議類型。 這裡用紅色寫著「諮詢」。 您可以同時使用 pg_advisory_unlock 進行封鎖。

解鎖 Postgres 鎖管理器。 布魯斯·莫吉安

最後,我想向您展示另一件令人興奮的事情。 我將建立另一個視圖。 但我會將 pg_locks 表與 pg_stat_activity 表連接起來。 我為什麼要這樣做? 因為這將允許我查看所有當前會話並準確了解它們正在等待哪種鎖。 當我們將鎖表和查詢表放在一起時,這是非常有趣的。

解鎖 Postgres 鎖管理器。 布魯斯·莫吉安

這裡我們建立pg_stat_view。

解鎖 Postgres 鎖管理器。 布魯斯·莫吉安

我們將這一行更新為一位。 在這裡我們看到 724。然後我們將行更新為三。 你現在在這裡看到什麼? 這些是請求,即您會看到左欄中列出的完整請求清單。 然後在右側您可以看到障礙物及其造成的情況。 而且它可以讓您更加清楚,這樣您就不必每次都返回每個會話並查看是否需要加入。 他們為我們做這件事。

另一個非常有用的功能是 pg_blocking_pids。 你可能從未聽過她。 她在做什麼? 它允許我們知道該會話 11740 正在等待哪些特定進程 ID。 您可以看到 11740 正在等待 724。724 位於最頂端。 11306 是你的進程 ID。 本質上,這個函數會遍歷你的鎖定表。 我知道這有點複雜,但你能理解。 本質上,這個函數會遍歷這個鎖表,並嘗試找到這個進程ID在哪裡被賦予了它正在等待的鎖。 它還嘗試找出正在等待鎖的進程具有哪個進程 ID。 這樣你就可以運行這個函數了 pg_blocking_pids.

這非常有用。 我們是在 9.6 版本中才添加這個功能的,所以這個功能只有 5 年的歷史,但它非常非常有用。 這同樣適用於第二個請求。 它準確地展示了我們需要看到的內容。

解鎖 Postgres 鎖管理器。 布魯斯·莫吉安

這就是我想和你談談的。 正如我所料,我們用完了所有的時間,因為幻燈片太多了。 幻燈片可供下載。 我要感謝您來到這裡。 我相信您會喜歡本次會議的其餘部分,非常感謝!

問題:

例如,如果我嘗試更新行,而第二個會話嘗試刪除整個表。 據我了解,應該要有意圖鎖之類的東西。 Postgres中有這樣的東西嗎?

解鎖 Postgres 鎖管理器。 布魯斯·莫吉安

讓我們回到最開始。 您可能還記得,當您執行任何操作時,例如執行 SELECT 操作時,我們都會發出 AccessShareLock。 這可以防止表被刪除。 因此,例如,如果您想要更新表中的一行或刪除一行,那麼某人無法同時刪除整個表,因為您在整個表和該行上持有此 AccessShareLock。 一旦完成,他們就可以將其刪除。 但是,當您直接在那裡更改某些內容時,他們將無法做到這一點。

讓我們再來一次。 讓我們繼續討論刪除範例。 您會看到整個表格上方的行上有一個排他鎖。

這看起來像是鎖獨佔,對吧?

是的,看起來是這樣。 我明白你在說什麼。 你是說,如果我執行 SELECT 然後我有一個 ShareExclusive,然後我將其設定為 Row Exclusive,這會成為問題嗎? 但令人驚訝的是,這並沒有造成問題。 這看起來像是增加了鎖定程度,但本質上我有一個防止刪除的鎖。 現在,當我使這個鎖變得更強大時,它仍然可以防止刪除。 所以我不會上去。 也就是說,當它處於較低層級時,它也阻止了它發生,因此當我提高其層級時,它仍然阻止表被刪除。

我明白你在說什麼。 這裡不存在鎖升級的情況,即您試圖放棄一個鎖以引入一個更強的鎖。 這裡它只是全面增加了這種預防,因此不會引起任何衝突。 但這是一個好問題。 非常感謝您提出這個問題!

當我們有很多會話、大量使用者時,我們需要做什麼才能避免死鎖情況?

Postgres 自動注意到死鎖狀況。 並且它會自動刪除其中一個會話。 避免死鎖的唯一方法就是按照相同的順序來阻止人。 因此,當您查看應用程式時,通常會發現死鎖的原因......讓我們想像一下我想阻止兩個不同的事情。 一個應用程式鎖定表 1,另一個應用程式鎖定表 2,然後鎖定表 1。避免死鎖的最簡單方法是查看您的應用程式並嘗試確保所有應用程式中鎖定以相同的順序發生。 這通常可以消除 80% 的問題,因為各種各樣的人都在編寫這些應用程式。 如果您以相同的順序封鎖它們,那麼您就不會遇到死鎖情況。

非常感謝您的表演! 您談到了“vacuum full”,如果我理解正確的話,“vacuum full”會扭曲單獨儲存中的記錄順序,因此它們會保持當前記錄不變。 為什麼vacuum full需要獨佔鎖定存取以及為什麼它與寫入操作衝突?

這是個好問題。 原因是真空已滿佔據桌子。 我們實際上是在創建該表的新版本。 而且桌子會是新的。 事實證明,這將是該表的全新版本。 問題是,當我們這樣做時,我們不希望人們閱讀它,因為我們需要他們看到新表。 這與上一個問題有關。 如果我們可以同時閱讀,我們將無法移動它並將人們引導到新桌子。 我們需要等待每個人都讀完這個表,所以這本質上是一個鎖獨佔的情況。
我們只是說我們從一開始就鎖定,因為我們知道在最後我們需要一個獨佔鎖才能將每個人移動到新副本。 所以我們有可能解決這個問題。 我們透過同步索引來做到這一點。 但這要困難得多。 這與您之前關於鎖獨佔的問題非常相關。

是否可以為 Postgres 添加鎖定超時? 例如,在 Oracle 中,我可以編寫「選擇更新」並在更新之前等待 50 秒。 這對於申請來說是有好處的。 但在 Postgres 中,我要么需要立即執行而不等待,要么等到某個時間。

是的,您可以選擇鎖定的逾時時間。 您也可以發出 no way 指令,這將...如果您無法立即獲得鎖。 因此,要么鎖定超時,要么採取其他措施來允許您執行此操作。 這不是在句法層面上完成的。 這是作為伺服器上的變數完成的。 有時這不能使用。

你能開啟第 75 張投影片嗎?

是。

解鎖 Postgres 鎖管理器。 布魯斯·莫吉安

我的問題如下。 為什麼兩個更新進程都期望 703?

這是一個很好的問題。 順便說一句,我不明白 Postgres 為什麼要這樣做。 但是當 703 創建時,它期待著 702。而當 704 和 705 出現時,它們似乎不知道自己在期待什麼,因為那裡什麼都沒有。 Postgres 是這樣做的:當你無法獲得鎖時,它會寫“處理你有什麼意義?”,因為你已經在等待某人了。 所以我們就讓它懸在空中,它根本不會更新。 但這裡發生了什麼事? 一旦702完成該過程並且703收到其鎖,系統就會返回。 她說現在我們有兩個人在等。 然後我們一起更新它們。 讓我們表明雙方都在期待。

我不知道 Postgres 為什麼要這樣做。 但是有一個問題叫做f...。 在我看來,這不是俄語中的術語。 這是每個人都在等待一座城堡的時候,即使有20個權威在等待這座城堡。 突然他們都同時醒來。 每個人都開始嘗試做出反應。 但是系統讓大家都在等703。因為他們都在等,我們馬上就讓他們全部排隊。 如果在此之後出現任何其他新的請求,例如707,那麼將再次出現空。

在我看來,這樣做是為了讓我們可以說,在這個階段702正在等待703,而所有在那之後的人都不會進入這個領域。 但是,一旦第一個服務員離開,所有在更新之前等待的人都會收到相同的令牌。 所以我認為這樣做是為了我們可以按順序進行處理,以便它們得到正確的排序。

我一直認為這是一個相當奇怪的現象。 因為在這裡,例如,我們根本沒有列出它們。 但在我看來,每次我們給出一個新的鎖時,我們都會查看所有正在等待的人。 然後我們把它們全部排好。 然後,只有當下一個人完成處理後,任何新進來的人才會進入隊列。 非常好的問題。 非常感謝您的提問!

在我看來,當 705 期望 704 時,這更符合邏輯。

但這裡的問題如下。 從技術上講,您可以喚醒其中之一。 所以我們會喚醒其中之一。 但係統中會發生什麼? 你可以看到最上面的703如何封鎖了他自己的交易ID。 這就是 Postgres 的工作原理。 而且703被它自己的事務ID阻塞了,所以如果有人想等待,那麼他們就會等待703。而且,本質上,703完成了。 只有在其完成後,其中一個進程才會被喚醒。 我們不知道這個過程到底是什麼。 然後我們逐漸處理一切。 但不清楚哪個進程會先被喚醒,因為它可能是這些進程中的任何一個。 本質上,我們有一個調度程序,它表示我們現在可以喚醒這些進程中的任何一個。 我們只是隨機挑選一個。 所以兩者都需要注意,因為我們可以喚醒其中任何一個。

問題是我們有 CP 無窮大。 因此,我們很可能能夠喚醒後者。 例如,如果我們喚醒後一個,我們將等待剛剛收到區塊的那個,因此我們無法確定到底誰會首先被喚醒。 我們只要創造這樣一個情況,系統就會以隨機的順序喚醒它們。

Egor Rogov 有關鎖的文章。 瞧,它們也很有趣,也很有用。 當然,這個話題非常複雜。 非常感謝你,布魯斯!

來源: www.habr.com

添加評論