PostgreSQL中的表鎖

2021-02-20 非法加馮

PostgreSQL的並發控制以快照隔離(SI)為主,以兩階段鎖定(2PL)機制為輔。PostgreSQL對DML(增刪改查等命令)使用SI,對DDL(CREATE TABLE等命令)使用2PL。

PostgreSQL有好幾類鎖,其中最主要的是表級鎖與行級鎖,此外還有頁級鎖,諮詢鎖等等。表級鎖通常是各種命令執行時自動獲取的,或者通過事務中的LOCK語句顯式獲取;而行級鎖則是由SELECT FOR UPDATE|SHARE語句顯式獲取的。本文主要介紹PostgreSQL中的表鎖。

表級鎖

表級鎖通常會在執行各種命令執行時自動獲取,或者通過在事務中使用LOCK語句顯式獲取。

每種鎖都有自己的衝突集合,在同一時刻的同一張表上,兩個事務可以持有不衝突的鎖,不能持有衝突的鎖。

有些鎖是**自斥(self-conflict)**的,即最多只能被一個事務所持有。

表級鎖總共有八種模式,有著並不嚴格的強度遞增關係(例外是Share鎖不自斥)

表級鎖存在於PG的共享內存中,可以通過pg_locks系統視圖查閱。

表級鎖的模式

如何記憶這麼多類型的鎖呢?讓我們從演化的視角來看這些鎖。

表級鎖的演化

最開始只有兩種鎖:Share與Exclusive,共享鎖與排它鎖,即所謂讀鎖與寫鎖。讀鎖的目的是阻止表數據的變更,而寫鎖的目的是阻止一切並發訪問。這很好理解。

多版本並發控制

後來隨著多版本並發控制技術的出現(PostgreSQL使用快照隔離實現MVCC),讀不阻塞寫,寫不阻塞讀(針對表的增刪改查而言)。因而原有的鎖模型就需要升級了:這裡的共享鎖與排他鎖都有了一個升級版本,即前面多加一個ACCESS。ACCESS SHARE是改良版共享鎖,即允許ACCESS(多版本並發訪問)的SHARE鎖,這種鎖意味著即使其他進程正在並發修改數據也不會阻塞本進程讀取數據。當然有了多版本讀鎖也就會有對應的多版本寫鎖來阻止一切訪問,即連ACCESS(多版本並發訪問)都要EXCLUSIVE的鎖,這種鎖會阻止一切訪問,是最強的寫鎖。

引入MVCC後,INSERT|UPDATE|DELETE仍然使用原來的Exclusive鎖,而普通的只讀SELECT則使用多版本的AccessShare鎖。因為AccessShare鎖與原來的Exclusive鎖不衝突,所以讀寫之間就不會阻塞了。原來的Share鎖現在主要的應用場景為創建索引(非並發創建模式下,創建索引會阻止任何對底層數據的變更),而升級的多版本AccessExclusive鎖主要用於除了增刪改之外的排他性變更(DROP|TRUNCATE|REINDEX|VACUUM FULL等),這個模型如圖(a)所示。

當然,這樣還是有問題的。雖然在MVCC中讀寫之間相互不阻塞了,但寫-寫之間還是會產生衝突。上面的模型中,並發寫入是通過表級別的Exclusive鎖解決的。表級鎖雖然可以解決並發寫入衝突問題,但這個粒度太大了,會影響並發度:因為同一時刻一張表上只能有一個進程持有Exclusive鎖並執行寫入,而典型的OLTP場景是以單行寫入為主。所以常見的DBMS解決寫-寫衝突通常都是採用行級鎖來實現(下面會講到)。

行級鎖和表級鎖不是一回事,但這兩種鎖之間仍然存在著聯繫,協調這兩種鎖之間的關係,就需要引入意向鎖。

意向鎖

意向鎖用於協調表鎖與行鎖之間的關係:它用於保護較低資源級別上的鎖,即說明下層節點已經被加了鎖。當進程想要鎖定或修改某表上的某一行時,它會在這一行上加上行級鎖。但在加行級鎖之前,它還需要在這張表上加上一把意向鎖,表示自己將會在表中的若干行上加鎖。

