面試官:有一種數(shù)據(jù)類(lèi)型,Redis 要存兩次,為什么?
閱讀本文大概需要 9 分鐘。
來(lái)自:blog.csdn.net/zwx900102/article/details/113096979
前言
五種基本類(lèi)型之集合對(duì)象

intset 編碼
int16_t,int32_t,int64_t?的整數(shù)值,并且保證集合中沒(méi)有重復(fù)元素。inset.h?內(nèi)):typedef?struct?intset?{
????uint32_t?encoding;//編碼方式
????uint32_t?length;//當(dāng)前集合中的元素?cái)?shù)量
????int8_t?contents[];//集合中具體的元素
}?intset;

encoding
INTSET_ENC_INT16
contents[]?內(nèi)的每個(gè)元素都是一個(gè) int16_t 類(lèi)型的整數(shù)值,范圍是:-32768 ~ 32767(-2 的 15 次方 ~ 2 的 15 次方 - 1)。INTSET_ENC_INT32
contents[]?內(nèi)的每個(gè)元素都是一個(gè) int32_t 類(lèi)型的整數(shù)值,范圍是:-2147483648 ~ 2147483647(-2 的 31 次方 ~ 2 的 31 次方 - 1)。INTSET_ENC_INT64
contents[]?內(nèi)的每個(gè)元素都是一個(gè) int64_t 類(lèi)型的整數(shù)值,范圍是:-9223372036854775808 ~ 9223372036854775807(-2 的 63 次方 ~ 2 的 63 次方 - 1)。contents[]
contents[]?雖然結(jié)構(gòu)的定義上寫(xiě)的是 int8_t 類(lèi)型,但是實(shí)際存儲(chǔ)類(lèi)型是由上面的 encoding 來(lái)決定的。最新 Redis 面試題整理好了,點(diǎn)擊Java面試庫(kù)小程序在線刷題。整數(shù)集合的升級(jí)
根據(jù)新添加元素的類(lèi)型來(lái)擴(kuò)展底層數(shù)組空間的大小,按照升級(jí)后現(xiàn)有元素的位數(shù)來(lái)分配新的空間。 將現(xiàn)有的元素進(jìn)行類(lèi)型轉(zhuǎn)換,并將轉(zhuǎn)換類(lèi)型后的元素從后到前逐個(gè)重新放回到數(shù)組內(nèi)。 將新元素放到數(shù)組的頭部或者尾部(因?yàn)橛|發(fā)升級(jí)的條件就是當(dāng)前數(shù)組的整數(shù)類(lèi)型無(wú)法存儲(chǔ)新元素,所以新元素要么比現(xiàn)有元素都大,要么就比現(xiàn)有元素都?。?。 將 encoding 屬性修改為最新的編碼,并且同步修改 length 屬性。
PS:和字符串對(duì)象的編碼一樣,整數(shù)集合的類(lèi)型一旦發(fā)生升級(jí),將會(huì)保持編碼,無(wú)法降級(jí)。
升級(jí)示例
int16_t,內(nèi)部存儲(chǔ)了 3 個(gè)元素:
int32_t?類(lèi)型整數(shù),所以需要申請(qǐng)新空間,申請(qǐng)空間大小為?4 * 32 - 48=80。




hashtable 編碼
intset 和 hashtable 編碼轉(zhuǎn)換
集合對(duì)象保存的所有元素都是整數(shù)值。 集合對(duì)象保存的元素?cái)?shù)量小于等于 512 個(gè)(這個(gè)閾值可以通過(guò)配置文件? set-max-intset-entries?來(lái)控制)。
集合對(duì)象常用命令
sadd key member1 member2:將一個(gè)或多個(gè)元素 member 加入到集合 key 當(dāng)中,并返回添加成功的數(shù)目,如果元素已存在則被忽略。sismember key member:判斷元素 member 是否存在集合 key 中。srem key member1 member2:移除集合 key 中的元素,不存在的元素會(huì)被忽略。smove source dest member:將元素 member 從集合 source 中移動(dòng)到 dest 中,如果 member 不存在,則不執(zhí)行任何操作。smembers key:返回集合 key 中所有元素。
sadd?num?1?2?3??//設(shè)置?3?個(gè)整數(shù)的集合,會(huì)使用?intset?編碼
type?num?//查看類(lèi)型
object?encoding?num???//查看編碼
sadd?name?1?2?3?test??//設(shè)置?3?個(gè)整數(shù)和?1?個(gè)字符串的集合,會(huì)使用?hashtable?編碼
type?name?//查看類(lèi)型
object?encoding?name?//查看編碼

五種基本類(lèi)型之有序集合對(duì)象

skiplist 編碼
跳躍表
O(n)。

