網(wǎng)站中英文互譯 java怎么做html網(wǎng)頁制作模板代碼
還差什么?【按照這個(gè)為基礎(chǔ),對(duì)照他的Redis路線圖,沖沖沖】
?Redis的常見操作和命令:Redis基本操作命令(圖文詳解)_redis 命令_進(jìn)擊小高的博客-CSDN博客
?Redis的持久化,一致性:AOF,快照,事務(wù)如何保證、雙寫一致性
個(gè)人總結(jié)全技術(shù)棧思維導(dǎo)圖(持續(xù)更新) | feiye's blog
Redis的多線程這個(gè):啥時(shí)候多線程不理解
看完小林的面經(jīng)后去問問
Redis 是 C語言寫的
如果源碼不編譯,是無法實(shí)現(xiàn)自動(dòng)跳轉(zhuǎn)的,
Redis在win上編譯有點(diǎn)麻煩,我是使用的CentOS環(huán)境,Clion編譯
編譯完就可以直接通過shell連接Redis server了
server.c 中放的是就是主類? :6000多行左右是入口main()函數(shù)位置
Redis的使用:
通過redis.conf 文件的如下位置 配置 Redis有多少個(gè)數(shù)據(jù)庫(kù):
?select 0 select 1對(duì)應(yīng)就是數(shù)據(jù)庫(kù)的序號(hào),16個(gè)數(shù)據(jù)庫(kù)對(duì)應(yīng)0-15下標(biāo)
使用 `help @list`去查看所有的List操作
解釋一下數(shù)據(jù)類型:例如我們使用的命令是"ZADD"那么,Redis就知道我們操作的數(shù)據(jù)類型一定是Zset數(shù)據(jù)類型,因此,一定會(huì)在代碼中檢查 ZADD 后面的 操作對(duì)象是不是 zset數(shù)據(jù)類型
。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。
?下圖就是數(shù)據(jù)庫(kù)的結(jié)構(gòu):
其中最重要的一項(xiàng)就是存放該數(shù)據(jù)庫(kù)key-value對(duì)的:715行 dict 類型的 *dict指針
。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。
dict 數(shù)據(jù)類型如下定義:
?83行有兩個(gè)dictht 類型的數(shù)組:就是對(duì)應(yīng)存放 老HashTable 和 新擴(kuò)容的HashTable
。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。
62:hashFunction() 給出 key 求對(duì)應(yīng)的哈希值
65:keyCompare() 比較兩個(gè)Key是否一致,是否出現(xiàn)沖突,一致的話執(zhí)行后續(xù)邏輯操作
。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。
HashTable的數(shù)據(jù)結(jié)構(gòu):
74 指向一個(gè)哈希表 -->dictEntry就是哈希表中真正存放的entry的數(shù)據(jù)結(jié)構(gòu)
?。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。
哈希表中的每一個(gè)entry其數(shù)據(jù)結(jié)構(gòu):
53行就是 val 指針 ,指向封裝的RedisObject 各數(shù)據(jù)類型的對(duì)象
?。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。
下圖就是 RedisObject這個(gè)數(shù)據(jù)類型:
// 解釋下675、676 這些行的 ":4" 是啥意思:含義是強(qiáng)制要求這個(gè)變量只使用4bit
(1 byte =8 byte),算下來,RedisObject占 16 byte
(1)type表征了這個(gè)RedisObject里面盛放的數(shù)據(jù)類型是什么,是string list zset 還是啥別的
(2) encoding 表征了數(shù)值在內(nèi)存中的編碼方式:RedisObject的對(duì)象類型取決于是用什么API:
如下圖使用API “set” 來創(chuàng)建對(duì)象,那么,無論對(duì)象賦值是什么,都會(huì)被封裝成string類型
而使用API? “LPUSH” 則會(huì)被封裝成list 類型
同時(shí),即使是相同的數(shù)據(jù)類型,根據(jù)數(shù)據(jù)的不同值,在內(nèi)存里面用不同的方式去存儲(chǔ),導(dǎo)致type相同,但底層的編碼都是不一樣的
且,數(shù)據(jù)值發(fā)生變化的時(shí)候,Redis底層會(huì)幫我們把數(shù)據(jù)的encoding方式改變,做一些優(yōu)化
(3)refcount 表征 這個(gè) RedisObject 被引用了多少次,引用計(jì)數(shù)法的簡(jiǎn)單實(shí)現(xiàn)罷了,作用也是:垃圾回收
(4)ptr 是一個(gè)真正紙箱內(nèi)存區(qū)域的一個(gè)指針,代表我們將RedisObject存放到了哪里 ==》根據(jù)ptr就能知道數(shù)據(jù)到底真正存放在內(nèi)存的哪里
(4.1)同時(shí),Redis也對(duì) ptr做了優(yōu)化:
?由于指針是8個(gè)byte,而對(duì)于數(shù)值類型一般最長(zhǎng)也就8byte:
由于我們的value是SDS類型,判斷下SDS這個(gè)buf是不是小于20位(因?yàn)橛蟹?hào)long 類型就20位),若小于,那么,有希望將該value轉(zhuǎn)化為long 類型保存
由于指針字節(jié)和數(shù)值字節(jié)長(zhǎng)度一致,因此,Redis在能將SDS的內(nèi)容轉(zhuǎn)換為數(shù)值類型是就會(huì)執(zhí)行轉(zhuǎn)換,讓ptr存放數(shù)值,而非指向SDS的地址指針
把已經(jīng)存放數(shù)值的value變量強(qiáng)轉(zhuǎn)為(void*)類型再賦值給ptr ,如下圖所示,就可以減少內(nèi)存使用,減少一次IO
?
(4.2)Redis存放字符串的時(shí)候,他的底層編碼方式:
若字符串SDS的buf[]內(nèi)容? <= 44字節(jié):encoding 方式是 embstr
????????????????????????????????????????????? >? 44字節(jié):encoding 方式是 raw
為啥是44呢?
【回答】:CPU 通過地址總線 從內(nèi)存中拿數(shù)據(jù):并不是想拿多少拿多少
而是每次CPU至少要拿64byte(這個(gè)大小就是 緩存行大小 cache line -- 這是為了緩存一致性原理的優(yōu)化體現(xiàn)),即,即使CPU需要的數(shù)據(jù)很少,幾個(gè)byte而已,但是一次CPU與內(nèi)存的交互 也會(huì)順帶把那些不需要的數(shù)據(jù)也拿上,湊夠64byte
我們觀察 RedisObject 這個(gè)結(jié)構(gòu)體,它的大小是16byte,CPU取指定的 RedisObject的某個(gè)結(jié)構(gòu)體,除了需要的16byte,還得去會(huì)不相干的48byte,于是Redis的優(yōu)化是 想把 RedisObject的ptr指向的數(shù)據(jù)內(nèi)容放到這48byte上,這樣,就可以不用先將RedisObject對(duì)象放到L1 cache,在找到ptr,把ptr指向的地址中對(duì)應(yīng)的數(shù)據(jù)內(nèi)容加載到L1 cache了
而對(duì)于存放數(shù)據(jù),48字節(jié)的字符串應(yīng)該對(duì)應(yīng)的是 sdshdr8能表示的數(shù)據(jù)范圍里,而sdshdr8結(jié)構(gòu)體中,表征信息的len、alloc變量等需要4字節(jié),那么還剩下44字節(jié),因此,只要SDS的中的buf[]大小少于44字節(jié),就可以將該字符串內(nèi)容和RedisObject實(shí)例存放在一起,占滿64Byte的空間,一次被CPU讀到L1cache中
這種數(shù)據(jù)和RedisObject一起在分配空間上分配到一起的存放方式 就是 embstr (embedding string? 嵌入式字符串)
?Redis數(shù)據(jù)庫(kù)的各個(gè)數(shù)據(jù)類型總覽:
?
?哪怕是同一種RedisObject 的 type結(jié)構(gòu),底層數(shù)據(jù)類型實(shí)現(xiàn)也有很多不同:如 int 、raw 、embstr 、quicklist 等等
而value最后都會(huì)放到 RedisObject里面進(jìn)行封裝:
?。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。
?。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。
Redis的所有命令都會(huì)被封裝到RedisCommand這個(gè)類里面:
?
Redis 底層設(shè)計(jì)方式,Redis底層 如何 管理數(shù)據(jù):
Redis通過 HashTable 來 存放數(shù)據(jù):
我們知道 Redis 維護(hù)的是一個(gè)個(gè) key-value 的項(xiàng)entry,而組織存放這些entry的數(shù)據(jù)結(jié)構(gòu)是 HashTable,HashTable是一個(gè)數(shù)組:
通過對(duì) entry 中 key 的 hash() 處理,并且按照HashTable大小取模,這樣,把key對(duì)應(yīng)到為一個(gè)int 值。該 int 值作為下標(biāo),HashTable[i] 中就存放 該entry 的 value 內(nèi)容的地址,即,存放的是指向value值存放空間的指針(注意,HashTable中存的是地址,不是把entry直接存進(jìn)去了)
由于hash函數(shù)會(huì)導(dǎo)致哈希沖突,即,不同的key可能會(huì)哈希到同一個(gè)值:Redis 通過拉鏈法,即,在HashTable的值指向一個(gè)個(gè) 結(jié)構(gòu)體:結(jié)構(gòu)體內(nèi)容包括:key值,value的地址,和 next指針(用于指向下一個(gè)哈希到相同位置的entry條目),同時(shí)每次出現(xiàn)哈希沖突,新的entry對(duì)應(yīng)的結(jié)構(gòu)體以頭插法的方式插入到鏈表頭部,新頭插的entry可能會(huì)在最近被訪問的概率大
而哈希沖突越來越頻繁的時(shí)候,拉鏈越來越長(zhǎng),找到一個(gè)對(duì)應(yīng)的entry時(shí)間也越來越長(zhǎng),此時(shí),HashTable就會(huì)考慮擴(kuò)容:HashTable擴(kuò)容的條件是:若出現(xiàn)一條拉鏈鏈表的長(zhǎng)度>HashTable的長(zhǎng)度,那么,HashTable擴(kuò)容。
HashTable擴(kuò)容的方式:成倍擴(kuò)容:原來HashTable長(zhǎng)度是 8 ,擴(kuò)容后會(huì)開辟一個(gè)新的大小為8*2=16的空間,放置擴(kuò)容的HashTable。
而后將舊的HashTable中的拉鏈上的各個(gè)entry結(jié)構(gòu)體重新執(zhí)行哈希函數(shù),插入到擴(kuò)容后的HashTable中。這個(gè)操作就被稱為 rehash
主動(dòng)漸進(jìn)式將entry移動(dòng)到新的HashTable中,操作是:不管get set 等任何訪問HashTable的操作,只要訪問了HashTable,就按順序移動(dòng)一個(gè)hash槽(一個(gè)哈希槽對(duì)應(yīng)所有被哈希函數(shù)處理后對(duì)應(yīng)到該位置的entry)內(nèi)的所有內(nèi)容。這么做是由于,我們使用Redis就是為了加速,而大批量新舊HashTable的數(shù)據(jù)轉(zhuǎn)移會(huì)影響Redis正常功能,因此要漸進(jìn)式移動(dòng)數(shù)據(jù)。
在漸進(jìn)式移動(dòng)HashTable時(shí),調(diào)用get()要求獲得某個(gè)數(shù)據(jù)的value:執(zhí)行順序 :先去舊HashTable尋找,找不到再去新的HashTable尋找
調(diào)用set()時(shí),直接向新的HashTable中插入(這就是為啥訪問get時(shí),會(huì)出現(xiàn)key找不到,是因?yàn)間et的value是新set的數(shù)據(jù),已經(jīng)被插入到新的HashTable中了)
Redis對(duì)于dict(就是Redis中的數(shù)據(jù)庫(kù))rehash操作時(shí),若如208行所示:若訪問已經(jīng)發(fā)現(xiàn)n*10個(gè)哈希槽中都沒有數(shù)據(jù)(有一個(gè)字段empty_visit記錄發(fā)現(xiàn)了多少個(gè)空哈希槽),此次訪問就不rehash了,下次再有操作再說。【這說明只有部分哈希槽沖突嚴(yán)重,其他哈希槽都還好】
注意:HashTable大小一般是2^X大小,因?yàn)?#xff0c; 如下圖所示, 當(dāng)HashTable大小為2^X時(shí),取模就可以使用位運(yùn)算加速
Redis 的 key 數(shù)據(jù)類型:
Redis 在使用時(shí),key可以是 整形、浮點(diǎn)、字符串 、音頻視頻等,但實(shí)際上,Redis服務(wù)器端會(huì)將無論是什么數(shù)據(jù)類型的key都轉(zhuǎn)換成 String對(duì)象
SDS數(shù)據(jù)類型:
【SDS相較于char[] 做了哪些優(yōu)化】
(一)減少len這個(gè)屬性占的空間:
3.2和3.2版本之前SDS 數(shù)據(jù)結(jié)構(gòu)用 int 來描述SDS的buf[]長(zhǎng)度:
int占4個(gè)字節(jié),但是buf[]長(zhǎng)度可能只有10個(gè),造成SDS實(shí)例空間浪費(fèi)
結(jié)局方案是:Redis6版本提出了SDS 新的數(shù)據(jù)類型實(shí)現(xiàn):sdshdr5
結(jié)構(gòu)中包括:char類型的 flag,和 存放內(nèi)容的buf[]
char占8字節(jié),前3位用于表明這是個(gè)SDS的數(shù)據(jù)類型,后5位用來指示buf[]的長(zhǎng)度
?啥叫SDS數(shù)據(jù)類型:因?yàn)镾DS為了不同長(zhǎng)度的buf定義了多種不同的 sdshdrX 數(shù)據(jù)類型,有sdshdr5 , sdshdr8 等等:
?(二)預(yù)分配、懶回收:
定義字符串 ss = "lalala"時(shí),系統(tǒng)會(huì)自動(dòng) malloc出6個(gè)char大小存放
此時(shí)我們想在后面加一個(gè)字符,就得重新malloc(),malloc()函數(shù)調(diào)用要很長(zhǎng)的時(shí)間,因此,每次出現(xiàn)擴(kuò)容需求的時(shí)候,若擴(kuò)容內(nèi)容<1M : 倍增原來的buf數(shù)組長(zhǎng)度,若>1M,則滿足新的buf數(shù)組的長(zhǎng)度后在追加1M的大小
SDS的free變量(在6版本的Redis中改了個(gè)名字 叫 alloc,還是一個(gè)意思一個(gè)用法)就是記錄這個(gè)每次與分配后,還剩余的空間(free也因?yàn)閎uf的長(zhǎng)度不同 優(yōu)化為了 uint8_t? 或者 uint16_t)
(三)Redis 賦值buf[]時(shí),也會(huì) 以 `\0`結(jié)尾,為了兼容C語言,其實(shí)就是為了少寫點(diǎn)函數(shù),用用C語言的函數(shù)
所以,就比如上圖的sdshdr8這個(gè)結(jié)構(gòu):就是8bit的len,8bit的alloc,8bit的char,同時(shí),由于buf[]為了和C語言字符串統(tǒng)一,也是以'\0'結(jié)尾,因此,要保存8bit的char,總結(jié)下來就是4個(gè)字節(jié)
(四)二進(jìn)制安全,即使buf[]中出現(xiàn) `\0`也沒關(guān)系,不會(huì)被處理為內(nèi)容結(jié)束
List數(shù)據(jù)類型:
List的常見用法API:
1. 向list中追加元素:
`LPUSH alist a b c`? ==》向名為alist的列表中從左側(cè)加入了3個(gè)元素
因此 alist的內(nèi)存擺放是:c,b,a
2. 從list中取出元素:
`LPOP alist`? ==》從名為alist的列表中從左側(cè)取出元素,并從列表中刪除該元素
//由于我們剛存放的方式是 c,b,a? 因此,第一次LPOP取出的是 'c'
當(dāng)列表alist中的元素全被取出,Redis會(huì)把 alist 這個(gè)key也釋放掉
[可以感受到,list 是個(gè)雙端隊(duì)列]:同一邊放,同一邊取,list就是個(gè)棧;
????????????????????????????????????????????????????????? 一邊放,另一邊取,list就是個(gè)隊(duì)列
而`BLPOP key timeout` 這種語句,可以設(shè)置阻塞和超時(shí),若list中沒有元素,就等著阻塞,阻塞超過設(shè)置的超時(shí)時(shí)間,就不等待了
這種阻塞API讀取都有,參數(shù)類似:`BLPOP` `BRPOP`
3. `BRPOPPUSH source distination timeout`? :?
目的是構(gòu)建類似消息隊(duì)列的數(shù)據(jù)結(jié)構(gòu),當(dāng)一個(gè)消息被pop出去,在處理過程中宕機(jī)或錯(cuò)誤處理,那么,這則消息就相當(dāng)于丟失了,BRPOPPUSH則選擇對(duì)于list中的每個(gè)元素,pop之前加入到source隊(duì)列中,這樣就算處理失敗,該元素也不會(huì)消失,依舊存在 ?????????????????????????????????????????????????????
list的底層設(shè)計(jì):
1. list中存放的元素 長(zhǎng)度不定,同時(shí)操作從list兩邊去讀寫 ==》因此會(huì)想到使用雙端鏈表
但是,雙端鏈表需要2個(gè)指針,一個(gè)指針8字節(jié),因此,哪怕list存儲(chǔ)的元素所占內(nèi)存很少,list也至少需要16字節(jié)來存放指針等基本信息。
這樣的內(nèi)存開銷Redis不能接受,因此,Redis選擇ziplist 作為底層數(shù)據(jù)結(jié)構(gòu),存放list的各個(gè)元素
2. ziplist 底層編碼:【ziplist是緊湊的二進(jìn)制數(shù)據(jù)編碼類型,每次數(shù)據(jù)發(fā)生變動(dòng)都需要:新建存儲(chǔ)空間,數(shù)據(jù)移動(dòng)復(fù)制,指向新的空間三個(gè)步驟】
由于向ziplist中追加數(shù)據(jù)時(shí),由于 ziplist存放數(shù)據(jù)內(nèi)容的空間是一段連續(xù)的空間,因此,每次追加都需要新開辟足夠的空間、將就空間的數(shù)據(jù)賦值移動(dòng),再吧新加入的數(shù)據(jù)append到新空間,這個(gè)操作在ziplist存放的數(shù)據(jù)量打的時(shí)候很費(fèi)時(shí)間(因?yàn)橛忠_辟空間,又要O(n)復(fù)制),因此,采用雙層結(jié)構(gòu),quicklist組織quickNode,quickNode節(jié)點(diǎn)中的數(shù)據(jù)部分就是ziplist數(shù)據(jù)類型的。(ziplist這種二進(jìn)制編碼方式也注定他只能每次修改數(shù)據(jù)都要 重新開辟一次空間),因此,現(xiàn)在多了quickNode,并有專門的屬性控制:若quickNode節(jié)點(diǎn)中的ziplist長(zhǎng)度過長(zhǎng),在修改時(shí)就會(huì)影響效率,那么就把一個(gè)QuickNode中的ziplist內(nèi)容拆分成兩個(gè)QuickNode中的ziplist 內(nèi)容
如下圖所示:頭結(jié)點(diǎn)尾結(jié)點(diǎn)的quicklist的quicknode節(jié)點(diǎn)不壓縮這個(gè)優(yōu)化的原因是:因?yàn)榻?jīng)常使用的就是頭尾節(jié)點(diǎn),因此,不壓縮就是好的
一次新開辟一個(gè)比較連續(xù)的內(nèi)存空間存放ziplist:黃色部分是ziplist的描述信息:
第一塊32bits:描述 綠色部分的數(shù)據(jù)部分占多少個(gè)字節(jié)
第二塊32bits:最后的一個(gè)元素在綠色數(shù)據(jù)部分的位置(記錄偏移量) -- 因?yàn)椴灰欢ㄈ及褦?shù)據(jù)部分用滿
第三塊16bits:在綠色數(shù)據(jù)部分存放了多少個(gè)元素
最后一塊8bits :用于表明是ziplist的結(jié)尾
數(shù)據(jù)元素 放在綠色部分:每個(gè)元素是一個(gè)entry類型:
【壓縮存放太復(fù)雜了別看了】-- 就是 數(shù)據(jù)存儲(chǔ)不是字符,而是二進(jìn)制方式存儲(chǔ)
而list的底層實(shí)現(xiàn):是用雙端鏈表整合多個(gè)ziplist,組成最終的list:
quicklist 包含 以quicklsNode為元素的雙端隊(duì)列,quicklsNode 中的數(shù)據(jù)部分就是 ziplist
?==》list 的type 是list ,編碼類型(通過encoding Object aalist)獲得,是quicklist
?Set數(shù)據(jù)類型:
Set的API:
1. 向aset 中添加內(nèi)容 :`SADD aset 1 2 3 4 3 100 77 66 `
2. 獲得set中的元素:`SMEMBERS aset`
==>此時(shí)展示的aset中的數(shù)據(jù)內(nèi)容是否有序和不重復(fù)其實(shí)因?yàn)?把數(shù)據(jù)中的內(nèi)容 編碼為intset,因此庫(kù)有序但無重復(fù)
當(dāng)向 aset中添加其他數(shù)據(jù)類型(如string后),Redis的底層編碼就會(huì)變?yōu)镠ashTable,而此時(shí)再次調(diào)用 `SMEMBERS aset` 就會(huì)發(fā)現(xiàn) 元素 無序且不重復(fù)(因?yàn)镠ashTable是無序的)
intset數(shù)據(jù)類型就是一個(gè)數(shù)組:如果往 intset中添加新元素,如果存值的數(shù)組空間不夠,就得擴(kuò)容;空間夠的話,就把intset的部分?jǐn)?shù)據(jù)往后移,流出空間放新的插入數(shù)據(jù)i
不過intset由于都是數(shù)值,因此,可以很方便 二分查找定位元素
Hash 數(shù)據(jù)類型:
常見hash的API:
1. 定義一個(gè)hash對(duì)象ahash ,加入鍵值對(duì) <k1,v1> <k2,v2> hset ahash k1 v1 k2 v2
hash編碼方式:
當(dāng)Hash數(shù)據(jù)類型時(shí) ziplist編碼時(shí),就會(huì)按順序存放
當(dāng)內(nèi)容過大時(shí),就會(huì)采用hashtable存放key和value的內(nèi)容,此時(shí)就不再有序了
?zset數(shù)據(jù)類型:有序的無重復(fù)列表
zset常見API:
1. 向 zset中添加元素 :`ZADD azset 100 a 200 b 50 c` ==》向azset中添加元素和他們的分值,讓zset根據(jù)分值排序
2. 由于zset中元素有序,獲取zset中 某個(gè)區(qū)間的元素集 : `ZRANGE azset 0 3 withscores` ==> 獲取從第0名到第3名的全部元素和他們的分值
zset 默認(rèn)升序排列,也可以通過 `ZrevRANGE azset 0 3 withscores` 獲得降序排列
zset編碼方式:
當(dāng)數(shù)據(jù)量小的時(shí)候使用ziplist
當(dāng)數(shù)據(jù)量大的時(shí)候使用skiplist
跳表:
由于鏈表要求有序,但只有序是的鏈表也只能O(n)復(fù)雜度,因此,提一層索引層,可以加速查找,先確定范圍之后下沉一層,去查找,,,如下圖所示:
真正實(shí)現(xiàn)會(huì)有很多層索引,比較索引 -- 下沉查找 -- 比較索引 -- 下沉查找,直到確定范圍
?下沉一層去查找,每次可以減少一半的查找范圍,故,跳表時(shí)間復(fù)雜度logN,N就是幾層的索引
跳表的數(shù)據(jù)結(jié)構(gòu):
zset的跳表數(shù)據(jù)編碼的方式如下:
dict字典類型 將分值 和 key進(jìn)行存儲(chǔ)
如1018所示,(當(dāng)key或value值過大)zset中的編碼實(shí)現(xiàn)就是 跳表zskiplist
。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。
下圖是zskiplist的數(shù)據(jù)結(jié)構(gòu):
level記錄索引的層次個(gè)數(shù)
?zskiplistLevel就是 索引層 ,其中的span數(shù)據(jù)表明該層次的索引中,兩個(gè)索引間隔多少
跳表中的元素存在,但分?jǐn)?shù)發(fā)生改變:若新的分時(shí)還能使得該元素在原來的位置,直接重置值即可;若位置改變,先把原來的元素刪去,在把新的元素-分?jǐn)?shù)插入進(jìn)zset
?