首先聲明:未經本人同意,請勿轉載,謝謝!
本人使用自己編譯的開源版本的greenplum數據庫用於學習,版本為PostgreSQL 8.3.23 (Greenplum Database 4.3.99.00 build dev) on x86_64-unknown-linux-gnu, compiled by GCC gcc (GCC) 4.8.5 20150623
在使用的過程中遇到不少的問題,今天記錄一下高並發的情況下,執行insert和vacuum操作造成的死鎖,以及解決方案
一、問題描述:
在對ao分區表進行高並發測試的時候同時執行了vacuum的動作,發現會有死鎖的問題產生,可以通過如下手段復現:
1)多個線程循環執行insert into t3 select * from XX(t3為本例中用於測試的分區表),為了提高問題復現的速度,可以僅對表中的一個分區進行操作
2)單個線程循環執行vacuum的動作
一段時間后會發現vacuum和insert都卡住,至此問題被復現出來。
二、結論:
該問題僅在AO分區表中會出現,產生的原因是由於數據庫加鎖的流程設計不合理導致(詳情見分析過程)。
三、問題定位及解決方案
拋磚引玉,僅分享自己知道的一點點東西,如果有錯誤的地方還請指正,歡迎大家一起來討論這個問題。
1)首先連接master執行:
select * from pg_stat_activity;
查到如下結果:
(圖一)
發現有一個session是lock狀態,為了弄清楚這里的lock的具體情況,則連接master和standby查看鎖的具體情況,執行如下sql:
select a.locktype,b.relname,substring(c.current_query,1,50),c.xact_start,a.pid,a.mode,a.granted from pg_locks a,pg_class b,pg_stat_activity c where a.relation = b.oid and a.pid = c.procpid and relname like 't3%';
master:
(圖二)
segment1:
(圖三)
segment2:
(圖四)
需要注意的是,這里圖三和圖四中在pg_stat_activity表中的current_query一列有<IDLE> in transaction,這里的值和我們平時看到的<IDLE>不是一回事,並不是一個空閑的連接,要弄清楚它的意義來看下官方文檔:
(圖五)
官方的文檔里,對該值僅有這么一小段描述,這里並不能完全的表達出這個字段的意思,我們來看源碼:
(圖六)
源碼里就可以看出這種狀態說明該連接的事務是處於阻塞狀態。
那么又是為什么會有鎖等待的出現呢,因為同一時間,針對同一個對象,有些鎖是不能夠同時被不同的事務所持有的,如果一個事務持有了某個鎖,另一個事務需要獲取相同的鎖或者是與這個鎖沖突的鎖,就會出現等待的情況,我們來看下鎖之間的沖突情況:
(圖七)
綜合以上的所有信息,畫出了如下的圖:
(圖七)
說明:
相同顏色的色塊表示同一個連接(在物理機上,由ps -ef | grep $pid查到的con確定),紅色的線表示等待關系。


create or replace function f_lock_level(i_mode text) returns integer as $$ begin RETURN (select case i_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 ); end; $$ language plpgsql strict; --查詢 with t_wait as (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.current_query,b.xact_start,b.query_start, b.usename,b.datname from pg_locks a,pg_stat_activity b where a.pid=b.procpid and not a.granted), t_run as (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.current_query,b.xact_start,b.query_start, b.usename,b.datname from pg_locks a,pg_stat_activity b where a.pid=b.procpid and a.granted) select r.locktype,r.mode r_mode,r.usename r_user,r.datname r_db,r.relation::regclass,r.pid r_pid, 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.current_query r_query,w.mode w_mode,w.pid w_pid,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.current_query w_query from t_wait w,t_run r where 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 order by f_lock_level(w.mode)+f_lock_level(r.mode) desc,r.xact_start;
注意:上面的sql適用於文章開頭部分greenplum內核版本,不同的版本會有些許差異,這個sql是我根據文章最后參考資料德哥給出的SQL自己修改的,如果您的環境中無法運行,請使用德哥給出的語句。
該sql會返回類似如下的結果(部分):
(圖十)

