日本熟妇hd丰满老熟妇,中文字幕一区二区三区在线不卡 ,亚洲成片在线观看,免费女同在线一区二区

基于TairString實現高性能分布式鎖

分布式鎖是大型應用中最常見的功能之一,基于Redis實現分布式鎖的方式有很多。本文介紹并分析常見的分布式鎖實現方式,之后結合阿里巴巴集團在使用Tair(企業版)和分布式鎖方面的業務經驗,介紹使用Tair(企業版)實現高性能分布式鎖的實踐方案。

背景信息

分布式鎖及其應用場景

應用開發時,如果需要在同進程內的不同線程并發訪問某項資源,可以使用各種互斥鎖、讀寫鎖;如果一臺主機上的多個進程需要并發訪問某項資源,則可以使用進程間同步的原語,例如信號量、管道、共享內存等。但如果多臺主機需要同時訪問某項資源,就需要使用一種在全局可見并具有互斥性的鎖了。這種鎖就是分布式鎖,可以在分布式場景中對資源加鎖,避免競爭資源引起的邏輯錯誤。

分布式鎖的特性

  • 互斥性

    在任意時刻,只有一個客戶端持有鎖。

  • 不死鎖

    分布式鎖本質上是一個基于租約(Lease)的租借鎖,如果客戶端獲得鎖后自身出現異常,鎖能夠在一段時間后自動釋放,資源不會被鎖死。

  • 一致性

    硬件故障或網絡異常等外部問題,以及慢查詢、自身缺陷等內部因素都可能導致Redis發生高可用切換,replica提升為新的master。此時,如果業務對互斥性的要求非常高,鎖需要在切換到新的master后保持原狀態。

使用原生Redis實現分布式鎖

說明

該部分介紹的實現方式同樣適用于Redis開源版

  • 加鎖

    在Redis中加鎖非常簡便,直接使用SET命令即可。示例及關鍵選項說明如下:

    SET resource_1 random_value NX EX 5

    表 1. 關鍵選項說明

    參數/選項

    說明

    resource_1

    分布式鎖的key,只要這個key存在,相應的資源就處于加鎖狀態,無法被其它客戶端訪問。

    random_value

    一個隨機字符串,不同客戶端設置的值不能相同。

    EX

    設置過期時間,單位為秒。您也可以使用PX選項設置單位為毫秒的過期時間。

    NX

    如果需要設置的key在Redis中已存在,則取消設置。

    示例代碼為resource_1這個key設置了5秒的過期時間,如果客戶端不釋放這個key,5秒后key將過期,鎖就會被系統回收,此時其它客戶端就能夠再次為資源加鎖并訪問資源了。

  • 解鎖

    解鎖一般使用DEL命令,但可能存在下列問題。

    1. t1時刻,App1設置了分布式鎖resource_1,過期時間為3秒。

    2. App1由于程序慢等原因等待超過了3秒,而resource_1已經在t2時刻被釋放。

    3. t3時刻,App2獲得這個分布式鎖。

    4. App1從等待中恢復,在t4時刻運行DEL resource_1將App2持有的分布式鎖釋放了。

    從上述過程可以看出,一個客戶端設置的鎖,必須由自己解開。因此客戶端需要先使用GET命令確認鎖是不是自己設置的,然后再使用DEL解鎖。在Redis中通常需要用Lua腳本來實現自鎖自解:

    if redis.call("get",KEYS[1]) == ARGV[1] then
        return redis.call("del",KEYS[1])
    else
        return 0
    end
  • 續租

    當客戶端發現在鎖的租期內無法完成操作時,就需要延長鎖的持有時間,進行續租(renew)。同解鎖一樣,客戶端應該只能續租自己持有的鎖。在Redis中可使用如下Lua腳本來實現續租:

    if redis.call("get",KEYS[1]) == ARGV[1] then
        return redis.call("expire",KEYS[1], ARGV[2])
    else
        return 0
    end

使用Tair實現分布式鎖

使用Tair內存型或持久內存型實例的String增強命令,無需Lua即可實現分布式鎖。

  • 加鎖

    加鎖方式與原生Redis相同,使用SET命令:

    SET resource_1 random_value NX EX 5
  • 解鎖

    直接使用Tair(企業版)CAD命令即可實現優雅而高效的解鎖:

    /* if (GET(resource_1) == my_random_value) DEL(resource_1) */
    CAD resource_1 my_random_value
  • 續租

    續租可以直接使用CAS命令實現:

    CAS resource_1 my_random_value my_random_value EX 10
    說明

    CAS命令不會檢查新設置的value和原value是否相同。

基于Jedis的示例代碼

  • 定義CAS/CAD命令

    enum TairCommand implements ProtocolCommand {
        CAD("CAD"), CAS("CAS");
    
        private final byte[] raw;
    
        TairCommand(String alt) {
          raw = SafeEncoder.encode(alt);
        }
    
        @Override
        public byte[] getRaw() {
          return raw;
        }
    }
  • 加鎖

    public boolean acquireDistributedLock(Jedis jedis,String resourceKey, String randomValue, int expireTime) {
        SetParams setParams = new SetParams();
        setParams.nx().ex(expireTime);
        String result = jedis.set(resourceKey,randomValue,setParams);
        return "OK".equals(result);
    }
  • 解鎖

    public boolean releaseDistributedLock(Jedis jedis,String resourceKey, String randomValue) {
        jedis.getClient().sendCommand(TairCommand.CAD,resourceKey,randomValue);
        Long ret = jedis.getClient().getIntegerReply();
        return 1 == ret;
    }
  • 續租

    public boolean renewDistributedLock(Jedis jedis,String resourceKey, String randomValue, int expireTime) {
        jedis.getClient().sendCommand(TairCommand.CAS,resourceKey,randomValue,randomValue,"EX",String.valueOf(expireTime));
        Long ret = jedis.getClient().getIntegerReply();
        return 1 == ret;
    }

如何保障一致性

Redis的主從同步(replication)是異步進行的,如果向master發送請求修改了數據后master突然出現異常,發生高可用切換,緩沖區的數據可能無法同步到新的master(原replica)上,導致數據不一致。如果丟失的數據跟分布式鎖有關,則會導致鎖的機制出現問題,從而引起業務異常。下文介紹三種保障一致性的方法。

  • 使用紅鎖(RedLock)

    紅鎖是Redis作者提出的一致性解決方案。紅鎖的本質是一個概率問題:如果一個主從架構的Redis在高可用切換期間丟失鎖的概率是k%,那么相互獨立的N個Redis同時丟失鎖的概率是多少?如果用紅鎖來實現分布式鎖,那么丟鎖的概率是(k%)^N。鑒于Redis極高的穩定性,此時的概率已經完全能滿足產品的需求。

    說明

    紅鎖的實現并非這樣嚴格,一般保證M(1<M=<N)個同時鎖上即可,但通常仍舊可以滿足需求。

    紅鎖的問題在于:

    • 加鎖和解鎖的延遲較大。

    • 難以在集群版或者標準版(主從架構)的Redis實例中實現。

    • 占用的資源過多,為了實現紅鎖,需要創建多個互不相關的云Redis實例或者自建Redis。

  • 使用WAIT命令。

    Redis的WAIT命令會阻塞當前客戶端,直到這條命令之前的所有寫入命令都成功從master同步到指定數量的replica,命令中可以設置單位為毫秒的等待超時時間。在云Redis版中使用WAIT命令提高分布式鎖一致性的示例如下:

    SET resource_1 random_value NX EX 5
    WAIT 1 5000

    使用以上代碼,客戶端在加鎖后會等待數據成功同步到replica才繼續進行其它操作,最大等待時間為5000毫秒。執行WAIT命令后如果返回結果是1則表示同步成功,無需擔心數據不一致。相比紅鎖,這種實現方法極大地降低了成本。

    需要注意的是:

    • WAIT只會阻塞發送它的客戶端,不影響其它客戶端。

    • WAIT返回正確的值表示設置的鎖成功同步到了replica,但如果在正常返回前發生高可用切換,數據還是可能丟失,此時WAIT只能用來提示同步可能失敗,無法保證數據不丟失。您可以在WAIT返回異常值后重新加鎖或者進行數據校驗。

    • 解鎖不一定需要使用WAIT,因為鎖只要存在就能保持互斥,延遲刪除不會導致邏輯問題。

  • 使用Tair

    • Tair的CAS/CAD命令可以極大降低分布式鎖的開發和管理成本,提升鎖的性能。

    • Tair內存型實例能提供三倍于原生Redis的性能,即使是大并發的分布式鎖也不會影響正常的實例服務。

    • Tair持久內存型實例基于持久內存技術,掉電數據不丟失,每個寫操作將在持久化成功之后返回,保證了數據的實時持久化。同時,持久內存版型實例還支持配置主備實例間同步方式為半同步,保證寫入數據并同步至備節點后,才成功返回客戶端(若出現備節點故障、網絡異常等情況會降級為異步同步),保證高可用切換后數據不丟失。