国产xxxx99真实实拍_久久不雅视频_高清韩国a级特黄毛片_嗯老师别我我受不了了小说

資訊專欄INFORMATION COLUMN

PostgreSQL鎖淺析

IT那活兒 / 665人閱讀
PostgreSQL鎖淺析
點擊上方“IT那活兒”公眾號,關注后了解更多內容,不管IT什么活兒,干就完了?。。?/strong> 

鎖存在的意義

在了解PostgreSQL鎖之前,我們需要了解鎖存在的意義是啥?

當多個會話同時訪問數據庫的同一數據時,理想狀態是為所有會話提供高效的訪問,同時還要維護嚴格的數據一致性。那這數據一致性通過什么來維護呢?就是之前文章多次提到的MVCC(多版本并發控制),可以點擊下列文章標題回顧早前發布的內容:

聊聊PostgreSQL事務id那點事
Mvcc機制
MVCC:每個SQL語句看到的都只是當前事務開始的數據快照,而不管底層數據的當前狀態。這樣可以保護語句不會看到可能由其他在相同數據行上執行更新的并發事務造成的不一致數據,為每一個數據庫會話提供事務隔離。MVCC避免了傳統的數據庫系統的鎖定方法,將通過鎖爭奪最小化的方法來達到多會話并發訪問時的性能最大化目的。

PostgreSQL提供了多種鎖模式用于控制對表中數據的并發訪問,其中最主要的是表級鎖與行級鎖,此外還有頁級鎖,咨詢鎖等等,接下來主要介紹表級鎖與行級鎖。

表級鎖

