1. 程式人生 > 資料庫 >如何使用Redis鎖處理併發問題詳解

如何使用Redis鎖處理併發問題詳解

前言

上週“被”上線了一個緊急專案,週五下班接到需求,週一開始思考解決方案,週三開發完成,週四走流程上線,也算是面向領導程式設計了。之前的專案裡面由於是自運維,然後大多數又都趕時間,所以在處理定時任務上面基本都是自己在伺服器上新增crontab,而不是讓多個例項自己去處理定時任務的併發鎖,並且Laravel 5.5開始自帶併發鎖,我們也快升級了。但是這次專案是Python專案,無奈只能自己實現一下,以下這個方案實現起來非常簡單且易於理解。

import redis
r = redis.Redis(...)

last_heart = 0		# 記錄上一次得到的鎖心跳
free_lock_try = 6	# 鎖無心跳的最大次數 

while not r.setnx('mylock',1):
 now_heart = r.get('mylock')
 print(f"沒獲取到鎖,now_heart={now_heart},last_heart={last_heart},free_lock_try={free_lock_try}")
 if now_heart == last_heart:
  free_lock_try = free_lock_try - 1
  if free_lock_try == 0:	# 鎖已經1分鐘沒有心跳了
   old_heart = r.getset('mylock',1)	# 將lock重置為1,並返回set之前的心跳值
   if old_heart < now_heart:
    time.sleep(10)
    continue
   else:
    break	# 成功獲取到鎖,退出迴圈
 else:
  free_lock_try = 6	# 鎖有心跳,重置free_lock_try值
  last_heart = now_heart
 time.sleep(10)

def producer_exit():
 """程式正常退出時候自動清理鎖"""
 r.delete('mylock')
import atexit
atexit.register(producer_exit)

# 業務程式碼
while True:
 r.incr('mylock')	# 讓鎖心跳加一
 ...

我們來看看這段程式都解決了併發鎖中的哪些問題

  • 高併發下,多個程序無法同時獲取到鎖。這裡使用的是redis.setnx,如果鎖已經存在,其他程序是無法重置鎖並獲取到鎖的。另外當多個程序同時發現有鎖已經沒有心跳了,使用的是redis.getset將心跳重置為1,都能set成功,但是get出來的值多個程序是不一樣的,只有真正獲取到鎖的程序返回的是之前程序的心跳,而其他程序獲取到的都是1。
  • 有鎖程序正常退出,可以使用atexit註冊程序退出函式刪除鎖,這裡也可以不要,不過下次啟動得等新的程序等待幾次心跳
  • 有鎖程序意外退出,退出後心跳不再增加,超過free_lock_try次數後,其他程序會重新設定並獲取鎖
  • 所有程序全都意外退出,這個問題不是鎖來關心的,可以使用supervisor進行守護程序。

導致Redis併發原因解釋

正所謂只有知其然才能知其所以然,只有弄明白問題出現的原因所在,才能對症下藥,尋找解決問題的良方。眾所周知,Redis程式採用單執行緒模式進行執行,作為單執行緒程式,Redis客戶端的命令是逐條執行,也叫做One by One執行。既然是逐條命令執行,從表面上來看Redis似乎不存在高併發的問題,這一觀點論也有道理,原子性的Redis命令本身也確實不存在高併發問題,這與多執行緒下的程式勃然不同。但是我們專案工作搭建Redis環境之後,通常都會是一組命令集合執行程式,一個請求中就包含了N個Redis執行命令,再加上多個客戶端請求,命令就更多了,導致連線超時、資料混亂或錯誤、請求阻塞等多種問題。

即總結為,產生Redis併發誘因是程式中的業務複雜度導致。

總結

以上就是這篇文章的全部內容了,希望本文的內容對大家的學習或者工作具有一定的參考學習價值,謝謝大家對我們的支援。