亚洲乱码中文字幕综合,中国熟女仑乱hd,亚洲精品乱拍国产一区二区三区,一本大道卡一卡二卡三乱码全集资源,又粗又黄又硬又爽的免费视频

關(guān)于分布式鎖的三種實現(xiàn)方式

 更新時間:2022年08月26日 10:28:55   作者:頭未禿  
這篇文章主要介紹了關(guān)于分布式鎖的三種實現(xiàn)方式,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教

Java中的鎖主要包括synchronized鎖和JUC包中的鎖,這些鎖都是針對單個JVM實例上的鎖,對于分布式環(huán)境如果我們需要加鎖就顯得無能為力。

在單個JVM實例上,鎖的競爭者通常是一些不同的線程,而在分布式環(huán)境中,鎖的競爭者通常是一些不同的線程或者進(jìn)程。如何實現(xiàn)在分布式環(huán)境中對一個對象進(jìn)行加鎖呢?答案就是分布式鎖。

分布式鎖實現(xiàn)方案

目前分布式鎖的實現(xiàn)方案主要包括三種:

  • 基于數(shù)據(jù)庫(唯一索引)
  • 基于緩存(Redis,memcached,tair)
  • 基于Zookeeper

基于數(shù)據(jù)庫實現(xiàn)分布式鎖:主要是利用數(shù)據(jù)庫的唯一索引來實現(xiàn),唯一索引天然具有排他性,這剛好符合我們對鎖的要求:同一時刻只能允許一個競爭者獲取鎖。加鎖時我們在數(shù)據(jù)庫中插入一條鎖記錄,利用業(yè)務(wù)id進(jìn)行防重。當(dāng)?shù)谝粋€競爭者加鎖成功后,第二個競爭者再來加鎖就會拋出唯一索引沖突,如果拋出這個異常,我們就判定當(dāng)前競爭者加鎖失敗。防重業(yè)務(wù)id需要我們自己來定義,例如我們的鎖對象是一個方法,則我們的業(yè)務(wù)防重id就是這個方法的名字,如果鎖定的對象是一個類,則業(yè)務(wù)防重id就是這個類名。

基于緩存實現(xiàn)分布式鎖:理論上來說使用緩存來實現(xiàn)分布式鎖的效率最高,加鎖速度最快,因為Redis幾乎都是純內(nèi)存操作,而基于數(shù)據(jù)庫的方案和基于Zookeeper的方案都會涉及到磁盤文件IO,效率相對低下。一般使用Redis來實現(xiàn)分布式鎖都是利用Redis的SETNX key value這個命令,只有當(dāng)key不存在時才會執(zhí)行成功,如果key已經(jīng)存在則命令執(zhí)行失敗。

基于Zookeeper:Zookeeper一般用作配置中心,其實現(xiàn)分布式鎖的原理和Redis類似,我們在Zookeeper中創(chuàng)建瞬時節(jié)點(diǎn),利用節(jié)點(diǎn)不能重復(fù)創(chuàng)建的特性來保證排他性。

在實現(xiàn)分布式鎖的時候我們需要考慮一些問題,例如:分布式鎖是否可重入,分布式鎖的釋放時機(jī),分布式鎖服務(wù)端是否有單點(diǎn)問題等。

基于數(shù)據(jù)庫實現(xiàn)分布式鎖

上面已經(jīng)分析了基于數(shù)據(jù)庫實現(xiàn)分布式鎖的基本原理:通過唯一索引保持排他性,加鎖時插入一條記錄,解鎖是刪除這條記錄。下面我們就簡要實現(xiàn)一下基于數(shù)據(jù)庫的分布式鎖。

表設(shè)計

