使用redis分布式鎖解決并發(fā)線程資源共享問題

前言

創(chuàng)新互聯(lián)公司從2013年創(chuàng)立,是專業(yè)互聯(lián)網(wǎng)技術(shù)服務(wù)公司,擁有項(xiàng)目網(wǎng)站設(shè)計(jì)制作、網(wǎng)站設(shè)計(jì)網(wǎng)站策劃,項(xiàng)目實(shí)施與項(xiàng)目整合能力。我們以讓每一個(gè)夢想脫穎而出為使命,1280元易門做網(wǎng)站,已為上家服務(wù),為易門各地企業(yè)和個(gè)人服務(wù),聯(lián)系電話:028-86922220

眾所周知, 在多線程中,因?yàn)楣蚕砣肿兞?會(huì)導(dǎo)致資源修改結(jié)果不一致,所以需要加鎖來解決這個(gè)問題,保證同一時(shí)間只有一個(gè)線程對(duì)資源進(jìn)行操作

但是在分布式架構(gòu)中,我們的服務(wù)可能會(huì)有n個(gè)實(shí)例,但線程鎖只對(duì)同一個(gè)實(shí)例有效,就需要用到分布式鎖----redis setnx

原理

修改某個(gè)資源時(shí), 在redis中設(shè)置一個(gè)key,value根據(jù)實(shí)際情況自行決定如何表示

我們既然要通過檢查key是否存在(存在表示有線程在修改資源,資源上鎖,其他線程不可同時(shí)操作,若key不存在,表示資源未被線程占用,允許線程搶占,然后將通過setnx設(shè)置vlaue,表示資源上鎖,其他線程不可同時(shí)操作)

圖示:

使用redis分布式鎖解決并發(fā)線程資源共享問題

分析

我們的服務(wù)處于一個(gè)集群中,如果只是簡單的的使用線程鎖來解決以上問題,是存在問題的:因?yàn)榫€程是基于進(jìn)程的,兩個(gè)web server處于不同的進(jìn)程空間

也就是說,user1的請(qǐng)求發(fā)往web server1,那只能與web server1的其他請(qǐng)求進(jìn)行鎖的操作,而不能對(duì)web server2的請(qǐng)求產(chǎn)生影響

上面的圖中,user1發(fā)往web server1的請(qǐng)求負(fù)責(zé)處理的線程為Thread1,同理負(fù)責(zé)處理user2發(fā)往web server2的請(qǐng)求的線程thread2

在同一時(shí)刻1,兩個(gè)線程都讀取了MySQL中residue_ticket的值為100,對(duì)應(yīng)上圖 (1)(2), 各自對(duì)100進(jìn)行-1操作,更新到數(shù)據(jù)庫,對(duì)應(yīng)(3)(4)

我們預(yù)期的情況是residue_ticket值被減少了兩次,應(yīng)該為98,但是實(shí)際情況下,兩個(gè)線程都做了100-1=99的操作,并都將mysql中的值改為了99, 的這就會(huì)導(dǎo)致最終數(shù)據(jù)不一致,所以就要用到分布式鎖。

為什么用redis?

因?yàn)閞edis是單線程的,不存在多線程資源競爭,并且它真的很快

為什么用setnx 而不是set?

setnx表示只有在key不存在時(shí)才能設(shè)置成功,但是set會(huì)在key存在的情況下修改value

利用setnx的特性,我們可以這樣這樣設(shè)計(jì):

偽代碼:

# 設(shè)置redis鎖的
  redis key = 'residue_ticket_lock'

  # get_ticket是處理購票的邏輯
  def get_ticket():
    time_out = 5  # 為了防止線程過多,當(dāng)前線程獲取不到鎖,長時(shí)間處于循環(huán)中而導(dǎo)致的性能影響,我們設(shè)置一個(gè)超時(shí)時(shí)間,如果當(dāng)前線程在超時(shí)時(shí)間內(nèi)還沒有搶占到分布式鎖,就返回失敗的結(jié)果
    while True:
       if redis.setnx('residue_ticket_lock','lock',5):
          # 如果setnx返回True, 表示此刻沒有其他線程在操作數(shù)據(jù)庫,當(dāng)前線程可以上鎖成功,注意不僅設(shè)置了value=lock,還設(shè)置了過期時(shí)間,這是必要的,為了防止上鎖的線程異常崩掉導(dǎo)致不能釋放(刪除key)而導(dǎo)致其他所有線程永遠(yuǎn)拿不到操作權(quán)
          residue_ticket = mysql.get('residue_ticket')   # 從mysql中獲取當(dāng)前剩余票數(shù)
          mysql.update('residue_ticket',residue_ticket-1)  # 訂購成功,將票數(shù)-1,更新數(shù)據(jù)到mysql
          # 刪除key,釋放鎖
          redis.del('residue_ticket')
          return True
       else:
          # 如果setnx返回False,表示有其他線程對(duì)在操作,當(dāng)前線程等待0.01s,并繼續(xù)循環(huán)
          time.sleep(0.01)
          time_out -= 0.01
          continue
    return False

以上就是本文的全部內(nèi)容,希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持創(chuàng)新互聯(lián)。

分享文章:使用redis分布式鎖解決并發(fā)線程資源共享問題
網(wǎng)站URL:http://muchs.cn/article4/pjpdoe.html

成都網(wǎng)站建設(shè)公司_創(chuàng)新互聯(lián),為您提供移動(dòng)網(wǎng)站建設(shè)、App設(shè)計(jì)、動(dòng)態(tài)網(wǎng)站搜索引擎優(yōu)化、企業(yè)網(wǎng)站制作、靜態(tài)網(wǎng)站

廣告

聲明:本網(wǎng)站發(fā)布的內(nèi)容(圖片、視頻和文字)以用戶投稿、用戶轉(zhuǎn)載內(nèi)容為主,如果涉及侵權(quán)請(qǐng)盡快告知,我們將會(huì)在第一時(shí)間刪除。文章觀點(diǎn)不代表本網(wǎng)站立場,如需處理請(qǐng)聯(lián)系客服。電話:028-86922220;郵箱:631063699@qq.com。內(nèi)容未經(jīng)允許不得轉(zhuǎn)載,或轉(zhuǎn)載時(shí)需注明來源: 創(chuàng)新互聯(lián)

網(wǎng)站托管運(yùn)營