表級鎖通常會在執行各種命令執行時自動獲取,或者通過在事務中使用LOCK語句顯式獲取。
每種鎖都有自己的沖突集合, 兩個事務在同一時刻不能在同一個表上持有屬于相互沖突模式的鎖,但可以持有不沖突的鎖。
表級鎖總共有八種模式,其存在于PG的共享內存中,可以通過pg_locks系統視圖查閱。
1. ACCESS SHARE
只與ACCESS EXCLUSIVE鎖模式沖突。
SELECT命令在被引用的表上獲得一個這種模式的鎖。通常,任何只讀取表而不修改它的查詢都將獲得這種鎖模式。
2. ROW SHARE
與EXCLUSIVE和ACCESS EXCLUSIVE鎖模式沖突。
SELECT FOR UPDATE和SELECT FOR SHARE命令在目標表上取得一個這種模式的鎖 (加上在被引用但沒有選擇FOR UPDATE/FOR SHARE的任何其他表上的ACCESS SHARE鎖)。
3. ROW EXCLUSIVE
與SHARE、SHARE ROW EXCLUSIVE、EXCLUSIVE和ACCESS EXCLUSIVE鎖模式沖突。
命令UPDATE、DELETE和INSERT在目標表上取得這種鎖模式(加上在任何其他被引用表上的ACCESS SHARE鎖)。通常,這種鎖模式將被任何修改表中數據的命令取得。
4. SHARE UPDATE EXCLUSIVE
與SHARE UPDATE EXCLUSIVE、SHARE、SHARE ROW EXCLUSIVE、EXCLUSIVE和ACCESS EXCLUSIVE鎖模式沖突。這種模式保護一個表不受并發模式改變和VACUUM運行的影響。
由VACUUM(不帶FULL)、ANALYZE、 CREATE INDEX CONCURRENTLY、REINDEX CONCURRENTLY、 CREATE STATISTICS以及某些ALTER INDEX和 ALTER TABLE的變體獲得。
5. SHARE
與ROW EXCLUSIVE、SHARE UPDATE EXCLUSIVE、SHARE ROW EXCLUSIVE、EXCLUSIVE和ACCESS EXCLUSIVE鎖模式沖突。這種模式保護一個表不受并發數據改變的影響。
由CREATE INDEX(不帶CONCURRENTLY)取得。
6. SHARE ROW EXCLUSIVE
與ROW EXCLUSIVE、SHARE UPDATE EXCLUSIVE、SHARE、SHARE ROW EXCLUSIVE、EXCLUSIVE和ACCESS EXCLUSIVE鎖模式沖突。這種模式保護一個表不受并發數據修改所影響,并且是自排他的,這樣在一個時刻只能有一個會話持有它。
由CREATE TRIGGER和某些形式的ALTER TABLE所獲得。
7. EXCLUSIVE
與ROW SHARE、ROW EXCLUSIVE、SHARE UPDATE EXCLUSIVE、SHARE、SHARE ROW EXCLUSIVE、EXCLUSIVE和ACCESS EXCLUSIVE鎖模式沖突。這種模式只允許并發的ACCESS SHARE鎖,即只有來自于表的讀操作可以與一個持有該鎖模式的事務并行處理。
由REFRESH MATERIALIZED VIEW CONCURRENTLY獲得。
8. ACCESS EXCLUSIVE
與所有模式的鎖沖突(ACCESS SHARE、ROW SHARE、ROW EXCLUSIVE、SHARE UPDATE EXCLUSIVE、SHARE、SHARE ROW EXCLUSIVE、EXCLUSIVE和ACCESS EXCLUSIVE)。這種模式保證持有者是訪問該表的唯一事務。
由ALTER TABLE、DROP TABLE、TRUNCATE、REINDEX、CLUSTER、VACUUM FULL和REFRESH MATERIALIZED VIEW(不帶CONCURRENTLY)命令獲取。很多形式的ALTER INDEX和ALTER TABLE也在這個層面上獲得鎖。這也是未顯式指定模式的LOCK TABLE命令的默認鎖模式。
表級鎖模式沖突表:
注:X表示沖突。
怎么去看上面這個圖呢?
我們在這里以2個例子說明:
場景一
當一個會話運行了update語句,此時會話表上的鎖模式為ROW EXCLUSIVE,從上圖我們可以看出ROW EXCLUSIVE與SHARE、SHARE ROW EXCLUSIVE、EXCLUSIVE和ACCESS EXCLUSIVE鎖模式沖突。
也就是說在這個會話未提交事務釋放鎖之前,我們不能做申請SHARE、SHARE ROW EXCLUSIVE、EXCLUSIVE和ACCESS EXCLUSIVE相關的操作,例如CREATE INDEX(不帶CONCURRENTLY)、ALTER TABLE、DROP TABLE、TRUNCATE、REINDEX、CLUSTER、VACUUM FULL和REFRESH MATERIALIZED VIEW(不帶CONCURRENTLY)等。
會話一執行update語句:
會話二執行alter table語句時處于等待狀態:
我們查看等待情況如下:
注:Lock_Granted: true即為堵塞源。
一直到會話一結束,會話二語句才執行成功。
場景二
當一個會話運行了truncate語句,此時會話表上的鎖模式為ACCESS EXCLUSIVE,從圖上看我們可以看到它和所有鎖模式都沖突,意味著在當前會話未結束之前,這個表上的其他操作都做不了。
會話一執行truncate語句:
會話二執行select語句時處于等待狀態:

查看鎖等待情況如下:

通過上面2個案例我們應該比較了解各種鎖模式沖突的情況了。接下來我們介紹行級鎖。

行級鎖

