国产探花免费观看_亚洲丰满少妇自慰呻吟_97日韩有码在线_资源在线日韩欧美_一区二区精品毛片,辰东完美世界有声小说,欢乐颂第一季,yy玄幻小说排行榜完本

首頁 > 數(shù)據(jù)庫 > Redis > 正文

談?wù)凴edis分布式鎖的正確實現(xiàn)方法

2020-10-28 21:29:24
字體:
供稿:網(wǎng)友

前言

最近在參加學(xué)校安排的實訓(xùn)任務(wù),我們小組需完成一套分布式&微服務(wù)跨境電商,雖然這題目看起來有點老套,并且隊友多是 Java 技術(shù)棧,所以我光榮(被迫)
的成為了一名前端,并順路使用 PHP 的 Swoole 幫助負(fù)責(zé)服務(wù)器端的同學(xué)編寫了幾個微服務(wù)模塊。在小組成員之間的協(xié)作中,還是出現(xiàn)了不少有趣的火花。

在昨天 review 隊友代碼的過程中,發(fā)現(xiàn)了我們組分布式鎖的寫法似乎有點問題,實現(xiàn)代碼如下:

加鎖部分

解鎖部分

主要原理是使用了 redis 的 setnx 去插入一組 key-value,其中 key 要上鎖的標(biāo)識(在項目中是鎖死用戶 userId),如果上鎖失敗則返回 false。但是根據(jù)二段鎖的思路,仔細(xì)思考會存在這么一個有趣的現(xiàn)象:

假設(shè)微服務(wù) A 的某個請求對 userId = 7 的用戶上鎖,則微服務(wù) A 的這個請求可以讀取這個用戶的信息,且可以修改其內(nèi)容 ;其他模塊只能讀取這個用戶的信息,無法修改其內(nèi)容。
假設(shè)微服務(wù) A 的當(dāng)前請求對 userId = 7 的用戶解鎖,則所有模塊可以讀取這個用戶的信息,且可以修改其內(nèi)容
如此一來:

  • 若微服務(wù)模塊 A 接收到另一個需要修改 userId = 7 的用戶 的請求時,假設(shè)這個用戶還在被鎖狀態(tài)下,這次請求可以修改它嗎?(可以,解個鎖就行)
  • 若微服務(wù)模塊 B 接收到另一個需要修改 userId = 7 的用戶 的請求時,假設(shè)這個用戶還在被鎖狀態(tài)下,這次請求可以修改它嗎?(可以,解個鎖就行)
  • 若微服務(wù)模塊 A 執(zhí)行上鎖的請求中途意外崩掉,其他用戶還能修改信息嗎? (可以,解個鎖就行)

很明顯,這三點并不是我們所希望的。那么如何實現(xiàn)分布式鎖才是最佳實踐吶?

一個好的分布式鎖需要實現(xiàn)什么

  • 由某個模塊的某次請求上鎖,并且只有由這個模塊的這次請求解鎖(互斥,只能有一個微服務(wù)的某次請求持有鎖)
  • 若上鎖模塊的上鎖請求超時執(zhí)行,則應(yīng)自動解鎖,并還原其所做修改(容錯,就算 一個持有鎖的微服務(wù)宕機也不影響最終其他模塊的上鎖 )

我們應(yīng)該怎么做

綜上所述,我們小組的分布式鎖在實現(xiàn)模塊互斥的情況下,忽略的一個重要問題便是“請求互斥”。我們只需要在加鎖時,key-value 的值保存為當(dāng)前請求的 requestId ,解鎖時加多一次判斷,是否為同一請求即可。

那么這么修改之后,我們可以高枕無憂了嗎?

是的,夠用了。因為我們開發(fā)環(huán)境 Redis 是統(tǒng)一用一臺服務(wù)器上的單例,采用上述方式實現(xiàn)的分布式鎖并沒有什么問題,但在準(zhǔn)備部署到生產(chǎn)環(huán)境下時,突然意識到一個問題:如果實現(xiàn)主從讀寫分離,redis 多機主從同步數(shù)據(jù)時,采用的是異步復(fù)制,也便是一個“寫”操作到我們的 reids 主庫之后,便馬上返回成功(并不會等到同步到從庫后再返回,如果這種是同步完成后再返回便是同步復(fù)制),這將會造成一個問題:

假設(shè)我們的模塊 A中 id=1 的請求上鎖成功后,沒同步到從庫前主庫被我們玩壞了(宕機),則 redis 哨兵將會從從庫中選擇出一臺新的主庫,此時若模塊 A 中 id=2 的請求重新請求加鎖,將會是成功的。

技不如人,我們只能借助搜索引擎劃水了(大霧),發(fā)現(xiàn)這種情況還真的有通用的解決方案:redlock。

怎么實現(xiàn) Redlock 分布式安全鎖

首先 redlock 是 redis 官方文檔推薦的實現(xiàn)方式,本身并沒有用到主從層面的架構(gòu),采用的是多態(tài)主庫,依次去取鎖的方式。假設(shè)這里有 5 臺主庫,整體流程大致如下:

加鎖

  1. 應(yīng)用層請求加鎖
  2. 依次向 5 臺 redis 服務(wù)器發(fā)送請求
  3. 若有超過半數(shù)的服務(wù)器返回加鎖成功,則完成加鎖,如果沒有則自動執(zhí)行解鎖,并等待一段隨機時間后重試。(客觀原因加鎖失敗:網(wǎng)絡(luò)情況不好、服務(wù)器未響應(yīng)等問題, 等待一段隨機時間后重試可以避開“蜂擁而進”的情況造成服務(wù)器資源占用瞬時猛增 )
  4. 如有其中任意一臺服務(wù)器已經(jīng)持有該鎖,則加鎖失敗, 等待一段隨機時間后重試。 (主觀原因加鎖失敗:已經(jīng)被被別人鎖上了)

解鎖

直接向 5 臺服務(wù)器發(fā)起請求即可,無論這臺服務(wù)器上是不是已經(jīng)有鎖。

整體思路很簡單,但是實現(xiàn)起來仍有許多值得注意的地方。在向這 5 臺服務(wù)器發(fā)送加鎖請求時,由于會帶上一個過期時間以保證上文所提到的“自動解鎖(容錯性) ”,考慮到延時等原因,這 5 臺機自動解鎖的時間不完全相同,因此存在一個加

鎖時間差的問題,一般而言是這么解決的: