欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

Redis五種數(shù)據(jù)類型詳解

 更新時間:2023年04月25日 10:12:52   作者:EzreaLwj  
Redis是基于內(nèi)存的 K-V 數(shù)據(jù)庫,常用于緩存、消息隊列,分布式鎖等場景,并且提供了常見的數(shù)據(jù)結(jié)構(gòu):字符串、哈希、列表、集合、帶排序的集合,本文主要介紹了Redis的五種數(shù)據(jù)類型,感興趣的小伙伴可以參考閱讀本文

什么是 Redis

Redis 是基于內(nèi)存的 K-V 數(shù)據(jù)庫,常用于緩存、消息隊列分布式鎖等場景,并且提供了常見的數(shù)據(jù)結(jié)構(gòu):字符串、哈希、列表、集合、帶排序的集合

Redis 數(shù)據(jù)類型詳解

前置知識

Redis中的任意數(shù)據(jù)類型的鍵和值都會被封裝為一個 RedisObject

typedef struct redisObject {
    unsigned type:4;
    unsigned encoding:4;
    unsigned lru:LRU_BITS; /* LRU time (relative to global lru_clock) or
                            * LFU data (least significant 8 bits frequency
                            * and most significant 16 bits access time). */
    int refcount;
    void *ptr;
} robj;

type:指具體的對象類型

encoding:底層編碼方式

lru:記錄最后一次被訪問的時間

refcount:對象引用計數(shù)器,計數(shù)器為 0 時表示沒有被引用,可以回收

ptr:實際存儲的值

五種數(shù)據(jù)結(jié)構(gòu)

Redis中會根據(jù)存儲的數(shù)據(jù)類型不同,選擇不同的編碼方式。每種數(shù)據(jù)類型的使用的編碼方式如下:

數(shù)據(jù)類型編碼方式
OBJ_STRINGint、embstr、raw
OBJ_LISTLinkedList和ZipList(3.2以前)、QuickList(3.2以后)
OBJ_SETintset、HT
OBJ_ZSETZipList、HT、SkipList
OBJ_HASHZipList、HT

Redis 的編碼方式

Redis 中會根據(jù)存儲的數(shù)據(jù)類型不同,選擇不同的編碼方式,共包含 11 種不同類型:

編號編碼方式說明
0OBJ_ENCODING_RAWraw編碼動態(tài)字符串
1OBJ_ENCODING_INTlong類型的整數(shù)的字符串
2OBJ_ENCODING_HThash表(字典dict)
3OBJ_ENCODING_ZIPMAP已廢棄
4OBJ_ENCODING_LINKEDLIST雙端鏈表
5OBJ_ENCODING_ZIPLIST壓縮列表
6OBJ_ENCODING_INTSET整數(shù)集合
7OBJ_ENCODING_SKIPLIST跳表
8OBJ_ENCODING_EMBSTRembstr的動態(tài)字符串
9OBJ_ENCODING_QUICKLIST快速列表
10OBJ_ENCODING_STREAMStream流

String

介紹

字符串 (string) 其實是最簡單的數(shù)據(jù)結(jié)構(gòu),不僅可以存儲字符串,還可以存儲整數(shù)、浮點數(shù)、二進(jìn)制數(shù)據(jù)等;

常用命令

set key value:添加一個 string 類型的鍵值對

get key:獲取 key 對應(yīng)的 value

mset:批量設(shè)置對個鍵值對

mget:批量獲取多個鍵值對

incr:讓一個整形自增 1

setnx:添加一個 string 類型的字符串,前提是這個 key 不存在

使用場景

緩存對象

直接緩存整個 json 字符串對象 SET user:1 '{"name":"ezreal", "age":18}'

緩存更新策略

  • 使用 redis 的內(nèi)存淘汰機(jī)制;
  • 超時剔除,給數(shù)據(jù)添加 TTL 過期時間;
  • 客戶端主動更新緩存;

分布式鎖