同一個事務可能會在相同的行上保持沖突的鎖,甚至是在不同的子事務中。但是除此之外,兩個事務永遠不可能在相同的行上持有沖突的鎖。行級鎖不影響數據查詢,它們只阻塞對同一行的寫入者和加鎖者。行級鎖在事務結束時或保存點回滾的時候釋放,就像表級鎖一樣。
行級鎖模式
1. FOR UPDATE
FOR UPDATE會導致由SELECT語句檢索到的行被鎖定,就好像它們要被更新。這可以阻止它們被其他事務鎖定、修改或者刪除,一直到當前事務結束。
也就是說其他嘗試UPDATE、DELETE、SELECT FOR UPDATE、SELECT FOR NO KEY UPDATE、SELECT FOR SHARE或者SELECT FOR KEY SHARE這些行的事務將被阻塞,直到當前事務結束。
反過來,SELECT FOR UPDATE將等待已經在相同行上運行以上這些命令的并發事務,并且接著鎖定并且返回被更新的行(或者沒有行,因為行可能已被刪除)。
2. FOR NO KEY UPDATE
行為與FOR UPDATE類似,不過獲得的鎖較弱:這種鎖將不會阻塞嘗試在相同行上獲得鎖的SELECT FOR KEY SHARE命令。任何不獲取FOR UPDATE鎖的UPDATE也會獲得這種鎖模式。
3. FOR SHARE
行為與FOR NO KEY UPDATE類似,不過它在每個檢索到的行上獲得一個共享鎖而不是排他鎖。
一個共享鎖會阻塞其他事務在這些行上執行UPDATE、DELETE、SELECT FOR UPDATE或者SELECT FOR NO KEY UPDATE,但是它不會阻止它們執行SELECT FOR SHARE或者SELECT FOR KEY SHARE。
4. FOR KEY SHARE
行為與FOR SHARE類似,不過鎖較弱:SELECT FOR UPDATE會被阻塞,但是SELECT FOR NO KEY UPDATE不會被阻塞。一個鍵共享鎖會阻塞其他事務執行修改鍵值的DELETE或者UPDATE,但不會阻塞其他UPDATE,也不會阻止SELECT FOR NO KEY UPDATE、SELECT FOR SHARE或者SELECT FOR KEY SHARE。
行級鎖模式沖突表:
PG中我們要查看鎖信息,pg_locks視圖提供了詳細的信息,不同于oracle的dba_locks,pg_locks稍顯復雜。
接下來我們對pg_locks視圖各字段做下詳細介紹:
  • locktype
    可鎖對象的類型:relation, extend, frozenid, page, tuple, transactionid, virtualxid, spectoken, object, userlock, or advisory.
    locktype的等待事件:
  • database oid (參考 pg_database.oid)

    鎖目標存在的數據庫的OID,如果目標是一個共享對象則為0,如果目標是一個事務ID則為空。

  • relation oid (參考 pg_class.oid)

    作為鎖目標的關系的OID,如果目標不是一個關系或者只是關系的一部分則此列為空。

  • page int4

    作為鎖目標的頁在關系中的頁號,如果目標不是一個關系頁或元組則此列為空。

  • tuple int2

    作為鎖目標的元組在頁中的元組號,如果目標不是一個元組則此列為空。

  • virtualxid text

    作為鎖目標的事務虛擬ID,如果目標不是一個虛擬事務ID則此列為空。

  • transactionid xid

    作為鎖目標的事務ID,如果目標不是一個事務ID則此列為空ID。

  • classid oid (參考 pg_class.oid)

    包含鎖目標的系統目錄的OID,如果目標不是一個普通數據庫對象則此列為空。

  • objid oid (參考 any OID column)

    鎖目標在它的系統目錄中的OID,如果目標不是一個普通數據庫對象則為空。

  • objsubid int2

    鎖的目標列號(classid和objid指表本身),如果目標是某種其他普通數據庫對象則此列為0,如果目標不是一個普通數據庫對象則此列為空。

  • virtualtransaction text

    保持這個鎖或者正在等待這個鎖的事務的虛擬ID。

  • pid int4

    保持這個鎖或者正在等待這個鎖的服務器進程的PID,如果此鎖被一個預備事務所持有則此列為空。

  • mode text

    此進程已持有或者希望持有的鎖模式的名稱。

  • granted bool

    如果鎖已授予則為真,如果鎖被等待則為假。

  • fastpath bool
    如果鎖通過快速路徑獲得則為真,通過主鎖表獲得則為假。