舉個例子,假設不存在意向鎖。假設進程A獲取了表上某行的行鎖,持有行上的排他鎖意味著進程A可以對這一行執行寫入;同時因為不存在意向鎖,進程B很順利地獲取了該表上的表級排他鎖,這意味著進程B可以對整個表,包括A鎖定對那一行進行修改,這就違背了常識邏輯。因此A需要在獲取行鎖前先獲取表上的意向鎖,這樣後來的B就意識到自己無法獲取整個表上的排他鎖了(但B依然可以加一個意向鎖,獲取其他行上的行鎖)。

因此,這裡RowShare就是行級共享鎖對應的表級意向鎖(SELECT FOR SHARE|UPDATE命令獲取),而RowExclusive(INSERT|UPDATE|DELETE獲取)則是行級排他鎖對應的表級意向鎖。注意因為MVCC的存在,只讀查詢並不會在行上加鎖。引入意向鎖後的模型如圖(c)所示。而合併MVCC與意向鎖模型之後的鎖模型如圖(d)所示。

自斥鎖

上面這個模型已經相當不錯,但仍然存在一些問題,譬如自斥:這裡RowExclusive與Share鎖都不是自斥的。

舉個例子,並發VACUUM不應阻塞數據寫入,而且一個表上不應該允許多個VACUUM進程同時工作。因為不能阻塞寫入,因此VACUUM所需的鎖強度必須要比Share鎖弱,弱於Share的最強鎖為RowExclusive,不幸的是,該鎖並不自斥。如果VACUUM使用該鎖,就無法阻止單表上出現多個VACUUM進程。因此需要引入一個自斥版本的RowExclusive鎖,即ShareUpdateExclusive鎖。

同理,再比如執行觸發器管理操作(創建,刪除,啟用)時,該操作不應阻塞讀取和鎖定,但必須禁止一切實際的數據寫入,否則就難以判斷某條元組的變更是否應該觸發觸發器。Share鎖滿足不阻塞讀取和鎖定的條件,但並不自斥,因此可能出現多個進程在同一個表上並發修改觸發器。並發修改觸發器會帶來很多問題(譬如丟失更新,A將其配置為Replica Trigger,B將其配置為Always Trigger,都反回成功了,以誰為準?)。因此這裡也需要一個自斥版本的Share鎖,即ShareRowExclusive鎖。

因此,引入兩種自斥版本的鎖後,就是PostgreSQL中的最終表級鎖模型,如圖(e)所示。

表級鎖的命名與記憶

PostgreSQL的表級鎖的命名有些詰屈聱牙,這是因為一些歷史因素,但也可以總結出一些規律便於記憶。

最初只有兩種鎖:共享鎖(Share)與排他鎖(Exclusive)。

特徵是只有一個單詞,表示這是兩種最基本的鎖:讀鎖與寫鎖。

多版本並發控制的出現,引入了多版本的共享鎖與排他鎖(AccessShare與AccessExclusive)。

特徵是Access前綴,表示這是用於"多版本並發控制"的改良鎖。

為了處理並發寫入之間的衝突,又引入了兩種意向鎖(RowShare與RowExclusive)

特徵是Row前綴,表示這是行級別共享/排他鎖對應的表級意向鎖。

最後,為了處理意向排他鎖與共享鎖不自斥的問題,引入了這兩種鎖的自斥版本(ShareUpdateExclusive, ShareRowExclusive)。這兩種鎖的名稱比較難記:

都是以Share打頭,以Exclusive結尾。表示這兩種鎖都是某種共享鎖的自斥版本。

兩種鎖強度圍繞在Share前後,Update弱於Share,Row強於Share。

ShareRowExclusive可以理解為Share + Row Exclusive,因為Share不排斥其他Share,但RowExclusive排斥Share,因此同時加這兩種鎖的結果等效於ShareRowExclusive,即SIX。

ShareUpdateExclusive可以理解為ShareUpdate + Exclusive:UPDATE操作持有RowExclusive鎖,而ShareUpdate指的是本鎖與普通的增刪改(持RowExclusive鎖)相容,而Exclusive則表示自己和自己不相容。

Share, ShareRowUpdate, Exclusive 這三種鎖極少出現,基本可以無視。所以實際上主要用到的鎖是:

多版本兩種:AccessShare, AccessExclusive

意向鎖兩種:RowShare,RowExclusive

自斥更新排他鎖一種:ShareUpdateExclusive


顯式加鎖

通常表級鎖會在相應命令執行中自動獲取,但也可以手動顯式獲取。使用LOCK命令加鎖的方式