使用 setnx 指令可以實現(xiàn)分布式鎖,若設(shè)置值成功則表示獲取鎖成功,設(shè)置失敗則獲取鎖失敗,注意要設(shè)置該 key 的過期時間,防止一直搶占該鎖

常規(guī)計數(shù)

可以通過 incr 指令來統(tǒng)計數(shù)量,前提設(shè)置的 value 需要是整形

共享 session

我們可以把多臺服務(wù)器上的 session 統(tǒng)一放到 redis 中,無論向集群哪臺服務(wù)器發(fā)送消息,都保證去 redis 中獲取 session 的信息

底層實現(xiàn)

redis 的底層實現(xiàn)是 SDS 字符串

SDS 結(jié)構(gòu)體

struct __attribute__ ((__packed__)) sdshdr8 {
    uint8_t len; /* buf已保存的字符串字節(jié)數(shù),不包含結(jié)束標(biāo)示 */
    uint8_t alloc; /* buf申請的總的字節(jié)數(shù),不包含結(jié)束標(biāo)示 */
    unsigned char flags; /* 不同的SDS頭類型 */
    char buf[];
};

len:該字符串的 長度

alloc:buf 申請的 總字節(jié)數(shù)

buf:實際存儲數(shù)據(jù)的位置

flag:SDS 頭類型,用來控制SDS頭大小 :SDS_TYPE_5,SDS_TYPE_8SDS_TYPE_16,SDS_TYPE_32,SDS_TYPE_64

整形的存儲

如果存儲的是一個整形,則則直接存儲在 RedisObject 中即可;

SDS 動態(tài)擴(kuò)容

假如現(xiàn)在在 name 字符串后加入 ezreal,形成新的字符串 'nameezreal'

  • 新字符串的長度小于 1M,則新的內(nèi)存空間為 新字符串 的長度的兩倍 + 1
  • 新字符串的長度大于 1M,則新的內(nèi)存空間為 新字符串 的長度 + 1M + 1,稱為內(nèi)存預(yù)分配;

List

介紹

列表是一個有序的字符串集合,可以通過下標(biāo)索引來訪問指定位置的字符串,還可以在列表頭部或者尾部來添加或刪除元素

常用命令

LPUSH KEY:向列表左側(cè)插入元素

RPUSH KEY:向列表右側(cè)插入元素

LPOP KEY:向列表左側(cè)彈出元素

RPOP KEY:向列表右側(cè)彈出元素

LRANGE KEY star end:獲取指定范圍內(nèi)的 key

BLPOP / BRPOP:與 LPOP / RPOP 類似,只不過在沒有元素時會等待

使用場景

消息隊列

消息隊列實現(xiàn)的三大要素:消息保序,處理重復(fù)的消息保證消息的可靠性

  • 消息保序

我們可以基于 LPUSH / RPOP 的方法來實現(xiàn)一個隊列,消息先進(jìn)先出,保證消息的順序;

當(dāng)有一個缺點,消費者只能不斷的輪詢該隊列是否有消息,因為消費者新增一條消息后是不能通知消費者的,所以消費者要不斷輪詢 while(1),不斷的占用 CPU。我們可以使用 BRPOP 來優(yōu)化,消費者沒有獲取到消息時,會自動阻塞,直到生產(chǎn)者放入新的消息。

  • 處理重復(fù)的消息

我們可以為每個消息生成一個 唯一 的消息 ID,每次消費消息時,從數(shù)據(jù)庫中判斷該消息是否已經(jīng)被處理了,防止消息的重復(fù)消息。

  • 保證消息可靠性

當(dāng)消息被消費者獲取后,沒有處理完消息,機(jī)器就宕機(jī)了,這樣不僅消息沒有完全被消費,List 中的消息也丟失了,從而導(dǎo)致這個任務(wù)永遠(yuǎn)丟失了;

我們可以使用 Redis 中的BRPOPLPUSH命令,作用是把該消息從 List 中拿出來,同時還會把該消息放到另一個 List 中作留存。

底層實現(xiàn)

在 Redis 3.2 之前,List 的底層實現(xiàn)是壓縮列表 ( ZipList) 或 雙向鏈表

在 Redis 3.2 之后,List 的底層實現(xiàn)是 QuickList

ZipList

ZipList 又叫壓縮列表,類似 "雙向鏈表",但其不是鏈表。它由一段連續(xù)的內(nèi)存塊組成,可以在列表頭部或者尾部添加或者刪除元素。

zlbytes:整個 ZipList 的 內(nèi)存字節(jié)數(shù),占用 4 字節(jié)

zltail:從 ZipList 的起始地址到最后一個 entry 的內(nèi)存字節(jié)數(shù),占用 4 字節(jié)

zllen:ZipList 中 entry 的個數(shù),占用 2 字節(jié)

zlend:結(jié)束標(biāo)識 0xff,占用 1 字節(jié)

ZipEntry結(jié)構(gòu)

previous_entry_length:上一個 entry 的長度,占用 1個 或 5個字節(jié)

  • 若上一個 entry 的長度小于 256 字節(jié),則 encoding 使用 1 個字節(jié)來存儲
  • 若上一個 entry 的長度大于 256 字節(jié),則 encoding 使用 5 個字節(jié)來存儲

encoding:記錄 content 的類型 [ 整形 or 字符串 ] 和長度,占用 1個,2個 或者 5個字節(jié)

content:實際的內(nèi)容

Encoding 編碼

Entry 中的 encoding 編碼分為字符串和整數(shù)兩種:
字符串:如果 encoding 是以“00”“01”或者“10”開頭,則證明 content 是字符串

整數(shù):如果encoding是以“11”開始,則證明content是整數(shù),且encoding固定只占用1個字節(jié)

編碼編碼長度整數(shù)類型
110000001int16_t(2 bytes)
110100001int32_t(4 bytes)
111000001int64_t(8 bytes)
11110000124位有符整數(shù)(3 bytes)
1111111018位有符整數(shù)(1 bytes)
1111xxxx1直接在xxxx位置保存數(shù)值,范圍從0001~1101,減1后結(jié)果為實際值

可見,ZipList 通過記錄上一個entry 的長度 和 自身entry 的長度(length + encoding + content),從而可以快速得到上一個 entry 和下一個 entry ,從而實現(xiàn)從頭到尾 或 從尾到頭的遍歷。

QuickList

QuickList 是一個雙向鏈表,它的每一個節(jié)點都是 ZipList

為了避免 QuickList 中的每個 ZipList 中 entry 過多,Redis 提供了一個配置項:list-max-ziplist-size來限制。

如果值為,則代表 ZipList 的允許的 entry 個數(shù)的最大值;

如果值為負(fù),則代表ZipList的最大內(nèi)存大小,分5種情況:

  • -1:每個 ZipList 的內(nèi)存占用不能超過 4kb
  • -2:每個 ZipList 的內(nèi)存占用不能超過 8kb
  • -3:每個 ZipList 的內(nèi)存占用不能超過 16kb
  • -4:每個 ZipList 的內(nèi)存占用不能超過 32kb
  • -5:每個 ZipList 的內(nèi)存占用不能超過 64kb

其默認(rèn)值為 -2:

以下是 QuickList 的和 QuickListNode 的結(jié)構(gòu)源碼:

QuickList

typedef struct quicklist {
    quicklistNode *head;		// 頭結(jié)點指針
    quicklistNode *tail;		// 尾結(jié)點指針
    unsigned long count;    	/* total count of all entries in all ziplists */
    unsigned long len;          /* number of quicklistNodes */
    int fill : QL_FILL_BITS;              /* fill factor for individual nodes */
    unsigned int compress : QL_COMP_BITS; /* depth of end nodes not to compress;0=off */
    unsigned int bookmark_count: QL_BM_BITS;
    quicklistBookmark bookmarks[];
} quicklist;

QuickListNode

typedef struct quicklistNode {
    struct quicklistNode *prev; // 前一個指針
    struct quicklistNode *next; // 后一個指針
    unsigned char *zl;  		// 當(dāng)前結(jié)點的ziplist指針
    unsigned int sz;             /* ziplist size in bytes */
    unsigned int count : 16;     /* count of items in ziplist */
    unsigned int encoding : 2;   /* RAW==1 or LZF==2 */
    unsigned int container : 2;  /* NONE==1 or ZIPLIST==2 */
    unsigned int recompress : 1; /* was this node previous compressed? */
    unsigned int attempted_compress : 1; /* node can't compress; too small */
    unsigned int extra : 10; /* more bits to steal for future usage */
} quicklistNode;

整體結(jié)構(gòu)

Hash

介紹

Hash 是一個鍵值對的集合,形如:key:{field1: value1,field1: value1},非常適合存儲一個對象

常用命令

# 存儲一個哈希表key的鍵值
HSET key field value   
# 獲取哈希表key對應(yīng)的field鍵值
HGET key field

# 在一個哈希表key中存儲多個鍵值對
HMSET key field value [field value...] 
# 批量獲取哈希表key中多個field鍵值
HMGET key field [field ...]       
# 刪除哈希表key中的field鍵值
HDEL key field [field ...]    

# 返回哈希表key中field的數(shù)量
HLEN key       
# 返回哈希表key中所有的鍵值
HGETALL key 

# 為哈希表key中field鍵的值加上增量n
HINCRBY key field n

使用場景

緩存對象

例如下圖,對于使用 string + json 還是用 hash 來存儲,建議優(yōu)先選擇 string + json ,對于某些字段經(jīng)常改變的話,可以考慮使用 hash

購物車

以用戶 id 為 key,商品 id 為 field,商品數(shù)量為 value,恰好構(gòu)成了購物車的 3 個要素,如下圖所示。

涉及的命令如下:

  • 添加商品:HSET cart:{用戶id} {商品id} 1
  • 添加數(shù)量:HINCRBY cart:{用戶id} {商品id} 1
  • 商品總數(shù):HLEN cart:{用戶id}
  • 刪除商品:HDEL cart:{用戶id} {商品id}
  • 獲取購物車所有商品:HGETALL cart:{用戶id}

當(dāng)前僅僅是將商品 ID 存儲到了 Redis 中,在回顯商品具體信息的時候,還需要拿著商品 id 查詢一次數(shù)據(jù)庫,獲取完整的商品的信息。

底層實現(xiàn)

Hash 的底層實現(xiàn)可以是 ZipList 或者 Dict

  • Hash結(jié)構(gòu)默認(rèn)采用 ZipList 編碼,用以節(jié)省內(nèi)存。 ZipList 中相鄰的兩個 entry 分別保存 fieldvalue
  • 當(dāng) ZipList 的元素個數(shù)小于 512(默認(rèn))且每個 Entry 的大小小于 64 字節(jié)(默認(rèn)),則會使用 ZipList 來作為 Hash 的底層實現(xiàn),否則則使用 Dict

Dict

Dict 中維護(hù)一個 哈希表 DitHashTable,里面有一個 DictEntry 數(shù)組,用來存儲實際的 key-value,每個槽位下的 DictEntry 都會形成一個鏈表,處理哈希沖突

向 Dict 插入一個元素時,先會根據(jù) key 計算出一個哈希值 h,然后計算 h & sizemask 得到數(shù)組下標(biāo),最后加入該 DictEntry 即可

數(shù)據(jù)結(jié)構(gòu)體

字典(Dict)

typedef struct dict {
    dictType *type; 		// dict 類型,不同的哈希函數(shù)
    void *privdata;  		// 私有數(shù)據(jù),做特殊hash的時候用
    dictht ht[2]; 			// 包含兩個hash表,一個是當(dāng)前數(shù)據(jù),另個為空,rehash的時候用
    long rehashidx; 		// rehash的進(jìn)度
    int16_t pauserehash;	// rehash是否暫停 
} dict;

哈希表(DictHashTable)

typedef struct dictht {
    dictEntry **table;   		// 指向entry 的數(shù)組
    unsigned long size;  		// 哈希表大小
    unsigned long sizemask;   	// 掩碼:size - 1
    unsigned long used;  		// entry 的個數(shù)
} dictht;