附上一些網上摘錄的常用查鎖SQL:
1)查看當前事務鎖等待、持鎖信息的SQL:
with
t_wait as
(
select a.mode,a.locktype,a.database,a.relation,a.page,a.tuple,a.classid,a.granted,
a.objid,a.objsubid,a.pid,a.virtualtransaction,a.virtualxid,a.transactionid,a.fastpath,
b.state,b.query,b.xact_start,b.query_start,b.usename,b.datname,b.client_addr,b.client_port,b.application_name
from pg_locks a,pg_stat_activity b where a.pid=b.pid and not a.granted
),
t_run as
(
select a.mode,a.locktype,a.database,a.relation,a.page,a.tuple,a.classid,a.granted,
a.objid,a.objsubid,a.pid,a.virtualtransaction,a.virtualxid,a.transactionid,a.fastpath,
b.state,b.query,b.xact_start,b.query_start,b.usename,b.datname,b.client_addr,b.client_port,b.application_name
from pg_locks a,pg_stat_activity b where a.pid=b.pid and a.granted
),
t_overlap as
(
select r.* from t_wait w join t_run r on
(
r.locktype is not distinct from w.locktype and
r.database is not distinct from w.database and
r.relation is not distinct from w.relation and
r.page is not distinct from w.page and
r.tuple is not distinct from w.tuple and
r.virtualxid is not distinct from w.virtualxid and
r.transactionid is not distinct from w.transactionid and
r.classid is not distinct from w.classid and
r.objid is not distinct from w.objid and
r.objsubid is not distinct from w.objsubid and
r.pid <> w.pid
)
),
t_unionall as
(
select r.* from t_overlap r
union all
select w.* from t_wait w
)
select locktype,datname,relation::regclass,page,tuple,virtualxid,transactionid::text,classid::regclass,objid,objsubid,
string_agg(
Pid: ||case when pid is null then NULL else pid::text end||chr(10)||
Lock_Granted: ||case when granted is null then NULL else granted::text end|| , Mode: ||case when mode is null then NULL else mode::text end|| , FastPath: ||case when fastpath is null then NULL else fastpath::text end|| , VirtualTransaction: ||case when virtualtransaction is null then NULL else virtualtransaction::text end|| , Session_State: ||case when state is null then NULL else state::text end||chr(10)||
Username: ||case when usename is null then NULL else usename::text end|| , Database: ||case when datname is null then NULL else datname::text end|| , Client_Addr: ||case when client_addr is null then NULL else client_addr::text end|| , Client_Port: ||case when client_port is null then NULL else client_port::text end|| , Application_Name: ||case when application_name is null then NULL else application_name::text end||chr(10)||
Xact_Start: ||case when xact_start is null then NULL else xact_start::text end|| , Query_Start: ||case when query_start is null then NULL else query_start::text end|| , Xact_Elapse: ||case when (now()-xact_start) is null then NULL else (now()-xact_start)::text end|| , Query_Elapse: ||case when (now()-query_start) is null then NULL else (now()-query_start)::text end||chr(10)||
SQL (Current SQL in Transaction): ||chr(10)||
case when query is null then NULL else query::text end,
chr(10)||--------||chr(10)
order by
( case mode
when INVALID then 0
when AccessShareLock then 1
when RowShareLock then 2
when RowExclusiveLock then 3
when ShareUpdateExclusiveLock then 4
when ShareLock then 5
when ShareRowExclusiveLock then 6
when ExclusiveLock then 7
when AccessExclusiveLock then 8
else 0
end  ) desc,
(case when granted then 0 else 1 end)
) as lock_conflict
from t_unionall
group by
locktype,datname,relation,page,tuple,virtualxid,transactionid::text,classid,objid,objsubid ;
輸出結果格式如下:
注:Lock_Granted: true即為堵塞源。
2)查看堵塞會話,并生成kill sql:
with recursive tmp_lock as (
select distinct
--w.mode w_mode,w.page w_page,
--w.tuple w_tuple,w.xact_start w_xact_start,w.query_start w_query_start,
--now()-w.query_start w_locktime,w.query w_query
w.pid as id,--w_pid,
r.pid as parentid--r_pid,
--r.locktype,r.mode r_mode,r.usename r_user,r.datname r_db,
--r.relation::regclass,
--r.page r_page,r.tuple r_tuple,r.xact_start r_xact_start,
--r.query_start r_query_start,
--now()-r.query_start r_locktime,r.query r_query,
from (
select a.mode,a.locktype,a.database,
a.relation,a.page,a.tuple,a.classid,
a.objid,a.objsubid,a.pid,a.virtualtransaction,a.virtualxid,
a.transactionid,
b.query as query,
b.xact_start,b.query_start,b.usename,b.datname
from pg_locks a,
pg_stat_activity b
where a.pid=b.pid
and not a.granted
) w,
(
select a.mode,a.locktype,a.database,
a.relation,a.page,a.tuple,a.classid,
a.objid,a.objsubid,a.pid,a.virtualtransaction,a.virtualxid,
a.transactionid,
b.query as query,
b.xact_start,b.query_start,b.usename,b.datname
from pg_locks a,
pg_stat_activity b -- select pg_typeof(pid) from pg_stat_activity
where a.pid=b.pid
and a.granted
) r
where 1=1
and r.locktype is not distinct from w.locktype
and r.database is not distinct from w.database
and r.relation is not distinct from w.relation
and r.page is not distinct from w.page
and r.tuple is not distinct from w.tuple
and r.classid is not distinct from w.classid
and r.objid is not distinct from w.objid
and r.objsubid is not distinct from w.objsubid
and r.transactionid is not distinct from w.transactionid
and r.pid <> w.pid

),tmp0 as (
select *
from tmp_lock tl
union all
select t1.parentid,0::int4
from tmp_lock t1
where 1=1
and t1.parentid not in (select id from tmp_lock)
),tmp3 (pathid,depth,id,parentid) as (
SELECT array[id]::text[] as pathid,1 as depth,id,parentid
FROM tmp0
where 1=1
and parentid=0
union
SELECT t0.pathid||array[t1.id]::text[] as pathid,t0.depth+1 as depth,t1.id,t1.parentid
FROM tmp0 t1,
tmp3 t0
where 1=1
and t1.parentid=t0.id
)
select distinct
/||array_to_string(a0.pathid,/) as pathid,
a0.depth,
a0.id,a0.parentid,lpad(a0.id::text, 2*a0.depth-1+length(a0.id::text), ) as tree_id,
--select pg_cancel_backend(||a0.id|| ); as cancel_pid,
--select pg_terminate_backend(||a0.id|| ); as term_pid,
case when a0.depth =1 then select pg_terminate_backend(|| a0.id || ); else null end  as term_pid,
case when a0.depth =1 then select cancel_backend(|| a0.id || ); else null end  as cancel_pid
,a2.datname,a2.usename,a2.application_name,a2.client_addr,a2.wait_event_type,a2.wait_event,a2.state
--,a2.backend_start,a2.xact_start,a2.query_start
from tmp3 a0
left outer join (select distinct /||id||/ as prefix_id,id
from tmp0
where 1=1 ) a1
on position( a1.prefix_id in /||array_to_string(a0.pathid,/)||/ ) >0
left outer join pg_stat_activity a2 -- select * from pg_stat_activity
on a0.id = a2.pid
order by /||array_to_string(a0.pathid,/),a0.depth;
輸出結果格式如下:

