1. 程式人生 > 資料庫 >使用redis分散式鎖解決併發執行緒資源共享問題

使用redis分散式鎖解決併發執行緒資源共享問題

前言

眾所周知,在多執行緒中,因為共享全域性變數,會導致資源修改結果不一致,所以需要加鎖來解決這個問題,保證同一時間只有一個執行緒對資源進行操作

但是在分散式架構中,我們的服務可能會有n個例項,但執行緒鎖只對同一個例項有效,就需要用到分散式鎖----redis setnx

原理

修改某個資源時,在redis中設定一個key,value根據實際情況自行決定如何表示

我們既然要通過檢查key是否存在(存在表示有執行緒在修改資源,資源上鎖,其他執行緒不可同時操作,若key不存在,表示資源未被執行緒佔用,允許執行緒搶佔,然後將通過setnx設定vlaue,表示資源上鎖,其他執行緒不可同時操作)

圖示:

分析

我們的服務處於一個叢集中,如果只是簡單的的使用執行緒鎖來解決以上問題,是存在問題的:因為執行緒是基於程序的,兩個web server處於不同的程序空間

也就是說,user1的請求發往web server1,那隻能與web server1的其他請求進行鎖的操作,而不能對web server2的請求產生影響

上面的圖中,user1發往web server1的請求負責處理的執行緒為Thread1,同理負責處理user2發往web server2的請求的執行緒thread2

在同一時刻1,兩個執行緒都讀取了mysql中residue_ticket的值為100,對應上圖 (1)(2), 各自對100進行-1操作,更新到資料庫,對應(3)(4)

我們預期的情況是residue_ticket值被減少了兩次,應該為98,但是實際情況下,兩個執行緒都做了100-1=99的操作,並都將mysql中的值改為了99,的這就會導致最終資料不一致,所以就要用到分散式鎖。

為什麼用redis?

因為redis是單執行緒的,不存在多執行緒資源競爭,並且它真的很快

為什麼用setnx 而不是set?

setnx表示只有在key不存在時才能設定成功,但是set會在key存在的情況下修改value

利用setnx的特性,我們可以這樣這樣設計:

虛擬碼:

# 設定redis鎖的
  redis key = 'residue_ticket_lock'

  # get_ticket是處理購票的邏輯
  def get_ticket():
    time_out = 5  # 為了防止執行緒過多,當前執行緒獲取不到鎖,長時間處於迴圈中而導致的效能影響,我們設定一個超時時間,如果當前執行緒在超時時間內還沒有搶佔到分散式鎖,就返回失敗的結果
    while True:
       if redis.setnx('residue_ticket_lock','lock',5):
          # 如果setnx返回True,表示此刻沒有其他執行緒在操作資料庫,當前執行緒可以上鎖成功,注意不僅設定了value=lock,還設定了過期時間,這是必要的,為了防止上鎖的執行緒異常崩掉導致不能釋放(刪除key)而導致其他所有執行緒永遠拿不到操作權
          residue_ticket = mysql.get('residue_ticket')   # 從mysql中獲取當前剩餘票數
          mysql.update('residue_ticket',residue_ticket-1)  # 訂購成功,將票數-1,更新資料到mysql
          # 刪除key,釋放鎖
          redis.del('residue_ticket')
          return True
       else:
          # 如果setnx返回False,表示有其他執行緒對在操作,當前執行緒等待0.01s,並繼續迴圈
          time.sleep(0.01)
          time_out -= 0.01
          continue
    return False

以上就是本文的全部內容,希望對大家的學習有所幫助,也希望大家多多支援我們。