哈希節(jié)點(DictEntry)

typedef struct dictEntry {
    void *key;  // 鍵
    union {
        void *val;
        uint64_t u64;
        int64_t s64;
        double d;
    } v;// 值
    struct dictEntry *next;// 下一個結(jié)點
} dictEntry;

Dict 的 rehash

不管是擴(kuò)容還是收縮,必定會創(chuàng)建新的哈希表,導(dǎo)致哈希表的 size 和 sizemask 變化,而 key 的查詢與 sizemask 有關(guān)。因此必須對哈希表中的每一個 key 重新計算索引,插入新的哈希表,這個過程稱為 rehash。過程是這樣的:

  • 計算新hash表的realeSize,值取決于當(dāng)前要做的是擴(kuò)容還是收縮:
    • 如果是擴(kuò)容,則新size為第一個大于等于dict.ht[0].used + 1 的2^n
    • 如果是收縮,則新size為第一個大于等于dict.ht[0].used 的2^n (不得小于4)
  • 按照新的 realeSize 申請內(nèi)存空間,創(chuàng)建 dictht,并賦值給 dict.ht[1]
  • 設(shè)置 dict.rehashidx = 0,標(biāo)示開始 rehash
  • 將 dict.ht[0] 中的每一個 dictEntry 都 rehash 到 dict.ht[1]
  • 將 dict.ht[1] 賦值給 dict.ht[0],給 dict.ht[1] 初始化為空哈希表,釋放原來的dict.ht[0]的內(nèi)存
  • 將 rehashidx 賦值為 -1,代表 rehash 結(jié)束
  • 在rehash過程中,新增操作,則直接寫入ht[1] ,查詢、修改和刪除則會在dict.ht[0]和dict.ht[1]依次查找并執(zhí)行。這樣可以確保 ht[0] 的數(shù)據(jù)只減不增,隨著 rehash 最終為空

Set

介紹

Set 是 Redis 中的無序集合,可以保證元素的唯一性,但不一定保證元素的有序性,可以對 set 取交集,并集等操作

常用命令

Set 常用操作

# 往集合key中存入元素,元素存在則忽略,若key不存在則新建
SADD key member [member ...]
# 從集合key中刪除元素
SREM key member [member ...] 
# 獲取集合key中所有元素
SMEMBERS key
# 獲取集合key中的元素個數(shù)
SCARD key

# 判斷member元素是否存在于集合key中
SISMEMBER key member

# 從集合key中隨機(jī)選出count個元素,元素不從key中刪除
SRANDMEMBER key [count]
# 從集合key中隨機(jī)選出count個元素,元素從key中刪除
SPOP key [count]

Set 集合操作

# 交集運算
SINTER key [key ...]
# 將交集結(jié)果存入新集合destination中
SINTERSTORE destination key [key ...]

# 并集運算
SUNION key [key ...]
# 將并集結(jié)果存入新集合destination中
SUNIONSTORE destination key [key ...]

# 差集運算
SDIFF key [key ...]
# 將差集結(jié)果存入新集合destination中
SDIFFSTORE destination key [key ...]

使用場景

點贊

使用 set 集合來存儲,key 為文章 id,value為 用戶 id 即可,獲取該文章的點贊數(shù)量取該 set 中 id 的總數(shù)即可

關(guān)注

使用 set 集合來存儲,key 為用戶 id,value 為該用戶關(guān)注的用戶的 id,可以對兩個 set 取并集,就可以找出它們的共同關(guān)注

抽獎

使用 set 集合來存儲,key 為活動 id,value 為參見該活動的用戶

  • 若不可重復(fù)抽獎,則使用 SPOP key [count] 來隨機(jī)獲取 count 個用戶,并從 set 中移除
  • 若可以重復(fù)抽獎,則使用 SRANDMEMBER key [count] 來隨機(jī)獲取 count 個用戶,但不會從 set 中移除

底層實現(xiàn)