本文作者:魏 斌(上海新炬王翦團隊)

本文來源:“IT那活兒”公眾號

文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。

轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/129295.html

相關文章

  • 線程間的同步與通信(3)——淺析synchronized的實現原理

    摘要:由此可見,自旋鎖和各有優劣,他們分別適用于競爭不多和競爭激烈的場景中。每一個試圖進入同步代碼塊的線程都會被封裝成對象,它們或在對象的中,或在中,等待成為對象的成為的對象即獲取了監視器鎖。 前言 系列文章目錄 前面兩篇文章我們介紹了synchronized同步代碼塊以及wait和notify機制,大致知道了這些關鍵字和方法是干什么的,以及怎么用。 但是,知其然,并不知其所以然。 例如...

    keithxiaoy 評論0 收藏0
  • 淺析如何用Redis實現分布式

    摘要:刪除在使用實現分布式鎖的時候,主要就會使用到這三個命令。其實,使用的可靠性是要大于使用實現的分布式鎖的,但是相比而言,的性能更好。 選用Redis實現分布式鎖原因 Redis有很高的性能 Redis命令對此支持較好,實現起來比較方便 使用命令介紹 SETNX SETNX key val當且僅當key不存在時,set一個key為val的字符串,返回1;若key存在,則什么都不做,返回...

    張率功 評論0 收藏0
  • PostgreSQL9.6:新增pg_blocking_pids函數準確定位 Blocking SQ

    摘要:創建測試表會話一備注會話一在事務里更新的記錄,并不提交。會話二備注會話二刪除的記錄,此時由于這條記錄之前被并沒有提交,這句仍然處于等待狀態。 PosttgreSQL 的SQL被鎖情況在數據庫維護過程中非常常見,之前博客 PostgreSQL 鎖分析 演示了 PostgreSQL 鎖的一些場景,在開始本文的介紹之前特做以下說明,假如會話A堵住會話B,我們稱會話B為 blocked 會話...

    liuchengxu 評論0 收藏0

發表評論

0條評論

IT那活兒

|高級講師

TA的文章

閱讀更多
最新活動
閱讀需要支付1元查看
<