LOCK [ TABLE ] [ ONLY ] name [ * ] [, ...] [ IN lockmode MODE ] [ NOWAIT ]

顯式鎖表必須在事務中進行,在事務外鎖表會報錯。

鎖定視圖時,視圖定義中所有出現的表都會被鎖定。

使用表繼承時,默認父表和所有後代表都會加鎖,指定ONLY選項則繼承於該表的子表不會自動加鎖。

鎖表或者鎖視圖需要對應的權限,例如AccessShare鎖需要SELECT權限。

默認獲取的鎖模式為AccessExclusive,即最強的鎖。

LOCK TABLE只能獲取表鎖,默認會等待衝突的鎖被釋放,指定NOWAIT選項時,如果命令不能立刻獲得鎖就會中止並報錯。

命令一旦獲取到鎖, 會被在當前事務中一直持有。沒有UNLOCK TABLE命令,鎖總是在事務結束時釋放。

例子:數據遷移

舉個例子,以遷移數據為例,假設希望將某張表的數據遷移到另一個實例中。並保證在此期間舊錶上的數據在遷移期間不發生變化,那麼我們可以做的就是在複製數據前在表上顯式加鎖,並在複製結束,應用開始寫入新表後釋放。應用仍然可以從舊錶上讀取數據,但不允許寫入。那麼根據鎖衝突矩陣,允許只讀查詢的鎖要弱於AccessExclusive,阻止寫入的鎖不能弱於ShareRowExclusive,因此可以選擇ShareRowExclusive或Exclusive鎖。因為拒絕寫入意味著鎖定沒有任何意義,所以這裡選擇更強的Exclusive鎖。

BEGIN;
LOCK TABLE tbl IN EXCLUSIVE MODE;
-- DO Something
COMMIT

鎖的查詢

PostgreSQL提供了一個系統視圖pg_locks,包含了當前活動進程持鎖的信息。可以鎖定的對象包括:關係,頁面,元組,事務標識(虛擬的或真實的),其他資料庫對象(帶有OID)。

樣例數據

這個視圖需要一些額外的知識才能解讀。

該視圖是資料庫集簇範圍的視圖,而非僅限於單個資料庫,即可以看見其他資料庫中的鎖。

一個進程在一個時間點只能等待至多一個鎖,等待鎖用granted=f表示,等待進程會休眠至其他鎖被釋放,或者系統檢測到死鎖。

每個事務都有一個虛擬事務標識virtualtransaction(以下簡稱vxid),修改資料庫狀態(或者顯式調用txid_current獲取)的事務才會被分配一個真實的事務標識transactionid(簡稱txid),vxid|txid本身也是可以鎖定的對象。

每個事務都會持有自己vxid上的Exclusive鎖,如果有txid,也會同時持有其上的Exclusive鎖(即同時持有txid和vxid上的排它鎖)。因此當一個事務需要等待另一個事務時,它會嘗試獲取另一個事務txid|vxid上的共享鎖,因而只有當目標事務結束(自動釋放自己事務標識上的Exclusive鎖)時,等待事務才會被喚醒。

pg_locks視圖通常並不會直接顯示行級鎖信息,因為這些信息存儲在磁碟磁碟上(),如果真的有進程在等待行鎖,顯示的形式通常是一個事務等待另一個事務,而不是等待某個具體的行鎖。

諮詢鎖本質上的鎖對象客體是一個資料庫範疇內的BIGINT,classid裡包含了該整數的高32bit,objid裡包含有低32bit,objsubid裡則說明了諮詢鎖的類型,單一Bigint則取值為1,兩個int32則取值為2。

本視圖並不一定能保證提供一個一致的快照,因為所有fastpath=true的鎖信息是從每個後端進程收集而來的,而fastpath=false的鎖是從常規鎖管理器中獲取的,同時謂詞鎖管理器中的數據也是單獨獲取的,因此這幾種來源的數據之間可能並不一致。

頻繁訪問本視圖會對資料庫系統性能產生影響,因為要對鎖管理器加鎖獲取一致性快照。

虛擬事務

一個後端進程在整個生命周期中的每一個事務都會有一個自己的虛擬事務ID。PG中事務號是有限的(32-bit整型),會循環使用。為了節約事務號,PG只會為實際修改資料庫狀態的事務分配真實事務ID,而只讀事務就不分配了,用虛擬事務ID湊合一下。txid是事務標識,全局共享,而vxid是虛擬事務標識,在短期內可以保證全局唯一性。因為vxid由兩部分組成:BackendID與LocalTransactionId,前者是後端進程的標識符(本進程在內存中進程數組中的序號),後者是一個遞增的事務計數器。因此兩者組合即可獲得一個暫時唯一的虛擬事務標識(之所以是暫時是因為這裡的後端ID是有可能重複的)