CREATE TABLE `distributed_lock` (
  `id` bigint(20) NOT NULL AUTO_INCREMENT,
  `unique_mutex` varchar(255) NOT NULL COMMENT '業(yè)務(wù)防重id',
  `holder_id` varchar(255) NOT NULL COMMENT '鎖持有者id',
  `create_time` datetime DEFAULT NULL ON UPDATE CURRENT_TIMESTAMP,
  PRIMARY KEY (`id`),
  UNIQUE KEY `mutex_index` (`unique_mutex`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

id字段是數(shù)據(jù)庫的自增id,unique_mutex字段就是我們的防重id,也就是加鎖的對象,此對象唯一。在這張表上我們加了一個唯一索引,保證unique_mutex唯一性。holder_id代表競爭到鎖的持有者id。

加鎖

insert into distributed_lock(unique_mutex, holder_id) values (‘unique_mutex', ‘holder_id');

如果當(dāng)前sql執(zhí)行成功代表加鎖成功,如果拋出唯一索引異常(DuplicatedKeyException)則代表加鎖失敗,當(dāng)前鎖已經(jīng)被其他競爭者獲取。

解鎖

delete from methodLock where unique_mutex=‘unique_mutex' and holder_id=‘holder_id';

解鎖很簡單,直接刪除此條記錄即可。

分析

是否可重入:就以上的方案來說,我們實現(xiàn)的分布式鎖是不可重入的,即是是同一個競爭者,在獲取鎖后未釋放鎖之前再來加鎖,一樣會加鎖失敗,因此是不可重入的。解決不可重入問題也很簡單:加鎖時判斷記錄中是否存在unique_mutex的記錄,如果存在且holder_id和當(dāng)前競爭者id相同,則加鎖成功。這樣就可以解決不可重入問題。

鎖釋放時機(jī):設(shè)想如果一個競爭者獲取鎖時候,進(jìn)程掛了,此時distributed_lock表中的這條記錄就會一直存在,其他競爭者無法加鎖。為了解決這個問題,每次加鎖之前我們先判斷已經(jīng)存在的記錄的創(chuàng)建時間和當(dāng)前系統(tǒng)時間之間的差是否已經(jīng)超過超時時間,如果已經(jīng)超過則先刪除這條記錄,再插入新的記錄。另外在解鎖時,必須是鎖的持有者來解鎖,其他競爭者無法解鎖。這點(diǎn)可以通過holder_id字段來判定。

數(shù)據(jù)庫單點(diǎn)問題:單個數(shù)據(jù)庫容易產(chǎn)生單點(diǎn)問題:如果數(shù)據(jù)庫掛了,我們的鎖服務(wù)就掛了。對于這個問題,可以考慮實現(xiàn)數(shù)據(jù)庫的高可用方案,例如MySQL的MHA高可用解決方案。

基于Zookeeper實現(xiàn)分布式鎖

前置知識

Zookeeper的數(shù)據(jù)存儲結(jié)構(gòu)就像一棵樹,這棵樹由節(jié)點(diǎn)組成,這種節(jié)點(diǎn)叫做Znode。

Znode分為四種類型:

  • 持久節(jié)點(diǎn)(PERSISTENT):默認(rèn)的節(jié)點(diǎn)類型。創(chuàng)建節(jié)點(diǎn)的客戶端與zookeeper斷開連接后,該節(jié)點(diǎn)依舊存在 。
  • 持久節(jié)點(diǎn)順序節(jié)點(diǎn)(PERSISTENT_SEQUENTIAL): 所謂順序節(jié)點(diǎn),就是在創(chuàng)建節(jié)點(diǎn)時,Zookeeper根據(jù)創(chuàng)建的時間順序給該節(jié)點(diǎn)名稱進(jìn)行編號:
  • 臨時節(jié)點(diǎn)(EPHEMERAL) :和持久節(jié)點(diǎn)相反,當(dāng)創(chuàng)建節(jié)點(diǎn)的客戶端與zookeeper斷開連接后,臨時節(jié)點(diǎn)會被刪除。
  • 臨時順序節(jié)點(diǎn)(EPHEMERAL_SEQUENTIAL) :顧名思義,臨時順序節(jié)點(diǎn)結(jié)合和臨時節(jié)點(diǎn)和順序節(jié)點(diǎn)的特點(diǎn):在創(chuàng)建節(jié)點(diǎn)時,Zookeeper根據(jù)創(chuàng)建的時間順序給該節(jié)點(diǎn)名稱進(jìn)行編號;當(dāng)創(chuàng)建節(jié)點(diǎn)的客戶端與Zookeeper斷開連接后,臨時節(jié)點(diǎn)會被刪除。

Zookeeper分布式鎖恰恰應(yīng)用了臨時順序節(jié)點(diǎn)。具體如何實現(xiàn)呢?讓我們來看一看詳細(xì)步驟:

加鎖和解鎖流程

獲取鎖

首先,在Zookeeper當(dāng)中創(chuàng)建一個持久節(jié)點(diǎn)ParentLock。當(dāng)?shù)谝粋€客戶端想要獲得鎖時,需要在ParentLock這個節(jié)點(diǎn)下面創(chuàng)建一個臨時順序節(jié)點(diǎn) Lock1。

之后,Client1查找ParentLock下面所有的臨時順序節(jié)點(diǎn)并排序,判斷自己所創(chuàng)建的節(jié)點(diǎn)Lock1是不是順序最靠前的一個。如果是第一個節(jié)點(diǎn),則成功獲得鎖。

這時候,如果再有一個客戶端 Client2 前來獲取鎖,則在ParentLock下載再創(chuàng)建一個臨時順序節(jié)點(diǎn)Lock2。

Client2查找ParentLock下面所有的臨時順序節(jié)點(diǎn)并排序,判斷自己所創(chuàng)建的節(jié)點(diǎn)Lock2是不是順序最靠前的一個,結(jié)果發(fā)現(xiàn)節(jié)點(diǎn)Lock2并不是最小的。

于是,Client2向排序僅比它靠前的節(jié)點(diǎn)Lock1注冊Watcher,用于監(jiān)聽Lock1節(jié)點(diǎn)是否存在。這意味著Client2搶鎖失敗,進(jìn)入了等待狀態(tài)。

這時候,如果又有一個客戶端Client3前來獲取鎖,則在ParentLock下載再創(chuàng)建一個臨時順序節(jié)點(diǎn)Lock3。

Client3查找ParentLock下面所有的臨時順序節(jié)點(diǎn)并排序,判斷自己所創(chuàng)建的節(jié)點(diǎn)Lock3是不是順序最靠前的一個,結(jié)果同樣發(fā)現(xiàn)節(jié)點(diǎn)Lock3并不是最小的。

于是,Client3向排序僅比它靠前的節(jié)點(diǎn)Lock2注冊Watcher,用于監(jiān)聽Lock2節(jié)點(diǎn)是否存在。這意味著Client3同樣搶鎖失敗,進(jìn)入了等待狀態(tài)。

這樣一來,Client1得到了鎖,Client2監(jiān)聽了Lock1,Client3監(jiān)聽了Lock2。這恰恰形成了一個等待隊列,很像是Java當(dāng)中ReentrantLock(可重入鎖)所依賴的AQS(AbstractQueuedSynchronizer)。

獲得鎖的過程大致就是這樣,那么Zookeeper如何釋放鎖呢?

釋放鎖的過程很簡單,只需要釋放對應(yīng)的子節(jié)點(diǎn)就好。

釋放鎖

釋放鎖分為兩種情況:

1.任務(wù)完成,客戶端顯示釋放

當(dāng)任務(wù)完成時,Client1會顯示調(diào)用刪除節(jié)點(diǎn)Lock1的指令。

2.任務(wù)執(zhí)行過程中,客戶端崩潰

獲得鎖的Client1在任務(wù)執(zhí)行過程中,如果Duang的一聲崩潰,則會斷開與Zookeeper服務(wù)端的鏈接。根據(jù)臨時節(jié)點(diǎn)的特性,相關(guān)聯(lián)的節(jié)點(diǎn)Lock1會隨之自動刪除。

由于Client2一直監(jiān)聽著Lock1的存在狀態(tài),當(dāng)Lock1節(jié)點(diǎn)被刪除,Client2會立刻收到通知。這時候Client2會再次查詢ParentLock下面的所有節(jié)點(diǎn),確認(rèn)自己創(chuàng)建的節(jié)點(diǎn)Lock2是不是目前最小的節(jié)點(diǎn)。如果是最小,則Client2順理成章獲得了鎖。

同理,如果Client2也因為任務(wù)完成或者節(jié)點(diǎn)崩潰而刪除了節(jié)點(diǎn)Lock2,那么Client3就會接到通知。

最終,Client3成功得到了鎖。

使用Zookeeper實現(xiàn)分布式鎖的大致流程就是這樣。

分析

解決不可重入:客戶端加鎖時將主機(jī)和線程信息寫入鎖中,下一次再來加鎖時直接和序列最小的節(jié)點(diǎn)對比,如果相同,則加鎖成功,鎖重入。

鎖釋放時機(jī):由于我們創(chuàng)建的節(jié)點(diǎn)是順序臨時節(jié)點(diǎn),當(dāng)客戶端獲取鎖成功之后突然session會話斷開,ZK會自動刪除這個臨時節(jié)點(diǎn)。

單點(diǎn)問題:ZK是集群部署的,主要一半以上的機(jī)器存活,就可以保證服務(wù)可用性。

利用curator實現(xiàn)

Zookeeper第三方客戶端curator中已經(jīng)實現(xiàn)了基于Zookeeper的分布式鎖。利用curator加鎖和解鎖的代碼如下:

@Autowired
private CuratorFramework curatorFramework;
// 加鎖,支持超時,可重入
public boolean tryLock(long timeout, TimeUnit unit) throws InterruptedException {
    //
    InterProcessMutex interProcessMutex= new InterProcessMutex(curatorFramework, "/ParenLock");
    try {
        return interProcessMutex.acquire(timeout, unit);
    } catch (Exception e) {
        e.printStackTrace();
    }
    return true;
}
// 解鎖
public boolean unlock() {
InterProcessMutex interProcessMutex= new InterProcessMutex(curatorFramework,  "/ParenLock");
    try {
        interProcessMutex.release();
    } catch (Throwable e) {
        log.error(e.getMessage(), e);
    } finally {
        executorService.schedule(new Cleaner(client, path), delayTimeForClean, TimeUnit.MILLISECONDS);
    }
    return true;
}

最常用的鎖:

  • InterProcessMutex:分布式可重入排它鎖
  • InterProcessSemaphoreMutex:分布式排它鎖
  • InterProcessReadWriteLock:分布式讀寫鎖

基于緩存實現(xiàn)分布式鎖,以Redis為例

加鎖

public class RedisTool {
    private static final String LOCK_SUCCESS = "OK";
    private static final String SET_IF_NOT_EXIST = "NX";
    private static final String SET_WITH_EXPIRE_TIME = "PX";
    /**
     * 加鎖
     * @param stringRedisTemplate Redis客戶端
     * @param lockKey 鎖的key
     * @param requestId 競爭者id
     * @param expireTime 鎖超時時間,超時之后鎖自動釋放
     * @return 
     */
    public static boolean getDistributedLock(StringRedisTemplate stringRedisTemplate, String lockKey, String requestId, int expireTime) {
        return stringRedisTemplate.opsForValue().setIfAbsent(lockKey, requestId, 30, TimeUnit.SECONDS);
    }
}

可以看到,我們加鎖就一行代碼:

stringRedisTemplate.opsForValue().setIfAbsent(lockKey, requestId, 30, TimeUnit.SECONDS);

這個setIfAbsent()方法一共五個形參:

  • 第一個為key,我們使用key來當(dāng)鎖,因為key是唯一的。
  • 第二個為value,這里寫的是鎖競爭者的id,在解鎖時,我們需要判斷當(dāng)前解鎖的競爭者id是否為鎖持有者。
  • 第三個為expx,這個參數(shù)我們傳的是PX,意思是我們要給這個key加一個過期時間的設(shè)置,具體時間由第五個參數(shù)決定;
  • 第四個參數(shù)為time,與第四個參數(shù)相呼應(yīng),代表key的過期時間。

總的來說,執(zhí)行上面的setIfAbsent()方法就只會導(dǎo)致兩種結(jié)果:

  • 1.當(dāng)前沒有鎖(key不存在),那么就進(jìn)行加鎖操作,并對鎖設(shè)置一個有效期,同時value表示加鎖的客戶端。
  • 2.已經(jīng)有鎖存在,不做任何操作。上述解鎖請求中,緩存超時機(jī)制保證了即使一個競爭者加鎖之后掛了,也不會產(chǎn)生死鎖問題:超時之后其他競爭者依然可以獲取鎖。通過設(shè)置value為競爭者的id,保證了只有鎖的持有者才能來解鎖,否則任何競爭者都能解鎖,那豈不是亂套了。

解鎖

public class RedisTool {
    private static final Long RELEASE_SUCCESS = 1L;
    /**
     * 釋放分布式鎖
     * @param stringRedisTemplate Redis客戶端
     * @param lockKey 鎖
     * @param requestId 鎖持有者id
     * @return 是否釋放成功
     */
    public static boolean releaseDistributedLock(StringRedisTemplate stringRedisTemplate, String lockKey, String requestId) {
        String script = "if redis.call('get', KEYS[1]) == ARGV[1] then return redis.call('del', KEYS[1]) else return 0 end";
        Long result = stringRedisTemplate.execute(new DefaultRedisScript<Long>(script, Long.class), Collections.singletonList(lockKey), requestId);
        return RELEASE_SUCCESS.equals(result);
    }
}

解鎖的步驟

  • 1、判斷當(dāng)前解鎖的競爭者id是否為鎖的持有者,如果不是直接返回失敗,如果是則進(jìn)入第2步。
  • 2、刪除key,如果刪除成功,返回解鎖成功,否則解鎖失敗。

注意到這里解鎖其實是分為2個步驟,涉及到解鎖操作的一個原子性操作問題。這也是為什么我們解鎖的時候用Lua腳本來實現(xiàn),因為Lua腳本可以保證操作的原子性。那么這里為什么需要保證這兩個步驟的操作是原子操作呢?

設(shè)想:假設(shè)當(dāng)前鎖的持有者是競爭者1,競爭者1來解鎖,成功執(zhí)行第1步,判斷自己就是鎖持有者,這是還未執(zhí)行第2步。這是鎖過期了,然后競爭者2對這個key進(jìn)行了加鎖。加鎖完成后,競爭者1又來執(zhí)行第2步,此時錯誤產(chǎn)生了:競爭者1解鎖了不屬于自己持有的鎖??赡軙腥藛枮槭裁锤偁幷?執(zhí)行完第1步之后突然停止了呢?這個問題其實很好回答,例如競爭者1所在的JVM發(fā)生了GC停頓,導(dǎo)致競爭者1的線程停頓。這樣的情況發(fā)生的概率很低,但是請記住即使只有萬分之一的概率,在線上環(huán)境中完全可能發(fā)生。因此必須保證這兩個步驟的操作是原子操作。

分析

  • 是否可重入:以上實現(xiàn)的鎖是不可重入的,如果需要實現(xiàn)可重入,在SET_IF_NOT_EXIST之后,再判斷key對應(yīng)的value是否為當(dāng)前競爭者id,如果是返回加鎖成功,否則失敗。
  • 鎖釋放時機(jī):加鎖時我們設(shè)置了key的超時,當(dāng)超時后,如果還未解鎖,則自動刪除key達(dá)到解鎖的目的。如果一個競爭者獲取鎖之后掛了,我們的鎖服務(wù)最多也就在超時時間的這段時間之內(nèi)不可用。
  • Redis單點(diǎn)問題:如果需要保證鎖服務(wù)的高可用,可以對Redis做高可用方案:Redis集群+主從切換。目前都有比較成熟的解決方案。

redis分布式鎖,更詳細(xì)的可以參考:分布式鎖(Redisson)原理分析

三種方案比較

方案理解難易程度實現(xiàn)的復(fù)雜度性能可靠性優(yōu)點(diǎn)缺點(diǎn)
基于數(shù)據(jù)庫容易復(fù)雜不可靠  
基于緩存(Redis)一般一般可靠Set和Del指令性能較高1.實現(xiàn)復(fù)雜,需要考慮超時,原子性,誤刪等情形。2.沒有等待鎖的隊列,只能在客戶端自旋來等待,效率低下。(但是現(xiàn)在有Redisson這兩缺點(diǎn)就相當(dāng)于沒有了)
基于Zookeeper簡單一般一般1.有封裝好的框架,容易實現(xiàn)2.有等待鎖的隊列,大大提升搶鎖效率。添加和刪除節(jié)點(diǎn)性能較低

以上為個人經(jīng)驗,希望能給大家一個參考,也希望大家多多支持腳本之家。 

相關(guān)文章

  • redis實現(xiàn)分布式session的解決方案

    redis實現(xiàn)分布式session的解決方案

    session存放在服務(wù)器,關(guān)閉瀏覽器不會失效,本文主要介紹了redis實現(xiàn)分布式session的解決方案,文中通過示例代碼介紹的非常詳細(xì),具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2022-03-03
  • 淺談Redis位圖(Bitmap)及Redis二進(jìn)制中的問題

    淺談Redis位圖(Bitmap)及Redis二進(jìn)制中的問題

    這篇文章主要介紹了Redis位圖(Bitmap)及Redis二進(jìn)制中的問題,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2021-07-07
  • Redis實現(xiàn)登錄注冊的示例代碼

    Redis實現(xiàn)登錄注冊的示例代碼

    本文主要介紹了Redis實現(xiàn)登錄注冊的示例代碼,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2022-06-06
  • Redis實現(xiàn)用戶關(guān)注的項目實踐

    Redis實現(xiàn)用戶關(guān)注的項目實踐

    本文主要介紹了Redis實現(xiàn)用戶關(guān)注的項目實踐,通過使用Redis的set數(shù)據(jù)結(jié)構(gòu)來存儲關(guān)注對象,方便高效地進(jìn)行添加和取消關(guān)注操作,具有一定的參考價值,感興趣的可以了解一下
    2024-02-02
  • 淺談Redis在秒殺場景的作用

    淺談Redis在秒殺場景的作用

    本文主要介紹了淺談Redis在秒殺場景的作用,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2023-01-01
  • Redis的Sentinel解決方案介紹與運(yùn)行機(jī)制

    Redis的Sentinel解決方案介紹與運(yùn)行機(jī)制

    這篇文章主要介紹了Redis的Sentinel解決方案介紹與運(yùn)行機(jī)制, Sentinel 是一款面向分布式服務(wù)架構(gòu)的輕量級流量控制組件,主要以流量為切入點(diǎn),從流量控制、熔斷降級、系統(tǒng)自適應(yīng)保護(hù)等多個維度來保障服務(wù)的穩(wěn)定性,需要的朋友可以參考下
    2023-07-07
  • Redis Cluster集群動態(tài)擴(kuò)容的實現(xiàn)

    Redis Cluster集群動態(tài)擴(kuò)容的實現(xiàn)

    本文主要介紹了Redis Cluster集群動態(tài)擴(kuò)容的實現(xiàn),文中通過示例代碼介紹的非常詳細(xì),需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2021-07-07
  • 在Redis中如何保存時間序列數(shù)據(jù)詳解

    在Redis中如何保存時間序列數(shù)據(jù)詳解

    與發(fā)生時間相關(guān)的一組數(shù)據(jù),就是時間序列數(shù)據(jù),這些數(shù)據(jù)的特點(diǎn)是沒有嚴(yán)格的關(guān)系模型,記錄的信息可以表示成鍵和值的關(guān)系,這篇文章主要給大家介紹了關(guān)于在Redis中如何保存時間序列數(shù)據(jù)的相關(guān)資料,需要的朋友可以參考下
    2021-10-10
  • Redis RDB與AOF持久化方式詳細(xì)講解

    Redis RDB與AOF持久化方式詳細(xì)講解

    Redis是基于內(nèi)存的數(shù)據(jù)結(jié)構(gòu)服務(wù)器,保存了大量的鍵值對數(shù)據(jù),所以持久化到磁盤是非常必要的,Redis提供了兩種持久化的方式,分別是RDB和AOF。下面我們看下這兩種持久化方式的具體實現(xiàn)原理
    2022-11-11
  • redis底層數(shù)據(jù)結(jié)構(gòu)之skiplist實現(xiàn)示例

    redis底層數(shù)據(jù)結(jié)構(gòu)之skiplist實現(xiàn)示例

    這篇文章主要為大家介紹了redis底層數(shù)據(jù)結(jié)構(gòu)之skiplist實現(xiàn)示例,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2023-12-12

最新評論