我是啞巴

好吧,每當我們在本地系統中工作時,一切都像黃油一樣工作。這就是為什麼我們稱之為“沒有比 127.0.0.1 更好的地方了”,但醒醒吧,面對現實

宇智波斑

嗯,在生產中事情並不總是按預期進行。大多數情況下,當您執行應用程式的多個實例時。

微服務

🚀 正如您所看到的,如果我們的應用程式的多個實例正在執行,並且假設我們的客戶端發出請求,將用戶標記為我們資料庫中的付費用戶。

  • 客戶端會請求我們的伺服器

  • 請求將到達我們的負載平衡器

  • 其中一個實例將收到請求並向我們的資料庫發出寫入查詢

看起來好像還可以吧?到現在為止都沒問題吧。

嗯,是的,到目前為止還沒有問題。但是如果我們想寫這樣的業務邏輯怎麼辦:-

  • 從資料庫中獲取用戶

  • 檢查用戶是否為免費用戶或已付費用戶

  • 如果免費,則將其標記為付費並保存在資料庫中

  • 如果已付款,請在回覆中發送「已付款」。

⚡️ 眾所周知(假設我們在這裡使用 MySQL)MySQL 資料庫符合 ACID,這意味著任何查詢都將是原子的和隔離的。這意味著 MySQL 查詢將以原子方式執行,要么通過,要么失敗。但它不會在中間退出。

🤔 但這裡有個問題。想想,想想…

  • 第 1 步:我們正在獲取使用者(原子事務)

  • 步驟2:在程式碼中執行一些業務邏輯

  • 步驟 3:如果使用者未付款則更新 MySQL 記錄(原子事務)

如果在第 2 步,又一個取消付款的請求,然後該查詢首先執行並將用戶標記為免費,然後執行第 3 步並將用戶標記為已付費,會發生什麼情況。

🕺🏻萬歲,用戶甚至無需付費即可存取我們的產品。

鎖定

✅ 救世主Locks來了

作業系統鎖定迷因

🔐 鎖是一種結構,一次只允許一個執行緒進入臨界區(不應該被多個工作執行緒存取的程式碼區塊)

因此,我們將在操作完成之前獲取鎖定並在操作完成後釋放:-

  • 步驟0:try-acquire()鎖

  • 第 1 步:如果已獲取,我們將獲取使用者(原子事務)

  • 步驟2:在程式碼中執行一些業務邏輯

  • 步驟 3:如果使用者未付款則更新 MySQL 記錄(原子事務)

  • 第四步:release()鎖

😅 問題

現在,問題來了,如果我們使用一些記憶體鎖資料結構或任何基於記憶體的鎖,它將適合我們的應用程式的一個實例。執行相同程式碼並在資料庫中更新的其他實例怎麼樣?

那麼這裡就涉及到分散式鎖的概念了

🔓 分散式鎖

分散式鎖

在這裡,鎖充當一種集中式服務,如果我們的服務的一個實例獲取了鎖,那麼其他實例就無法使用同一個金鑰。

支付服務中可能有什麼密鑰?

🔓 對於進行付款的用戶,金鑰可以是 = "PAYMENT_" + user_id + amount 的組合

這對於每個用戶來說都是唯一的。並且當用戶付款或取消付款時,該密鑰將保持不變。因此,當一個操作發生時,其他操作無法繼續,因為這兩個操作將嘗試取得相同的金鑰。

💭Key到底是什麼,取得鎖,釋放鎖。最重要的是,redis 是如何使用的?


🎈 使用Redis實現分散式鎖

使用 Redis 的單一實例:-

Redis單一實例鎖

但單一 redis 實例存在以下幾個問題:-

  • 單一實例可能會失敗且取得的鎖定可能無法釋放

  • 如果使用兩個實例(主副本),則一個客戶端將取得一個實例上的鎖

  • 主伺服器必須與副本進行相同的通訊才能同步。此通信本身是異步通信

🚀 因此,如果在主伺服器上取得了鎖,並且在與副本通訊時,如果主伺服器在與副本同步之前發生故障。副本將成為主伺服器,其中相同金鑰上的鎖將可用於取得先前在主伺服器上取得的鎖定。

即使有兩個實例(主副本),我們服務的兩個實例也將能夠取得 Redis 上的鎖。

使用 Redlock 演算法:-

取得鎖:- 我們將嘗試在具有鎖定過期時間的多個 Redis 實例上取得鎖

鎖定驗證:如果主要 Redis 實例為用戶端取得了鎖,則將被視為已取得鎖定

釋放鎖:-釋放鎖時,所有實例都釋放鎖

Redlock演算法

是的,就是這樣。

❤️感謝您的閱讀,並訂閱我們的電子報以獲取更多此類文章:- https://www.serversidedigest.com/

欲了解更多資訊:-


原文出處:https://dev.to/ssd/how-to-implement-a-distributed-lock-using-redis-he


共有 0 則留言