typedef struct {

BackendId backendId;

        /* 後端ID,初始化時確定,其實是後端進程數組內索引號 */

LocalTransactionId localTransactionId;

        /* 後端內本地使用的命令標ID,類似自增計數器 */

} VirtualTransactionId;

應用常見操作的衝突關係

SELECT與UPDATE|DELETE|INSERT不會相互阻塞,即使訪問的是同一行。

I|U|D寫入操作與I|U|D寫入操作在表層面不會互斥,會在具體的行上通過RowExclusive鎖實現。

SELECT FOR UPDATE鎖定操作與I|U|D寫入在表層級也不會互斥,仍然是通過具體元組上的行鎖實現。

並發VACUUM,並發創建索引等操作不會阻塞讀寫,但它們是自斥的,即同一時刻只會有一個(所以同時在一個表上執行兩個CREATE INDEX CONCURRENTLY是沒有意義的,不要被名字騙了)

普通的索引創建CREATE INDEX,不帶CONCURRENTLY會阻塞增刪改,但不會阻塞查,很少用到。

任何對於觸發器的操作,或者約束類的操作,都會阻止增刪改,但不會阻塞只讀查詢以及鎖定。

冷門的命令REFRESH MATERIALIZED VIEW CONCURRENTLY允許SELECT和鎖定。

大多數很硬的變更:VACUUM FULL, DROP TABLE, TRUNCATE, ALTER TABLE的大多數形式都會阻塞一切讀取。

注意,鎖雖有強弱之分,但衝突關係是對等的。一個持有AccessShare鎖的SELECT會阻止後續的DROP TABLE獲得AccessExclusive鎖。後面的命令會進入鎖隊列中。

鎖隊列

PG中每個鎖上都會有一個鎖隊列。如果事務A佔有一個排他鎖,那麼事務B在嘗試獲取其上的鎖時就會在其鎖隊列中等待。如果這時候事務C同樣要獲取該鎖,那麼它不僅要和事務A進行衝突檢測,也要和B進行衝突檢測,以及隊列中其他的事務。這意味著當用戶嘗試獲取一個很強的鎖而未得等待時,已經會阻止後續新鎖的獲取。一個具體的例子是加列:

ALTER TABLE tbl ADD COLUMN mtime TIMESTAMP;

即使這是一個不帶默認值的加列操作(不會重寫整個表,因而很快),但本命令需要表上的AccessExclusive鎖,如果這張表上面已經有不少查詢,那麼這個命令可能會等待相當一段時間。因為它需要等待其他查詢結束並釋放掉鎖後才能執行。相應地,因為這條命令已經在等待隊列中,後續的查詢都會被它所阻塞。因此,當執行此類命令時的一個最佳實踐是在此類命令前修改lock_timeout,從而避免雪崩。

SET lock_timeout TO '1s';
ALTER TABLE tbl ADD COLUMN mtime TIMESTAMP;

這個設計的好處是,命令不會餓死:不會出現源源不斷的短小隻讀查詢無限阻塞住一個排他操作。

加鎖原則

夠用即可:使用滿足條件的鎖中最弱的鎖模式

越快越好:如果可能,可以用(長時間的弱鎖+短時間的強鎖)替換長時間的強鎖

遞增獲取:遵循2PL原則申請鎖;越晚使用激進鎖策略越好;在真正需要時再獲取。

相同順序:獲取鎖儘量以一致的順序獲取,從而減小死鎖的機率

最小化鎖阻塞時長

除了手工鎖定之外,很多常見的操作都會"鎖表",最常見的莫過於添加新欄位與添加新約束。這兩種操作都會獲取表上的AccessExclusive鎖以阻止一切並發訪問。當DBA需要在線維護資料庫時應當最小化持鎖的時間。

例如,為表添加新欄位的ALTER TABLE ADD COLUMN子句,根據新列是否提供易變默認值,會重寫整個表。

ALTER TABLE tbl ADD COLUMN mtime TIMESTAMP DEFAULT CURRENT_TIMESTAMP;

如果只是個小表,業務負載也不大,那麼也許可以直接這麼幹。但如果是很大的表,以及很高的負載,那麼阻塞的時間就會很可觀。在這段時間裡,命令都會持有表上的AccessExclusive鎖阻塞一切訪問。

可以通過先加一個空列,再慢慢更新的方式來最小化鎖等待時間:

ALTER TABLE tbl ADD COLUMN mtime TIMESTAMP;
UPDATE tbl SET mtime = CURRENT_TIMESTAMP; -- 可以分批進行

這樣,第一條加列操作的鎖阻塞時間就會非常短,而後面的更新(重寫)操作就可以以不阻塞讀寫的形式慢慢進行,最小化鎖阻塞。

同理,當想要為表添加新的約束時(例如新的主鍵),也可以採用這種方式:

CREATE UNIQUE INDEX CONCURRENTLY tbl_pk ON tbl(id); -- 很慢,但不阻塞讀寫
ALTER TABLE tbl ADD CONSTRAINT tbl_pk PRIMARY KEY USING INDEX tbl_pk; -- 阻塞讀寫,但很快

來替代單純的 ALTER TABLE tbl ADD PRIMARY KEY (id); 

相關焦點

  • 滲透中利用postgresql getshell及注入技巧
    3.B站在線學習連結由於分享的資源連結經常被和諧,所以必須遷移到B站上供大家學習B站up主官方帳號:VMYKnetwork團隊0x00 前言研究postgresql資料庫如何getshell是在滲透中遇到一個pgAdmin的web管理頁面可以直接操作postgresql且通過網上的文章沒有達到9.6版本getshell的效果所以便有了以下文章。
  • 去哪兒PostgreSQL指南
    建議使用NUMERIC(precision, scale)來存儲貨幣金額和其它要求精確計算的數值, 而不建議使用real, double precision;8. 建議使用hstore 來存儲非結構化,key-value 鍵值型,對數不定的數據;9.
  • postgresql email列表對NVM WAL BUFFER的討論
    不修改IO方式,有很多地方可以優化以提升性能:1)將WALWriteLock分成兩個鎖:一個用於寫,一個用於flush wal。現在,一個會話進行flush一個wal時,其他會話不能write wal,即使該wal位於其他段文件。沒有必要這樣做。
  • PostgreSQL簡介及安裝
    在PostgreSQL中,表可以設置為從「父」表繼承其特徵。可以安裝多個擴展以向PostgreSQL添加附加功能。5. PostgreSQL的特性覆蓋了SQL-2/SQL-92和SQL-3/SQL-99,是目前世界上支持最豐富的數據類型的資料庫。6.
  • PostgreSQL 10 新增特性
    (點擊上方公眾號,可快速關注)來源:oschinawww.oschina.net/news/83681/new-features-coming-in-postgresql
  • PostgreSQL函數
    )的方式來處理,注意,這裡的returns test並不是返回test表,實際上是返回與test表結構一樣的複合類型,postgresql在建表後會默認創建一個和表結構一樣的同名複合類型。也可以定義為最後需要說明的是sql函數中的參數,postgresql定義$1表示第一個參數,$2為第二個參數並以此類推。如果參數是複合類型,則可以使用點表示法,即$1.name訪問複合類型參數中的name欄位。
  • PostgreSQL初體驗
    具體的優缺點還等待著以後的學習過程中慢慢發掘。學習參考地址:菜鳥教程:https://www.runoob.com/postgresql/postgresql-tutorial.htmlW3C:https://www.w3cschool.cn/qysrc/qysrc-jwxg3758.html首先是配置環境,本人使用的是windows系統,接下來都是windows下的一些學習過程。
  • PostgreSQL連接Perl
    restart Stopping postgresql service:                               [  OK  ] Starting postgresql service:                               [  OK  ]
  • 資料庫常見面試題:樂觀、悲觀鎖,行鎖、表鎖、讀、寫鎖,間隙鎖
    元數據鎖:即MDL,MySQL5.5版本引。當對個表做增刪改查操作的時候,加MDL讀鎖;當要對表變更操作的時候,加MDL寫鎖。讀鎖之間不互斥,讀寫鎖之間、寫鎖之間是互斥的。MySQL所有引擎都持表鎖。級鎖由各個引擎實現。即鎖定某個表中的特定,並發度,鎖粒度低。在InnoDB事務中,鎖是在需要的時候才加上的,但並不是不需要了就刻釋放,是要等到事務結束時才釋放,這個就是兩階段鎖協議 。
  • PostgreSQL 12 首個版本說明草案發布
    PostgreSQL 12 還包含許多可能影響與舊版本之間的兼容性的變更: 刪除系統列 OID 的某些特殊行為 舊版本中,在創建表時可以通過WITH OIDS指定正常情況下不可見(normally-invisible)的 OID 列;在新版本中該特性已被刪除,不過列仍可以被顯式地指定為OID
  • 將函數索引從Oracle遷移到PostgreSQL
    通過AWS轉換後的腳本在PostgreSQL中執行,發現函數索引無法成功執行。通過Oracle查詢發現其使用了substr和to_char等函數。CREATE INDEX "HB_E2E"."IDX_CUST_ID_I" ON "HB_E2E"."
  • 共享鎖、排他鎖、互斥鎖、悲觀鎖、樂觀鎖、行鎖、表鎖、頁面鎖、不可重複讀、丟失修改、讀髒數據
    傳統的關係型資料庫裡邊就用到了很多這種鎖機制,比如行鎖,表鎖等,讀鎖,寫鎖等,都是在做操作之前先上鎖。再比如Java裡面的同步原語synchronized關鍵字的實現也是悲觀鎖。悲觀鎖和樂觀鎖推薦:面試難點:你了解樂觀鎖和悲觀鎖嗎?
  • MySQL的insert into select 引發鎖表
    必須存在,並且欄位field,field2…也必須存在(2)注意Table2的主鍵約束,如果Table2有主鍵而且不為空,則 field1, field2…中必須包括主鍵在執行語句的時候,MySQL是逐行加鎖的(掃描一個鎖一個),直至鎖住所有符合條件的數據,執行完畢才釋放鎖。
  • PostgreSQL連接Python
    以下Python程序將使用以前創建的資料庫中創建一個表:#!在資料庫test.db中,它會顯示以下消息:Opened database successfully Table created successfully INSERT 操作Python程序顯示了我們如何創建表COMPANY 在上面的例子中創建表中的記錄:#!
  • PostgreSQL Timelines and History File:Master Guide
    00000001000000000000000AAt this point of time, I have done a pointin time recovery to some point in 000000010000000000000007 wal, before secondinsert.so I gave recovery target lsn as『0/07000060』 in postgresql.conf
  • PostgreSQL類型bit和varbit報錯引起的日誌不可讀
    2、創建指向日誌文件的外部表postgres=# create extension file_fdw;CREATE EXTENSIONpostgres=# create server pglog FOREIGN DATA WRAPPER file_fdw;CREATE SERVER
  • PostgreSQL | 查LAST DDL TIME,PG的三種方法
    truncate在Oracle中屬於DDL語句,在PostgreSQL中屬於DML語句。然後我們來創建一張表,插入一些數據。更多高級用法需要查閱官方文檔 https://www.postgresql.org/docs/current/event-trigger-definition.html。還有別的辦法嗎?當然也有其他方法,這個方法就是開啟track_commit_timestamp,它可以用來跟蹤記錄事務提交的時間戳。
  • MySQL中悲觀鎖和樂觀鎖到底是什麼?
    索引和鎖是資料庫中的兩個核心知識點,隔離級別的實現都是通過鎖來完成的 按照鎖顆粒對鎖進行劃分 ? 鎖用來對數據進行鎖定,我們可以從鎖定對象的粒度大小來對鎖進行劃分,分別為行鎖、頁鎖和表鎖。
  • PostgreSQL Tips: 動態SQL
    在查詢中,動態 SQL 用於減少重複性任務。例如,可以使用動態 SQL 每天為特定表創建表分區,在所有外鍵上添加缺失的索引,或在不產生重大編碼影響的情況下為特定表添加數據審核功能。動態 SQL 的另一個重要用途是克服 PL/pgSQL 緩存的副作用,因為不緩存使用 EXECUTE 語句執行的查詢。
  • 資料庫:MySQL 中 「select ... for update」 排他鎖分析
    for update僅適用於InnoDB,且必須在事務塊(BEGIN/COMMIT)中才能生效。在進行事務操作時,通過「for update」語句,MySQL會對查詢結果集中每行數據都添加排他鎖,其他線程對該記錄的更新與刪除操作都會阻塞。排他鎖包含行鎖、表鎖。