使用redis分佈式鎖解決併發線程資源共享問題

衆所周知, 在多線程中,由於共享全局變量,會致使資源修改結果不一致,因此須要加鎖來解決這個問題,保證同一時間只有一個線程對資源進行操做mysql

可是在分佈式架構中,咱們的服務可能會有n個實例,但線程鎖只對同一個實例有效,就須要用到分佈式鎖----redis setnxweb

 

原理:

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

  咱們既然要經過檢查key是否存在(存在表示有線程在修改資源,資源上鎖,其餘線程不可同時操做,若key不存在,表示資源未被線程佔用,容許線程搶佔,而後將經過setnx設置vlaue,表示資源上鎖,其餘線程不可同時操做)sql

  圖示:數據庫

  

分析:

  咱們的服務處於一個集羣中,若是隻是簡單的的使用線程鎖來解決以上問題,是存在問題的:由於線程是基於進程的,兩個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, 的這就會致使最終數據不一致,因此就要用到分佈式鎖。spa

爲何用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
相關文章
相關標籤/搜索