亚洲中字慕日产2020,大陆极品少妇内射AAAAAA,无码av大香线蕉伊人久久,久久精品国产亚洲av麻豆网站

資訊專欄INFORMATION COLUMN

Redis分布式鎖

beita / 3805人閱讀

摘要:分布式鎖也有類似的首先獲取鎖,然后執(zhí)行操作,最后釋放鎖動(dòng)作,但這種鎖既不是給同一個(gè)進(jìn)程中的多個(gè)線程使用,也不是給同一臺(tái)機(jī)器上的多個(gè)進(jìn)程使用,而是由不同機(jī)器上的不同客戶端進(jìn)行獲取和釋放的。

一般來說,在對(duì)數(shù)據(jù)進(jìn)行“加鎖”時(shí),程序首先需要通過獲?。╝cquire)鎖來得到對(duì)數(shù)據(jù)進(jìn)行排他性訪問的能力,然后才能對(duì)數(shù)據(jù)執(zhí)行一系列操作,最后還要釋放(release)給其他程序。對(duì)于能夠被多個(gè)線程訪問的共享內(nèi)存數(shù)據(jù)結(jié)構(gòu)(shared-memory data structure)來說,這種“先獲取鎖,然后執(zhí)行操作,最后釋放鎖”的動(dòng)作非常常見。Redis使用WATCH命令來代替對(duì)數(shù)據(jù)進(jìn)行加鎖,因?yàn)閃ATCH只會(huì)在數(shù)據(jù)被其他客戶端搶先修改了的情況下通知執(zhí)行了這個(gè)命令的客戶端,而不會(huì)阻止其他客戶端對(duì)數(shù)據(jù)的修改,所以這個(gè)命令被稱為樂觀鎖(optimistic locking)。
分布式鎖也有類似的“首先獲取鎖,然后執(zhí)行操作,最后釋放鎖”動(dòng)作,但這種鎖既不是給同一個(gè)進(jìn)程中的多個(gè)線程使用,也不是給同一臺(tái)機(jī)器上的多個(gè)進(jìn)程使用,而是由不同機(jī)器上的不同Redis客戶端進(jìn)行獲取和釋放的。

為了防止客戶端在取得鎖之后崩潰,并導(dǎo)致鎖一直處于“已被獲取”的狀態(tài),最終版的鎖實(shí)現(xiàn)將帶有超時(shí)限制特性:如果獲得鎖的進(jìn)程未能在指定的時(shí)限內(nèi)完成操作,那么鎖將自動(dòng)釋放。

導(dǎo)致鎖出現(xiàn)不正確行為的原因,以及鎖在不正確運(yùn)行時(shí)的癥狀:
持有鎖的進(jìn)程因?yàn)椴僮鲿r(shí)間過長而導(dǎo)致鎖被自動(dòng)釋放,但進(jìn)程本身并不知曉這一點(diǎn),甚至還可能會(huì)錯(cuò)誤地釋放掉了其他進(jìn)程持有的鎖。
一個(gè)持有鎖并打算執(zhí)行長時(shí)間操作的進(jìn)程已經(jīng)崩潰,但其他想要獲取鎖的進(jìn)程不知道哪個(gè)進(jìn)程持有著鎖,也無法檢測(cè)出持有鎖的進(jìn)程已經(jīng)崩潰,只能白白地浪費(fèi)時(shí)間等待鎖被釋放。
在一個(gè)進(jìn)程持有的鎖過期之后,其他多個(gè)進(jìn)程同時(shí)嘗試去獲取鎖,并且都獲得了鎖。
上面第一種情況和第三種情況同時(shí)出現(xiàn),導(dǎo)致有多個(gè)進(jìn)程獲得了鎖,而每個(gè)進(jìn)程都以為自己是唯一一個(gè)獲得鎖的進(jìn)程。

簡(jiǎn)易鎖
為了對(duì)數(shù)據(jù)進(jìn)行排他性訪問,程序首先要做的就是獲取鎖。SETNX命令天生就適合用來實(shí)現(xiàn)鎖的獲取功能,這個(gè)命令只會(huì)在鍵不存在的情況下為鍵賦值,而鎖要做的就是將一個(gè)隨機(jī)生成的128位UUID設(shè)置為鍵的值,并使用這個(gè)值來防止鎖被其他進(jìn)程取得。
如果程序嘗試獲取鎖的時(shí)候失敗,那么它將不斷地進(jìn)行重試,直到成功地取得鎖或者超過給定的時(shí)限為止。