Set 底層是通過 IntSet(整數(shù)集合)或者 Dict 來實現(xiàn)的

  • 用 Dict 來存儲實際上和 Hash 的結(jié)構(gòu)一樣,只不過把 value 值統(tǒng)一存儲為 null 即可
  • 當(dāng)數(shù)據(jù)都是整數(shù)時,且數(shù)據(jù)的數(shù)量不超過 set-max-intset-entries 時,會使用 intset 來存儲數(shù)據(jù)
  • 否則則使用 Dict 來存儲

Intset

Intset 是 Redis 中的一種實現(xiàn)模式,基于整數(shù)數(shù)組來實現(xiàn),具備長度可變,有序的特征

結(jié)構(gòu)體

typedef struct intset {
    uint32_t encoding; 	// 編碼方式
    uint32_t length; 	// 長度
    int8_t contents[]; 	// 存儲位置
} intset;

encoding:表示存儲整數(shù)的大小

/* Note that these encodings are ordered, so:
 * INTSET_ENC_INT16 < INTSET_ENC_INT32 < INTSET_ENC_INT64. */
#define INTSET_ENC_INT16 (sizeof(int16_t))
#define INTSET_ENC_INT32 (sizeof(int32_t))
#define INTSET_ENC_INT64 (sizeof(int64_t))

length:數(shù)組的長度

contents:存儲實際的內(nèi)容

encoding:4字節(jié)

length:4字節(jié)

contents:2字節(jié) * 3 = 6 字節(jié)

擴(kuò)容過程

我們向該其中添加一個數(shù)字:50000,這個數(shù)字超出了 int16_t 的范圍,intset 會自動升級編碼方式到合適的大小。
以當(dāng)前案例來說流程如下:

  • 升級編碼為 INTSET_ENC_INT32 , 每個整數(shù)占 4 字節(jié),并按照新的編碼方式及元素個數(shù)擴(kuò)容數(shù)組
  • 倒序依次將數(shù)組中的元素拷貝到擴(kuò)容后的正確位置
  • 將待添加的元素放入數(shù)組末尾
  • 最后,將 inset 的 encoding 屬性改為 INTSET_ENC_INT32,將 length 屬性改為 4

小結(jié):

  • intset 可以保證 contents 的整數(shù)有序,唯一
  • 因為 contents 數(shù)組有序,所以可以使用 二分查找 來快速找到目標(biāo)值
  • 具備類型升級機(jī)制,可以節(jié)省內(nèi)存空間

ZSet

介紹

ZSet 是一個有序集合類型,比 Set 多出了一個 score 字段,該字段用來對 value 值進(jìn)行排序,保證 ZSet 中的值是有序的

所以每一個元素都有兩個值,一個 score,用來排序,一個 value 存儲實際的值

常用命令

ZSet 常用操作

# 往有序集合key中加入帶分值元素
ZADD key score member [[score member]...]   
# 往有序集合key中刪除元素
ZREM key member [member...]                 
# 返回有序集合key中元素member的分值
ZSCORE key member
# 返回有序集合key中元素個數(shù)
ZCARD key 

# 為有序集合key中元素member的分值加上increment
ZINCRBY key increment member 

# 正序獲取有序集合key從start下標(biāo)到stop下標(biāo)的元素
ZRANGE key start stop [WITHSCORES]
# 倒序獲取有序集合key從start下標(biāo)到stop下標(biāo)的元素
ZREVRANGE key start stop [WITHSCORES]

# 返回有序集合中指定分?jǐn)?shù)區(qū)間內(nèi)的成員,分?jǐn)?shù)由低到高排序。
ZRANGEBYSCORE key min max [WITHSCORES] [LIMIT offset count]

# 返回指定成員區(qū)間內(nèi)的成員,按字典正序排列, 分?jǐn)?shù)必須相同。
ZRANGEBYLEX key min max [LIMIT offset count]
# 返回指定成員區(qū)間內(nèi)的成員,按字典倒序排列, 分?jǐn)?shù)必須相同
ZREVRANGEBYLEX key max min [LIMIT offset count]

