advisorylock實現高併發非堵塞式業務鎖

德哥發表於2017-07-21

標籤

PostgreSQL , advisory lock , 鎖


背景

某些業務會利用資料庫來作為一種可靠的鎖,例如任務排程系統,或者其他需要可靠的鎖機制的系統。

通常他們可能會使用資料庫的一條記錄來實現鎖的SLOT和狀態資訊。

例如

create table lock_test (  
  tid int primary key,   -- 任務ID  
  state int default 1,   -- 任務狀態,1表示初始狀態,-1表示正在處理, 0表示處理結束  
  retry int default -1,   -- 重試次數  
  info text,   -- 其他資訊  
  crt_time timestamp  -- 時間  
);  

任務處理系統到資料庫獲取任務

例如

update lock_test set state=-1 , retry=retry+1 where tid=? and state=1;  

處理失敗

update lock_test set state=1 where tid=? and state=-1;  

處理成功

update lock_test set state=0 where tid=? and state=-1;  

當多個客戶端並行獲得同一個任務時,就會引發衝突,導致等待(雖然等待時間可能不長,但是在大型任務排程系統中,一點點的等待都無法忍受)。

如何解決這個衝突等待呢?

advisory lock登場,實際上在秒殺業務中我們也看到了它的蹤影。

《PostgreSQL 使用advisory lock實現行級讀寫堵塞》

《PostgreSQL 無縫自增ID的實現 – by advisory lock》

《PostgreSQL 使用advisory lock或skip locked消除行鎖衝突, 提高几十倍併發更新效率》

《聊一聊雙十一背後的技術 – 不一樣的秒殺技術, 裸秒》

advisory lock 實現高併發非堵塞式 業務鎖

事務級或會話級,根據業務形態選擇。

                                        List of functions  
   Schema   |               Name               | Result data type | Argument data types |  Type    
------------+----------------------------------+------------------+---------------------+--------  
 pg_catalog | pg_try_advisory_lock             | boolean          | bigint              | normal  
 pg_catalog | pg_try_advisory_xact_lock        | boolean          | bigint              | normal  

SQL改造如下

開始處理任務

update lock_test set state=-1 , retry=retry+1 where tid=? and state=1 and pg_try_advisory_xact_lock(?) returning *;  

處理失敗

update lock_test set state=1 where tid=? and state=-1 and pg_try_advisory_xact_lock(?);  

處理成功

update lock_test set state=0 where tid=? and state=-1 and pg_try_advisory_xact_lock(?);  

效能壓測對比

為了體現衝突的問題,我們使用一條記錄來表示一個任務,大家都來搶一個任務的極端場景。

create table lock_test (  
  tid int primary key,   -- 任務ID  
  state int default 1,   -- 任務狀態,1表示初始狀態,-1表示正在處理, 0表示處理結束  
  retry int default -1,   -- 重試次數  
  info text,   -- 其他資訊  
  crt_time timestamp  -- 時間  
);  
  
insert into lock_test values (1, 1, -1, `test`, now());  

1、傳統模式壓測

vi test1.sql  
  
update lock_test set state=-1 , retry=retry+1 where tid=1 and state=1;  
update lock_test set state=1 where tid=1 and state=-1;  
  
pgbench -M prepared -n -r -P 1 -f ./test1.sql -c 64 -j 64 -T 120  
  
query mode: prepared  
number of clients: 64  
number of threads: 64  
duration: 120 s  
number of transactions actually processed: 966106  
latency average = 7.940 ms  
latency stddev = 6.840 ms  
tps = 8050.081170 (including connections establishing)  
tps = 8054.812052 (excluding connections establishing)  
script statistics:  
 - statement latencies in milliseconds:  
         3.978  update lock_test set state=-1 , retry=retry+1 where tid=1 and state=1;  
         3.962  update lock_test set state=1 where tid=1 and state=-1;  

2、advisory lock模式壓測

vi test2.sql  
  
update lock_test set state=-1 , retry=retry+1 where tid=1 and state=1 and pg_try_advisory_xact_lock(1) returning *;  
update lock_test set state=1 where tid=1 and state=-1 and pg_try_advisory_xact_lock(1);  
  
pgbench -M prepared -n -r -P 1 -f ./test2.sql -c 64 -j 64 -T 120  
  
query mode: prepared  
number of clients: 64  
number of threads: 64  
duration: 120 s  
number of transactions actually processed: 23984594  
latency average = 0.320 ms  
latency stddev = 0.274 ms  
tps = 199855.983575 (including connections establishing)  
tps = 199962.502494 (excluding connections establishing)  
script statistics:  
 - statement latencies in milliseconds:  
         0.163  update lock_test set state=-1 , retry=retry+1 where tid=1 and state=1 and pg_try_advisory_xact_lock(1) returning *;  
         0.156  update lock_test set state=1 where tid=1 and state=-1 and pg_try_advisory_xact_lock(1);  

8000 TPS提升到20萬 TPS。開不開心、意不意外。

小結

1、使用advisory lock時,需要注意一點,因為它是庫級別的輕量級鎖,所以對於不同的業務(無需相互堵塞的業務),建議設計不同的advisory lock的ID空間,例如A業務的LOCK空間是1-1000000, B業務的LOCK空間是1000001-2000000的空間。諸如此類等等。

2、update, insert, delete都帶returning語法,可以返回NEW, OLD value。

3、advisory 的其他應用:

《PostgreSQL 使用advisory lock實現行級讀寫堵塞》

《PostgreSQL 無縫自增ID的實現 – by advisory lock》

《PostgreSQL 使用advisory lock或skip locked消除行鎖衝突, 提高几十倍併發更新效率》

《聊一聊雙十一背後的技術 – 不一樣的秒殺技術, 裸秒》

4、advisory lock的級別分事務級和會話級,根據業務的需求進行選擇。


相關文章