def acquire_lock(conn, lockname, acquire_timeout=10):
    identifier = str(uuid.uuid4()) //128位隨機(jī)標(biāo)識(shí)符
    
    end = time.time() + acquire_timeout
    while time.time() < end:
        if conn.setnx("lock:" + lockname, identifier): //嘗試獲取鎖
            return identifier
        time.sleep(.001)
    
    return False

下面代碼展示了使用鎖重新實(shí)現(xiàn)的商品購買操作:程序首先對(duì)市場(chǎng)進(jìn)行加鎖,接著檢查商品的價(jià)格,并在確保買家有足夠的錢來購買商品之后,對(duì)錢和商品進(jìn)行相應(yīng)的轉(zhuǎn)移。當(dāng)操作執(zhí)行完之后,程序就會(huì)釋放鎖。

def purchase_item_with_lock(conn, buyerid, itemid, sellerid):
    buyer = "users:%s"%buyerid
    sellerid = "users:%s"%sellerid
    item = "%s.%s"%(itemid, sellerid)
    inventory = "inventory:%s"%buyerid
    
    locked = acquire_lock(conn, market)
    if not locked:
        return False
    
    pipe = conn.pipeline(True)
    try://檢查指定的商品是否仍在出售,以及買家是否有足夠的錢來購買該商品
        pipe.zscore("market:", item)
        pipe.hget(buyer, "funds")
        price, funds = pipe.execute()
        if price is None or price > funds:
            return None
        
        pipe.hincrby(seller, "funds", int(price))
        pipe.hincrby(buyer, "funds", int(-price))
        pipe.sadd(inventory, itemid)
        pipe.zrem("market:", item)
        pipe.execute()
        return True
    
    finally:
        release_lock(conn, market, locked) //釋放鎖

上面代碼的鎖似乎是用來加鎖整個(gè)購買操作的,但實(shí)際上這把鎖是用來鎖住市場(chǎng)數(shù)據(jù)的,它之所以會(huì)包圍著執(zhí)行購買操作的代碼,是因?yàn)槌绦蛟诓僮魇袌?chǎng)數(shù)據(jù)期間必須一直持有鎖。

接下面的代碼release_lock函數(shù)展示了鎖釋放操作的實(shí)現(xiàn)代碼:函數(shù)首先使用WATCH命令監(jiān)視代表鎖的鍵,接著檢查鍵目前的值是否和加鎖時(shí)設(shè)置的值相同,并且確認(rèn)值沒有變化之后刪除該鍵(這個(gè)檢查還可以防止程序錯(cuò)誤地釋放同一個(gè)鎖多次)。

def release_lock(conn, lockname, identifier):
    pipe = conn.pipeline(True)
    lockname = "lock:" + lockname
    
    while True:
        try:
            pipe.watch(lockname)
            if pipe.get(lockname) == identifier:
                pipe.multi()
                pipe.delete(lockname)
                pipe.execute()
                return True
            
            pipe.unwatch()
            break
         except redis.exceptions.WatchError:
             pass
     
     return False

經(jīng)過測(cè)試,與之前WATCH實(shí)現(xiàn)相比,鎖實(shí)現(xiàn)的上架商品數(shù)量雖然有所減少,但是在買入商品時(shí)卻不需要進(jìn)行重試,并且上架商品數(shù)量和買入商品數(shù)量之間的比率,也跟賣家數(shù)量和買家數(shù)量之間的比率接近。

帶有超時(shí)限制的鎖
目前的鎖實(shí)現(xiàn)在持有者崩潰的時(shí)候不會(huì)自動(dòng)釋放,這將導(dǎo)致鎖一直處于已被獲取的狀態(tài)。為了解決這個(gè)問題,我們將為鎖加上超時(shí)功能。

為了給鎖加上超時(shí)的限制特性,程序?qū)⒃谌〉面i之后,調(diào)用EXPIRE命令來為鎖設(shè)置過期時(shí)間,使得Redis可以自動(dòng)刪除超時(shí)的鎖。為了確保鎖在客戶端已經(jīng)崩潰(客戶端在執(zhí)行介于SETNX和EXPIRE之間的時(shí)候崩潰是最糟糕的)的情況下仍然能夠自動(dòng)被釋放,客戶端會(huì)嘗試獲取鎖失敗之后,檢查鎖的超時(shí)時(shí)間,并為未設(shè)置超時(shí)時(shí)間的鎖設(shè)置超時(shí)時(shí)間。因?yàn)殒i總會(huì)帶有超時(shí)時(shí)間,并最終因?yàn)槌瑫r(shí)而自動(dòng)被釋放,使得其他客戶端可以繼續(xù)嘗試獲取已被釋放的鎖。