ZSet 運算操作

# 并集計算(相同元素分值相加),numberkeys一共多少個key,WEIGHTS每個key對應(yīng)的分值乘積
ZUNIONSTORE destkey numberkeys key [key...] 
# 交集計算(相同元素分值相加),numberkeys一共多少個key,WEIGHTS每個key對應(yīng)的分值乘積
ZINTERSTORE destkey numberkeys key [key...]

使用場景

排行榜

以文章排行榜為例子,使用 article:rank 作為 key,點贊數(shù)量作為 score,文章的 id 作為 value 即可,根據(jù) score 從大到小進(jìn)行排序,則可以得到一個 文章熱度的排行榜。

底層實現(xiàn)

ZSet 使用 壓縮列表 或者 跳表 + 字典來實現(xiàn)

  • 可以根據(jù) key 找到 value;
  • 保證 value 是唯一的;
  • 可以根據(jù) value 查找分?jǐn)?shù);

Ziplist 本身是沒有排序功能的,而且沒有鍵值對的概念,需要通過編碼來實現(xiàn):

  • ziplist連續(xù)內(nèi)存,scorevalue 是相互依靠的兩個 entry;
  • ziplist 本身不能排序,所以只能通過 score 來對 entry 從小到大排序;

當(dāng)同時滿足一下條件時,會選擇使用 Ziplist

  • 元素的數(shù)量小于 zset_max_ziplist_entries ,默認(rèn)是 256
  • 每個元素的內(nèi)存大小小于 zset_max_ziplist_value 字節(jié),默認(rèn)是 64 KB

否則使用 跳表 +Dict

使用 Dict 存儲 valuescore 的映射關(guān)系;

使用 跳表 來有序的存儲 score 和 value 值,便于范圍查找

SkipList

跳表是一個并聯(lián)多個有序鏈表的數(shù)據(jù)結(jié)構(gòu),它可以在 O(logn) 的時間復(fù)雜度內(nèi)完成插入、刪除、增加的操作;

特點

  • 跳表是一個雙向鏈表,每個節(jié)點包含 scoreelement
  • 節(jié)點按 score 進(jìn)行排序
  • 每個節(jié)點都可以包含多層指針,層數(shù)是 1 到 32 之間的隨機(jī)數(shù)
  • 不同層指針到下一個節(jié)點的跨度不同,層級越高,跨度越大
  • 增刪改查效率與紅黑樹基本一致,實現(xiàn)卻更簡單

跳表代碼

public class SkipList {

    private final int MAX_LEVEL = 32;

    private final double factor = 0.25;

    private int level = 1;

    /**
     * 頭結(jié)點
     */
    private final Node head = new Node(null, MAX_LEVEL);

    static class Node {
        Integer value;
        Node[] next;
        int size;

        public Node(Integer value, int size) {
            this.value = value;
            this.size = size;
            this.next = new Node[size];
        }
    }

    public boolean search(int value) {

        Node p = head;
        for (int i = level - 1; i >= 0; i--) {
            while (p.next[i] != null && p.next[i].value <= value) {
                if (p.next[i].value == value) {
                    return true;
                }
                p = p.next[i];
            }
        }
        return false;
    }

    public void insert(int value) {
        int randomLevel = randomLevel();
        Node newNode = new Node(value, randomLevel);

        Node p = head;
        for (int i = level - 1; i >= 0; i--) {
            while (p.next[i] != null && p.next[i].value <= value) {
                p = p.next[i];
            }

            if (p.next[i] == null) {
                p.next[i] = newNode;
            } else {
                newNode.next[i] = p.next[i];
                p.next[i] = newNode;
            }
        }

        if (randomLevel > level) {
            for (int i = level ; i < randomLevel ; i++) {
                head.next[i] = newNode;
            }
            level = randomLevel;
        }
    }

    public boolean delete(int value) {
        boolean contain = search(value);
        Node p = head;
        Node h = null;
        if (contain) {
            for (int i = level - 1; i >= 0; i--) {
                while (p.next[i] != null && p.next[i].value <= value) {
                    h = p;
                    p = p.next[i];
                }
                h.next[i] = p.next[i];
            }
        }

        return contain;
    }

