分布式鎖實現

2021-08-02 08:59:24 字數 4352 閱讀 9645

1,資料庫實現

原理

資料庫的行級x鎖。

優點 不需要引入第三方應用。

缺點 死鎖

對資料庫效能影響,可能較長時間占用資料庫連線資源

如果業務是分庫分表的,可能支援不了

示例**

2,快取實現
原理

通過setnx是否成功。

當且僅當 key 不存在,將 key 的值設為 value ,並返回1;若給定的 key 已經存在,則 setnx 不做任何動作,並返回0

優點 使用廣泛

缺點 當執行緒獲得鎖,測試應用掛了,那麼這個鎖只能等到超時自動刪除。所以一定要充分考慮業務可接受的超時時間。

實現

核心**

加鎖 `string ret = jedis.set(lockname, val, "nx", "ex", this.getsessionseconds());`

解鎖 long ret = jedis.del(lockname);

3,zookeeper實現
原理

zk的臨時順序節點。加鎖操作是客戶端去/lock目錄下建立臨時順序節點。客戶端檢查自己的節點序列號是目錄下最小的,則獲取鎖成功。否則件事比自己小的最大節點。等待獲取鎖。

優點 穩定高效。不存在鎖無法釋放的問題。即使宕機,zk會自己刪除臨時節點。

缺點 要接入zk,實現較複雜的客戶端邏輯。--但是,使用curator後也很簡單了。

實現

1,使用原生zookeeper api。步驟:建立根目錄,獲取鎖,建立臨時順序節點,排序,檢查自己是不是當前最小的znode,是則獲取鎖,不是則監聽比自己小的節點。釋放鎖,刪除節點。

2,使用apache的curator包,步驟:引入包curator-recipes。加鎖,interprocessmutex.acquire。解鎖,interprocessmutex.release();

簡單的令人髮指。

延伸

1,主鍵與索引

聚簇索引。innodb的資料組織形式。完整的記錄都儲存在主鍵索引中,通過主鍵索引可以找到記錄所有的列。

普通索引。存的是主鍵索引。即通過普通索引,找到主鍵,再通過主鍵索引可以找到記錄所有的列。

innodb對於主鍵使用了聚簇索引,這是一種資料儲存方式,表資料是和主鍵一起儲存,主鍵索引的葉結點儲存行資料。對於普通索引,其葉子節點儲存的是主鍵值。

2,sql執行計畫

3,mysql/innodb 加鎖機制分析

mysql是乙個支援外掛程式式儲存引擎的資料庫系統。

大體上可以將mysql分為兩部分:mysql server + 儲存引擎(如myisam、innodb)

以下內容基於innodb儲存引擎。

innodb基於多版本併發控制mvcc,與mvcc相對的是基於鎖的併發控制。

mvcc的最大好處是讀不加鎖,讀寫不衝突。這樣在讀多寫少的應用中,極大的提高了併發效能。

mvcc中讀又分為快照讀和當前讀。快照度顧名思義是讀的快照版本,有可能是歷史版本,不需要加鎖。當前讀是讀取的當前版本,當前讀返回的記錄需要加鎖,以保證其他事務不會併發修改。

那麼什時候是當前讀,什麼時候是快照讀?

快照讀:簡單的select語句。

select * from table where ..不加鎖

當前讀:特殊的select語句。

select * from table where .. for updatex鎖(排它鎖)

select * from table where .. lock in share modes鎖(共享鎖)

增刪改操作。

insert into table values (…)x鎖

update table set .. where ..x鎖

delete from table where ..x鎖

舉個例子,對於update操作,

一句sql給到mysql,mysql server解析,根據where條件,讀取第一條滿足的記錄,然後innodb將第一條記錄返回,並加鎖。

mysql server 執行update,然後innodb引擎執行update操作,並返回成功。

這樣一條記錄執行完成。

接著,進行第二條記錄的執行。

也就是說一句sql,會被拆成很多步操作。一條記錄一條記錄的加鎖,執行。

資料庫死鎖問題涉及 索引 執行順序 事務隔離級別三者。

兩階段鎖

rdbms對於加鎖的乙個原則是,兩階段鎖,即2pl。含義是,加鎖和解鎖分為兩個完全不相干的階段,加鎖時只加鎖,解鎖時只解鎖。

比如,乙個事務,先insert,再update,在delete。

那麼加鎖階段做,inser的加鎖,update的加鎖,delete的加鎖。

解鎖階段做,inser的解鎖,update的解鎖,delete的解鎖。

完全分開,不混雜。

隔離級別

rdbms四種隔離級別。

