advisorylock實現高併發非堵塞式業務鎖
標籤
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的級別分事務級和會話級,根據業務的需求進行選擇。
相關文章
- 從零開始的高併發(二)--- Zookeeper實現分散式鎖分散式
- 分散式鎖--高併發優化實踐(分段加鎖思想)!分散式優化
- 高併發(鎖)
- 高併發架構系列:分散式鎖的由來、特點及Redis分散式鎖的實現詳解架構分散式Redis
- Java高併發實戰,鎖的優化Java優化
- 分散式鎖解決併發的三種實現方式分散式
- Redis實現併發阻塞鎖方案Redis
- [分散式][高併發]高併發架構分散式架構
- 高併發核心技術 - 冪等性 與 分散式鎖分散式
- 無堵塞的併發程式設計程式設計
- django框架怎麼實現高併發Django框架
- Nginx 實現高併發的原理分析Nginx
- 《JAVA併發程式設計實戰》顯式鎖Java程式設計
- Java併發之顯式鎖Java
- PHP利用Mysql鎖解決高併發PHPMySql
- 實戰:併發轉賬業務中避免死鎖的各種方法
- 設計一個支援高併發的分散式鎖,商品維度分散式
- 用PHP實現高併發伺服器PHP伺服器
- 如何快速實現高併發短文檢索
- 利用Redis實現高併發計數器Redis
- Java併發-顯式鎖篇【可重入鎖+讀寫鎖】Java
- 【高併發】面試官:講講高併發場景下如何優化加鎖方式?面試優化
- php利用pcntl擴充套件實現高併發PHP套件
- [分散式][高併發]熔斷策略和最佳實踐分散式
- 實現分散式鎖分散式
- 分散式鎖實現分散式
- 歸併排序的非遞迴實現排序遞迴
- Java 併發包中的讀寫鎖及其實現分析Java
- ZooKeeper 分散式鎖 Curator 原始碼 03:可重入鎖併發加鎖分散式原始碼
- Java ConcurrentHashMap 高併發安全實現原理解析JavaHashMap
- 資料庫系列:InnoDB下實現高併發控制資料庫
- 探索 ConcurrentHashMap 高併發性的實現機制HashMap
- 使用Spark\/BigDL高階機器學習實現壽險業務再發現 [session]Spark機器學習Session
- 用分散式鎖解決併發問題分散式
- 併發程式設計之顯式鎖原理程式設計
- 分散式鎖----Redis實現分散式Redis
- Redis實現分散式鎖Redis分散式
- 分散式鎖及其實現分散式