    private int randomLevel() {
        int level = 1;

        Random random = new Random();
        while (random.nextDouble() <= factor && level <= MAX_LEVEL) {
            level++;
        }
        return level;
    }
}

以上就是Redis五種數(shù)據(jù)類型詳解的詳細(xì)內(nèi)容,更多關(guān)于Redis數(shù)據(jù)類型的資料請關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

  • 基于redis+lua進(jìn)行限流的方法

    基于redis+lua進(jìn)行限流的方法

    這篇文章主要介紹了基于redis+lua進(jìn)行限流,通過實例代碼詳細(xì)介紹了lua+redis進(jìn)行限流的做法,開發(fā)環(huán)境使用idea+redis+lua,本文給大家介紹的非常詳細(xì),需要的朋友可以參考下
    2022-07-07
  • redis批量刪除key的步驟

    redis批量刪除key的步驟

    本文分享最新版Redis批量刪除key的方法,希望能幫到遇到同樣問題的網(wǎng)友。
    2020-09-09
  • Redis List列表的詳細(xì)介紹

    Redis List列表的詳細(xì)介紹

    這篇文章主要介紹了Redis List列表的詳細(xì)介紹的相關(guān)資料,Redis列表是簡單的字符串列表,按照插入順序排序,需要的朋友可以參考下
    2017-08-08
  • 關(guān)于redis的延遲雙刪策略總結(jié)

    關(guān)于redis的延遲雙刪策略總結(jié)

    這篇文章主要介紹了關(guān)于redis的延遲雙刪策略總結(jié),具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2022-08-08
  • redis中redis-cli使用小結(jié)

    redis中redis-cli使用小結(jié)

    redis-cli 是Redis命令行界面,一個簡單的程序,允許直接從終端向Redis發(fā)送命令,并讀取服務(wù)器發(fā)送的回復(fù),本文主要介紹了redis中redis-cli使用小結(jié),感興趣的可以了解一下
    2023-10-10
  • Ubuntu下安裝redis的2種方法分享

    Ubuntu下安裝redis的2種方法分享

    redis是一個開源的、使用C語言編寫的、支持網(wǎng)絡(luò)交互的、可基于內(nèi)存也可持久化的Key-Value數(shù)據(jù)庫。這篇文章對redis就不進(jìn)行詳細(xì)的介紹了,這篇文章主要給大家介紹了Ubuntu下安裝redis的兩種方法,有需要的朋友們可以參考學(xué)習(xí),下面來一起看看吧。
    2016-12-12
  • Redis中有序集合的內(nèi)部實現(xiàn)方式的詳細(xì)介紹

    Redis中有序集合的內(nèi)部實現(xiàn)方式的詳細(xì)介紹

    本文主要介紹了Redis中有序集合的內(nèi)部實現(xiàn)方式的詳細(xì)介紹,文中通過示例代碼介紹的非常詳細(xì),具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2022-03-03
  • redis使用Lua腳本解決多線程下的超賣問題及原因解析

    redis使用Lua腳本解決多線程下的超賣問題及原因解析

    這篇文章主要介紹了redis使用Lua腳本解決多線程下的超賣問題,本文給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友可以參考下
    2023-05-05
  • Django使用redis配置緩存的方法

    Django使用redis配置緩存的方法

    Redis是一個內(nèi)存數(shù)據(jù)庫由于其性能極高,因此經(jīng)常作為中間件、緩存使用,緩存某些內(nèi)容是為了保存昂貴計算的結(jié)果,這樣就不必在下次執(zhí)行計算,接下來通過本文給大家分享redis配置緩存的方法,感興趣的朋友一起看看吧
    2021-06-06
  • Redis?Lua腳本實現(xiàn)ip限流示例

    Redis?Lua腳本實現(xiàn)ip限流示例

    這篇文章主要介紹了Redis?Lua腳本實現(xiàn)ip限流示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2022-07-07

最新評論