ru,read uncommit。可以讀取其他事務未提交的操作。實際工程中不會使用的,忽略。

rc,read commit。針對當前讀,加行鎖。存在幻讀情況。

rr,read repeat。針對當前讀,加行鎖,加間隙鎖(gap鎖)。所以不存在幻讀情況。

serializable。從mvcc退回到基於鎖的併發控制。讀加共享鎖,寫加排它鎖,且讀寫衝突。併發效能急劇下降,不建議使用。

幻讀:舉個例子就明白。在乙個事務中,有兩次一樣的select操作。但是第一次select之後,有其他事務做了insert操作,導致第二次查詢出的記錄多了一條。這就是幻讀。所以rr隔離加了間隙鎖,其他事務的insert操作,不能成功,也就不存在幻讀情況了。

加鎖分析

給出一條sql語句,不給出前提條件就分析是很業餘的表現。

必須要考慮哪些前提呢?

1,隔離級別

2,是不是主鍵

3,如果不是主鍵,是不是二級索引

4,如果是二級索引,它又是不是唯一索引

5,sql的執行計畫是什麼,

以一條sql為例。

update from table set .. where id = 100

1,id是主鍵,rc。 聚簇索引表id對應的這條記錄加x鎖。

2,id不是主鍵,但是二級唯一索引。rc。 id索引表id對應的這條索引記錄加x鎖。聚簇索引表對應的這條記錄加x鎖。

3,id不是主鍵,但是二級索引。rc。id索引表id對應的這條索引記錄加x鎖。聚簇索引表對應的這條記錄加x鎖。 區別就是非唯一索引,可能有多條加鎖。

幻讀就是在這種情況下發生的。

4,id上無索引。 此時只能走聚簇索引,做全表掃瞄。所有記錄全加x鎖。實際上mysql會做一層優化,mysql server會檢查將不滿足的記錄釋放掉鎖。但是這個優化違背了2pl原則。

5,id是主鍵,rr。同rc一樣。

6,id不是主鍵,但是二級唯一索引。rr。同rc一樣。

7,id不是主鍵,但是二級索引。rr。同rc的區別就是再加間隙鎖。

8,id上無索引。rr。和rc的區別是,再加上全表記錄間的間隙鎖。當然mysql也有優化。

9,serializable隔離級別。測試所有的讀都加共享鎖。

死鎖的場景

1,一種最常見的場景是,兩個事務兩條sql分別持有對方需要的行鎖。

這種情況大部分是由於加鎖順序導致,排除方法是分析事務的加鎖順序,調整加鎖順序一致。

2,另一種是兩個事務一條sql也會死鎖。

這是因為索引的問題。我們已經知道資料庫執行是一條一條執行的。

索引是單獨存在的儲存結構。

所以一條sql也是先給索引表加鎖,再給對應的聚簇索引記錄加鎖。這就會產生和第一種情況類似的場景。

因為索引是有順序的,比如name和age上都有索引。

update table set .. where name in (..);

update table set .. where age>10;

這樣兩條sql同時執行,name對應的第一條聚簇索引被鎖住,age>10對應的聚簇索引第一條也被鎖住。

然後再去鎖各自對應的第二條時,發現都被對方鎖住了。產生死鎖。

所以,死鎖歸根結底就是加鎖順序的問題。這不只是資料庫是這樣的,死鎖都是這個原因,資源的共享和占用時,需要同時持有多個資源,加鎖順序不同一定會導致死鎖。

分布式鎖 使用Redis實現分布式鎖

關於分布式鎖的實現,我的前一篇文章講解了如何使用zookeeper實現分布式鎖。關於分布式鎖的背景此處不再做贅述,我們直接討論下如何使用redis實現分布式鎖。關於redis,筆主不打算做長篇大論的介紹,只介紹下redis優秀的特性。支援豐富的資料型別,如string list map set zs...

分布式 分布式鎖

本質是利用redis的setnx 方法的特性來加鎖,setnx 即key不存在則設定key,否則直接返回false,要求在分布式系統中使用同乙個redis服務,以下提供兩種解決方案 1 直接使用redistemplate 這其實並不能完全保證高併發下的安全問題,因為可能在鎖過期之後該執行緒尚未執行完...

分布式鎖 哨兵模式 分布式鎖實現原理

背景 記錄對分布式鎖的相關理解,不斷提公升自己 可重入鎖 為什麼不建議使用redis分布鎖 主從切換可能丟失鎖資訊 考慮一下這樣的場景 在分布式環境中,很多併發需要鎖來同步,當使用redis分布式鎖,通用的做法是使用redis的setnx key value px 這樣的命令,設定乙個字段,當設定成...