第 1 種就是執(zhí)行 level1 層級(jí)的指針,需要遍歷 7 次( 1->8->9->12->15->20->35)才能找到元素 35。第 2 種就是執(zhí)行 level2 層級(jí)的指針,只需要遍歷 5 次( 1->9->12->15->35)就能找到元素 35。第 3 種就是執(zhí)行 level3 層級(jí)的元素,這時(shí)候只需要遍歷 3 次( 1->12->35)就能找到元素 35 了,大大提升了效率。
skiplist 的存儲(chǔ)結(jié)構(gòu)
zskiplistNode?節(jié)點(diǎn)(源碼?server.h?內(nèi)):typedef?struct?zskiplistNode?{
????sds?ele;//元素
????double?score;//分值
????struct?zskiplistNode?*backward;//后退指針
????struct?zskiplistLevel?{//層
????????struct?zskiplistNode?*forward;//前進(jìn)指針
????????unsigned?long?span;//當(dāng)前節(jié)點(diǎn)到下一個(gè)節(jié)點(diǎn)的跨度(跨越的節(jié)點(diǎn)數(shù))
????}?level[];
}?zskiplistNode;
level(層)
forward(前進(jìn)指針)
span(跨度)
backward(后退指針)
ele(元素)
score(分值)
typedef?struct?zskiplist?{
????struct?zskiplistNode?*header,?*tail;//跳躍表的頭節(jié)點(diǎn)和尾結(jié)點(diǎn)指針
????unsigned?long?length;//跳躍表的節(jié)點(diǎn)數(shù)
????int?level;//所有節(jié)點(diǎn)中最大的層數(shù)
}?zskiplist;
typedef?struct?zset?{
????dict?*dict;//字典對(duì)象
????zskiplist?*zsl;//跳躍表對(duì)象
}?zset;

為什么同時(shí)選擇使用字典和跳躍表
ziplist 編碼
https://blog.csdn.net/zwx900102/article/details/112651435
ziplist 和 skiplist 編碼轉(zhuǎn)換
有序集合對(duì)象中保存的元素個(gè)數(shù)小于 128 個(gè)(可以通過(guò)配置? zset-max-ziplist-entries?修改)。有序集合對(duì)象中保存的所有元素的總長(zhǎng)度小于 64 字節(jié)(可以通過(guò)配置? zset-max-ziplist-value?修改)。
有序集合對(duì)象常用命令
zadd key score1 member1 score2 member2:將一個(gè)或多個(gè)元素(member)及其 score 添加到有序集合 key 中。zscore key member:返回有序集合 key 中 member 成員的 score。zincrby key num member:將有序集合 key 中的 member 加上 num,num 可以為負(fù)數(shù)。zcount key min max:返回有序集合 key 中 score 值在 [min,max] 區(qū)間的 member 數(shù)量。zrange key start stop:返回有序集合 key 中 score 從小到大排列后在 [start,stop] 區(qū)間的所有 member。zrevrange key start stop:返回有序集合 key 中 score 從大到小排列后在 [start,stop] 區(qū)間的所有 member。zrangebyscore key min max:返回有序集合中按 score 從小到大排列后在 [min,max] 區(qū)間的所有元素。注意這里默認(rèn)是閉區(qū)間,但是可以在 max 和 min 的數(shù)值前面加上?(?或者?[?來(lái)控制開(kāi)閉區(qū)間。zrevrangebyscore key max min:返回有序集合中按 score 從大到小排列后在 [min,max] 區(qū)間的所有元素。注意這里默認(rèn)是閉區(qū)間,但是可以在 max 和 min 的數(shù)值前面加上?(?或者?[?來(lái)控制開(kāi)閉區(qū)間。zrank key member:返回有序集合中 member 中元素排名(從小到大),返回的結(jié)果從 0 開(kāi)始計(jì)算。zrevrank key member:返回有序集合中 member 中元素排名(從大到小),返回的結(jié)果從 0 開(kāi)始計(jì)算。zlexcount key min max:返回有序集合中 min 和 max 之間的 member 數(shù)量。注意這個(gè)命令中的 min 和 max 前面必須加?(?或者?[?來(lái)控制開(kāi)閉區(qū)間,特殊值 - 和 + 分別表示負(fù)無(wú)窮和正無(wú)窮。
zset-max-ziplist-entries?修改為 2,然后重啟 Redis 服務(wù)。zadd?name?1?zs?2?lisi?//設(shè)置?2?個(gè)元素會(huì)使用?ziplist
type?name?//查看類(lèi)型
object?encoding?name?//查看編碼
zadd?address?1?beijing?2?shanghai?3?guangzhou?4?shenzhen??//設(shè)置4個(gè)元素則會(huì)使用?skiplist編碼
type?address??//查看類(lèi)型
object?encoding?address?//查看編碼

總結(jié)
推薦閱讀:
程序員小姐姐寫(xiě)出代碼版《本草綱目》毽子操,劉畊宏回復(fù):很cool!
從 7 分鐘到 10 秒,Mybatis 批處理真的很強(qiáng)!
內(nèi)容包含Java基礎(chǔ)、JavaWeb、MySQL性能優(yōu)化、JVM、鎖、百萬(wàn)并發(fā)、消息隊(duì)列、高性能緩存、反射、Spring全家桶原理、微服務(wù)、Zookeeper......等技術(shù)棧!
?戳閱讀原文領(lǐng)??!? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ??朕已閱?

