国产xxxx99真实实拍_久久不雅视频_高清韩国a级特黄毛片_嗯老师别我我受不了了小说

資訊專欄INFORMATION COLUMN

Redis分布式鎖

beita / 3754人閱讀

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

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

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

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

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

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

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

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) //釋放鎖

上面代碼的鎖似乎是用來加鎖整個購買操作的,但實際上這把鎖是用來鎖住市場數(shù)據(jù)的,它之所以會包圍著執(zhí)行購買操作的代碼,是因為程序在操作市場數(shù)據(jù)期間必須一直持有鎖。

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

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

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

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

需要注意的一點是,因為多個客戶端在同一時間內(nèi)設(shè)置的超時時間基本上都是相同的,所以即使有多個客戶端同時為同一個鎖設(shè)置超時時間,鎖的超時時間也不會產(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)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請注明本文地址:http://m.specialneedsforspecialkids.com/yun/44609.html

相關(guān)文章

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

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

    YanceyOfficial 評論0 收藏0
  • Redis 布式--PHP

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

    canger 評論0 收藏0
  • Redis布式

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

    LeoHsiun 評論0 收藏0

發(fā)表評論

0條評論

最新活動
閱讀需要支付1元查看
<