需要注意的一點(diǎn)是,因?yàn)槎鄠€(gè)客戶端在同一時(shí)間內(nèi)設(shè)置的超時(shí)時(shí)間基本上都是相同的,所以即使有多個(gè)客戶端同時(shí)為同一個(gè)鎖設(shè)置超時(shí)時(shí)間,鎖的超時(shí)時(shí)間也不會(huì)產(chǎn)生太大變化。

def acquire_lock_with_timeout(conn, lockname, acquire_timeout=10, lock_timeout=10):
    identifier = str(uuid.uuid4())
    lockname = "lock:" + lockname
    lock_timeout = int(math.ceil(lock_timeout))
    
    end = time.time() + acquire_timeout
    while time.time() < end:
        if conn.setnx(lockname, identifier):
            conn.expire(lockname, lock_timeout)
            return identifier
        elif not conn.ttl(lockname):
            conn.expire(lockname, lock_timeout)
        
        time.sleep(.001)
     
     return False

文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請(qǐng)注明本文地址:http://www.ezyhdfw.cn/yun/44609.html

相關(guān)文章

  • 百度社招面試題——如何用Redis實(shí)現(xiàn)布式

    摘要:集群實(shí)現(xiàn)分布式鎖上面的討論中我們有一個(gè)非常重要的假設(shè)是單點(diǎn)的。但是其實(shí)這已經(jīng)超出了實(shí)現(xiàn)分布式鎖的范圍,單純用沒有命令來實(shí)現(xiàn)生成。這個(gè)問題用實(shí)現(xiàn)分布式鎖暫時(shí)無解。結(jié)論并不能實(shí)現(xiàn)嚴(yán)格意義上的分布式鎖。 關(guān)于Redis實(shí)現(xiàn)分布式鎖的問題,網(wǎng)絡(luò)上很多,但是很多人的討論基本就是把原來博主的貼過來,甚至很多面試官也是一知半解經(jīng)不起推敲就來面候選人,最近結(jié)合我自己的學(xué)習(xí)和資料查閱,整理一下用Redi...

    YanceyOfficial 評(píng)論0 收藏0
  • Redis 布式--PHP

    摘要:分布式鎖的作用在單機(jī)環(huán)境下,有個(gè)秒殺商品的活動(dòng),在短時(shí)間內(nèi),服務(wù)器壓力和流量會(huì)陡然上升。分布式集群業(yè)務(wù)業(yè)務(wù)場(chǎng)景下,每臺(tái)服務(wù)器是獨(dú)立存在的。這里就用到了分布式鎖這里簡(jiǎn)單介紹一下,以的事務(wù)機(jī)制來延生。 Redis 分布式鎖的作用 在單機(jī)環(huán)境下,有個(gè)秒殺商品的活動(dòng),在短時(shí)間內(nèi),服務(wù)器壓力和流量會(huì)陡然上升。這個(gè)就會(huì)存在并發(fā)的問題。想要解決并發(fā)需要解決以下問題 1、提高系統(tǒng)吞吐率也就是qps 每...

    canger 評(píng)論0 收藏0
  • Redis布式

    摘要:之分布式鎖的實(shí)現(xiàn)方案如何優(yōu)雅地實(shí)現(xiàn)分布式鎖博客地址分布式鎖關(guān)鍵詞分布式鎖是控制分布式系統(tǒng)之間同步訪問共享資源的一種方式。 Redis之分布式鎖的實(shí)現(xiàn)方案 - 如何優(yōu)雅地實(shí)現(xiàn)分布式鎖(JAVA) 博客地址 https://blog.piaoruiqing.cn/2019/05/19/redis分布式鎖/ 關(guān)鍵詞 分布式鎖: 是控制分布式系統(tǒng)之間同步訪問共享資源的一種方式。 spring-d...

    LeoHsiun 評(píng)論0 收藏0

發(fā)表評(píng)論

0條評(píng)論

最新活動(dòng)
閱讀需要支付1元查看
<