1. 程式人生 > 資料庫 >開發學習筆記-Redis

開發學習筆記-Redis

redis中的操作命令
1.redis中有關key的操作命令
檢視資料庫中所有的key:keys [pattern]
匹配0次或任意次字元
?匹配1次字元
[]匹配[]中的一個字元
keys * 檢視資料庫中所有的key
keys k
檢視資料庫中所有以k開頭的key
keys k*s 檢視資料庫中所有以k開頭,s結尾的key
keys k[eds]s 檢視資料庫中所有以k開頭,s結尾,中間為[]中任意一個字元的key
判斷key是否在資料庫中存在: exists key [key…] 如果存在,返回存在的key的數量,如果不存在,返回0
key k1 判斷k1是否存在
移動指定key到指定的資料庫例項: move key index

move k1 3 將k1移動到3號資料庫
檢視指定key的剩餘生存時間: ttl key
如果key不存在,返回-2
如果key沒有設定生存時間,返回-1
ttl k1 檢視k1的剩餘生存時間
設定key的最大生存時間: expire key seconds
expire k1 3 給k1設定3秒最大生存時間
檢視key的資料型別: type key
type k1 檢視k1的資料型別
重新命名key: rename key newkey
rename k1 k2 將k1改為k2
刪除key: del key [key…] 返回實際刪除的key的數量
del k1 刪除指定的key

2.redis中有關String型別的操作命令   單key - 單value
    將string型別的資料儲存到redis中: set key value,如果key已經存在,覆蓋之前的value
        set k1 v1       儲存一條鍵為k1的資料,值為v1
    從redis獲取字串型別的資料: get key
        get k1          獲取鍵為k1的值
    追加字串:  append key value   返回拼接後的字串長度,若key不存在,相當於set
        append k1 value 在k1後面拼接value
    獲取字串資料的長度:   strlen key
        strlen k1       獲取鍵為k1的字串的長度
    將字串數值進行加1運算: incr key  返回加1運算之後的資料 如果key不存在,設定一個key,初始化值為0,再加1
                                     要求key表示的value必須是數值,否則報錯
        set age 11  ----- age : 11
        incr age          age : 12  
    將字串數值進行減1運算: decr key  返回減1運算之後的資料 如果key不存在,設定一個key,初始化值為0,再減1
                                     要求key表示的value必須是數值,否則報錯
        set age 11  ----- age : 11
        decr age          age : 10
    將字串數值加offset運算: incrby key offset 返回加offset運算之後的資料 如果key不存在,設定一個key,初始化值為0,再加offset
                                              要求key表示的value必須是數值,否則報錯
        set age 11  ----- age : 11
        incrby age 10 --- age : 21
    將字串數值減offset運算: decrby key offset 返回減offset運算之後的資料 如果key不存在,設定一個key,初始化值為0,再減offset
                                              要求key表示的value必須是數值,否則報錯
        set age 11  ----- age : 11
        decrby age 10 --- age : 1  
    獲取字串的子字串: getrange key startIndex endIndex
        set str abcdefg ------- abcdefg
        getrange str 0 3 ------ abcd
    用value覆蓋從下標為startIndex開始的字串: setrange key startIndex value
        set str abcdeee ------ abcdeee
        setrange str 0 eeee -- eeeeeee
    設定字串資料的同時,設定它的最大生命週期: setex key seconds value
        setex k1 3 v1   設定k1,值為v1, 生命週期3秒
    只有在key不存在的時候,才設定字串型別的資料: setnx k1 v1
        setnx k1 v1
    批量設定string型別的資料到資料庫中: mset k1 v1 k2 v2 k3 v3 .....
        mset k1 v1 k2 v2 k3 v3  設定k1 k2 k3到資料庫中
    批量獲取string型別的資料: mget k1 k2 k3 k4 ....
        mget k1 k2 k3           獲取k1 k2 k3的資料
    批量設定string型別的資料到資料庫中,只有當所有key都存在才設定成功: msetnx k1 v1 k2 v2 ....
        msetnx k1 v1 k2 v2      設定k1 k2到資料庫中,只要有一個key已經存在,則所有的都不設定
3.redis中有關list型別資料的操作命令   單key - 多有序value [插入順序]
    將一個或者多個值依次插入到列表的表頭: lpush listName v1 v2 v3 v4..
        lpush list1 v1 v2 v3 --- v3 v2 v1
    將一個或者多個值依次插入到列表的表尾: rpush listName v1 v2 v3 v4..
        lpush list1 v1 v2 v3 --- v3 v2 v1
        rpush list1 v4 v5 v6 --- v3 v2 v1 v4 v5 v6
    獲取指定列表中的元素: lrange lsitName startIndex endIndex
        lpush list1 v1 v2 v3 --- v3 v2 v1
        rpush list1 v4 v5 v6 --- v3 v2 v1 v4 v5 v6
        lrange list1 0 2     --- v3 v2 v1
    從指定列表中移除並返回表頭元素: lpop listName
        lpush list1 v1 v2 v3 --- v3 v2 v1
        lpop list1           --- v3
        lrange list1 0 -1    --- v2 v1
    從指定列表中移除並返回表尾元素: rpop listName
        lpush list1 v1 v2 v3 --- v3 v2 v1
        rpop list1           --- v1
        lrange list1 0 -1    --- v3 v2
    獲取指令列表中指定下標的元素(不移除): lindex listName index
        lpush list1 v1 v2 v3 --- v3 v2 v1
        lindex list1 2       --- v1
    獲取指定列表的長度: llen listName
        lpush list1 v1 v2 v3 --- v3 v2 v1
        llen list1           --- 3
    從指定列表中移除與value相同的資料: lram listName count value
                    count > 0 : 從左側移除count個
                    count < 0 : 從右側移除-count個 
                    count = 0 : 移除所有
        lpush list1 v1 v1 v1 v2 v0 v2 v3 v3 v3 v3
        lram list1 2 v1      --- v1 v2 v0 v2 v3 v3 v3 v3
        lram list1 -2 v3     --- v1 v2 v0 v2 v3 v3
        lram list1 -1 v2     --- v1 v2 v0 v3 v3
    擷取指定列表中指定區間內的元素組成新的列表,並賦值給key : ltrim key startIndex endIndex
        lpush list1 v1 v2 v3 --- v3 v2 v1 [list1]
        ltrim list2 0 1      --- v3 v2 [list2]
    將指定列表中指定下標的元素設定為指定值: lset listName index value
        lpush list1 v1 v2 v3 --- v3 v2 v1
        lset list1 1 v0      --- v3 v0 v1
    將value插入到指定列表中某元素之前/之後的位置: linsert listName before/after pivot value
        lpush list1 v1 v2 v3 --- v3 v2 v1
        linsert list1 before v2 v0 --- v3 v0 v2 v1
        linsert list1 after v2 v4  --- v3 v0 v2 v4 v1
redis中有關set型別資料的操作命令 單key - 多無序value value不能重複
    將一個或多個數據新增到指定的集合中: sadd key v1 v2 v3 ... 如果元素已經存在,忽略元素,返回成功加入的元素的個數
        sadd set1 a b c a     --- a b c(不一定是這個順序)
    獲取指定集合中的所有元素: smembers key
        sadd set1 a b c a     --- a b c(不一定是這個順序)
        smembers set1         --- b a c
    判斷指定元素在指定集合中是否存在: sismember key value
        sismember key v1       判斷key中是否有v1元素
    獲取指定集合的長度: scard key
        sadd set1 a b c a     --- a b c(不一定是這個順序)
        scard set1            --- 3
    移除指定集合中一個或者多個元素: srem key member [member ...] 返回成功移除的個數
        sadd set1 a b c a     --- a b c(不一定是這個順序)
        srem set1 b c         --- 2 
    隨機獲取指定集合中的count個元素(預設為1): srandmember key[count]
                                                        count > 0 : 獲取不重複的count個
                                                        count < 0 : 獲取可能重複的count個
        sadd set1 a b c a     --- a b c(不一定是這個順序)
        srandmember set1      --- a / b / c
    隨機移除指定集合中count個元素(預設為1) :  spop key [count]
        sadd set1 a b c a     --- a b c(不一定是這個順序)
        spop set1 2           --- a / b / c
    將指定集合中的指定元素移動到另一集合: smove source dest member
        sadd set1 a b c a     --- a b c(不一定是這個順序)
        sadd set2 d e         --- d e 
        smove set1 set2 a     --- a d e
    求差集 獲取第一個集合有,但是其他集合都沒有的元素: sdiff key [key key ...]
        sadd set1 1 2 3 4 5 
        sadd set2 2 3 4 
        sadd set3 4 5
        sdiff set1 set2 set3 ----- 1
    求交集 獲取所有指定集合都有的元素: sinter key [key key ...]
        sadd set1 1 2 3 4 5 
        sadd set2 2 3 4 
        sadd set3 4 5
        sinter set1 set2 set3 ----- 4
    求並集 獲取所有指定集合中所有元素組: sunion key [key key ...]
        sadd set1 1 2 3 4 5 
        sadd set2 2 3 4 
        sadd set3 4 5 6
        sunion set1 set2 set3 --- 1 2 3 4 5 6 
redis中有關hash型別資料的操作命令 單key - field:value
    將一個field:value對存到hash表中: hset key filed value 如果已存在,則覆蓋
        hset stu1 stuName zhangsan
    將一個field:value對存到hash表中: hsetnx key field value 如果已存在,放棄新增操作
    獲取指定hash表中的指定field的值: hget key field
        hget stu1 stuName
    將一個或多個field:value對存到hash表中: hmset key field value [filed value ...]
        hmset stu1 stuName zhangsan stuId 1
    獲取指定hash表中指定的多個field的值: hmget field [field ...]
        hmget stu1 stuName stuId
    獲取指定hash表中所有的field和value: hgetall key
        hgetall stu1
    獲取指定hash表中所有的field: hkeys key
        hkeys stu1
    獲取指定hash表中所有的value: hvals key
        hvals stu1
    從指定hash表中刪除一個或多個field: hdel key field [field ...]
        hdel stu1 stuName
    獲取指定hash表中所有field的個數: hlen key
        hlen stu1
    判斷指定hash表中是否存在指定的field: hexists key field  存在返回1 不存在返回0    
        hexists stu1 stuName
    對指定hash表中指定的field值進行整數加法運算: hincrby key field int
        hincrby stu1 stuId 5    對stu1中stuId加5
    對指定hash表中指定的field值進行浮點數加法運算: hincrbyfloat key field float
        hincrbyfloat stu1 score 0.5    對stu1中score加0.5
redis中有關zset型別資料的操作命令: 有序集合 給每個元素關聯一個分數,按分數決定順序,每個元素都有下標,如果元素存在,覆蓋score
    將一個或者多個member及其score加入有序集合: zadd key score member [score member ...]
        zadd zset1 10 z1 20 z2 12 z3
    獲取指定有序集合中指定下標區間的元素: zrange key startIndex endIndex [withscores]
        zrange zset1 0 -1 withscores
    獲取指定有序集合中指定分數區間的元素: zrangebyscore key min max [withscores]
        zrangebyscore zset1 10 15 withscores
    獲取指定有序集合中指定分數區間內的元素個數: zcount key min max
        zcount zset1 10 15
    刪除指定有序集合一個或多個元素: zrem key member [member ...]
        zrem zset1 z1 z2
    獲取指定有序集合中所有元素的個數: zcard key
        zcard zset1
    獲取指定有序集合中指定元素的排名: zrank key member  從0開始
        zrank zset1 z3
    獲取指定有序集合中指定元素的分數: zscore key member
        zscore zset1 z3
    獲取指定有序集合中指定元素的排名(反向): zrevrank key member
        zrevrank zset1 z1

redis的配置檔案: 在redis根目錄下提供redis.conf配置分揀
可以配置一些redis服務端執行時的一些引數
如果不適用配置檔案,redis按照預設引數執行
如果使用配置檔案,在啟動redis服務時必須指定所使用的配置檔案
redis配置檔案中關於網路的配置:
port: 指定redis服務所使用的埠,預設使用6379
bind: 指定客戶端連線redis服務時使用的ip地址,預設可以使用redis所在主機上的任何一個ip都可以
一般情況下,都會配置一個ip,而且通常是一個真實ip
如果配置了port和bind,則客戶端連線redis服務時,必須指定埠和ip:
redis-cli -h ip -p port

redis-cli -h ip -p port shutdown
tcp-keepalive: 連線保活策略。
redis常規配置:
loglevel: 配置日誌級別
logfile: 指定日誌檔案,redis在執行過程中會輸出一些日誌資訊,預設情況下,這些日誌資訊會輸出到控制檯,
我們可以使用logfile配置日誌檔案,使redis把日誌資訊輸出到指定檔案。
databases: 配置資料庫的數量
resid安全配置:
requirepass: 設定訪問redis服務時所使用的密碼,預設不使用
此引數必須在protected-mode=yes時才起作用
一旦設定了密碼驗證,客戶端連線redis服務時,必須使用密碼連線 redis-cli -h ip -p port -a pwd

redis的持久化: redis提供持久化策略,在適當的時機採用適當手段把記憶體中的資料持久化到磁碟中,每次redis服務啟動時
都可以把磁碟上的資料再次載入到記憶體中使用。
1. RDB策略: 在指定時間間隔內,redis服務執行指定次數的寫操作會自動觸發一次持久化操作
RDB策略是redis預設的持久化策略,redis服務開啟時這種持久化策略就預設開啟了
save : 配置持久化策略
dbfilename: redis RDB持久化儲存的檔案
dir: redis持久化檔案所在的目錄
2. AOF策略: 採用操作日誌來記錄進行每一次寫操作,每次redis服務啟動時,都會重新執行一遍操作日誌中的指令。
效率低,redis預設不開啟AOF功能
小結:根據資料的特點來決定開啟哪種持久化策略,一般情況下開啟RDB策略足夠了。

redis的事務:
事務: 把一組資料庫操作放在一起執行,保證操作的原子性,要麼同時成功,要麼同時失敗
redis的事務: 允許把一組redis命令放在一起執行,把命令進行序列化(依次執行),然後一起執行,保證部分原子性
multi: 用來標記一個事務的開始
exec: 執行事務佇列中所有的命令
discard: 清除所有已經壓入佇列中的命令並結束整個事務
watch: 監控某一個鍵,當事務在執行過程中,此鍵程式碼的值發生變化,則本事務放棄執行,否則正常
unwatch: 放棄監控所有鍵
redis的事務只能保證部分原子性:
如果一組命令中,有在壓入事務佇列過程中發生錯誤的命令,則本事務中所有的命令都不執行,保證事務的原子性
如果一組命令中,在壓入佇列過程中正常,但是在執行事務佇列命令時發生了錯誤,則只會影響發生錯誤的命令,
不會影響其他命令的執行,不能保證事務的原子性

redis訊息的釋出與訂閱:
redis客戶端訂閱頻道,訊息的釋出者往頻道上釋出訊息,所有訂閱此頻道的客戶端都能接收到訊息
subscribe: 訂閱一個或者多個頻道的訊息
subscribe ch1 ch2 ch3…
publish: 將訊息釋出到指定頻道
publish ch1 message
psubscribe: 訂閱一個或者多個頻道的訊息,頻道名支援萬用字元
psubscribe ch*

redis的主從複製: 主庫-從庫 : 主少從多,主寫從讀,讀寫分離,主寫同步複製到從,從機也可以有從機
主從複製缺點: 增加延遲時間,從機數量越多,越延遲
搭建一主二從的redis叢集:
搭建三臺redis服務: 使用一個redis模擬三臺redis服務
提供三分redis配置檔案 redis6379.conf redis6380.conf redis6381.conf
修改三分配置檔案,以6379為例:
bind 127.0.0.1
port 6379
pidfile /var/run/redis_6379.pid
logfile “6379.log”
dbfilename dump6379.rdb
分別使用三個配置檔案,啟動三臺redis服務
檢視三臺redis服務在叢集中的主從角色:
info replication : master-主機 slave-從機
設定主從關係: 主機不用設定,預設就是主機
在6380 6381上執行: slaveof 127.0.0.1 6379
全量複製: 一旦主從關係確定,會自動把主機上已有的資料同步複製到從庫
增量複製: 主庫修改資料,會自動同步到從庫
主機宕機: 從機原地待命,依然可以讀
主機恢復: 一切恢復正常
從機宕機: 主機少一個從機,其他從機不變
從機恢復: 從機變成主機,需要重新設定主從關係
從機上位: 主機宕機且短時間無法恢復, 選一個從機變為主機
1.從機斷開原來的主從關係
slaveof no one
2.重新設定主從關係
3.之前的主機恢復後,不再擁有從機,需要重新設定主從關係
redis的哨兵模式: 主機宕機後自動挑選從機上位
提供哨兵配置檔案: 在redis安裝目錄下建立配置檔案 redis_sentinel.conf
配置檔案中: sentinel monitor dc-redis ip port num (num: 根據某規則對從機進行投票,一旦達到num票,則上位)
啟動哨兵服務: redis-sentinel redis_sentinel.conf
之前主機恢復: 自動從屬於新的主機

Jedis: 通過java訪問redis,進行操作
連線redis: Jedis jedis = new Jedis(ip, port);
jedis相當於一個客戶端,通過jedis的方法進行操作

快取雪崩:
原有快取大量失效,新快取未到,原本應該訪問快取的請求都去查詢資料庫,對資料庫和cpu造成巨大壓力,嚴重的會造成資料庫
宕機,形成一系列連鎖反應,造成系統崩潰。
解決方法: 給快取中資料設定不同的過期時間,避免再某一時刻大量資料同時過期
快取穿透:
使用者查詢的資料在資料庫中沒有,快取中顯然也沒有,但是這種查詢需要查一遍快取再查一遍資料庫,快取命中率低,造成資源浪費
解決方法:布隆過濾器:採用bitmap和多個hash函式來判斷請求的某個資料是否存在,將所有可能存在的資料存到一個足夠大的
bitmap中,將所有一定不存在的資料過濾掉,只有通過了所有的hash函式的判斷,才能判斷該資料可能存在
快取擊穿:
某個熱點資料在快取中不斷的被大量併發訪問,在這個資料失效的瞬間,會有許多併發的訪問轉而請求資料庫,造成cpu壓力
解決方法:設定一個短期key來鎖住對當前資料的訪問,訪問結束再刪除 / 設定熱點資料不過期
快取預熱:
系統上線後,將相關快取資料載入到快取系統
快取更新:
過期快取清理:
定時清理過期快取
定時刪除+懶惰刪除 :定時隨機抽查是否過期,在訪問時如果發現過期,則刪除
快取更新:
1.volatile-lru 從已設定過期時間的資料中挑選最近最少使用的資料淘汰
2.volatile-ttl 從已設定過期時間的資料中挑選將要過期的淘汰
3.volatile-random 從已設定過期時間的資料中隨機挑選一個淘汰
4.allkeys-lru 從所有資料中挑選最近最少使用的資料淘汰
5.allkeys-random 從所有資料中隨機挑選一個淘汰
6.no-enviction 不主動淘汰資料,當快取滿時進行寫操作會報錯
快取降級:
不理解!

redis和memcache的區別:
redis支援五種資料結構:string set zset hash list, 而memcache只支援string
redis能存放512M的value,memcache只能存放1M
redis提供持久化策略,memcache不能持久化
redis支援主從模式
redis速度快於memcache

redis速度快的原因:
1.單執行緒,避免上下文切換
2.採用IO多路複用機制(處理大量客戶端的訪問請求, IO多路複用:select epoll等)
3.純記憶體操作(記憶體訪問速度不是限制redis效率的瓶頸,因此可以使用單執行緒)

redis為什麼是單執行緒的
redis是基於記憶體的操作,cpu不是redis的瓶頸, 單執行緒不會影響redis的速度
採用單執行緒,避免了不必要的上下文切換和競爭條件
redis的瓶頸在於記憶體大小、網路頻寬
redis利用佇列技術將併發訪問變為序列訪問

Redis rdbsave
SAVE:
SAVE是同步RDB持久化方式,在進行SAVE的時候Redis不能接收客戶端請求
BGSAVE:
BGSAVE是非同步RDB持久化方式,在進行BGSAVE的時候Redis可以接收客戶端請求,一邊持久化,一遍提供讀寫服務
BGSAVE的原理是fork()+copy on write
fork()即建立一個子 程序,該子程序共享父程序的資料,且子程序和父程序對共享資料的修改互相不可見,互不影響,子程序做持久化工作,父程序對外提供讀寫服務
但要做到互相不可見,不可能是將資料複製了一份,否則需要Redis給RDB專門留出一半的空間才行,Redis使用copy on write來解決這個問題

    copy on write:子程序和父程序共享同一塊記憶體區域,如果父程序沒有執行寫操作,則子程序正常執行持久化工作,如果父程序修改了共享記憶體中的資料,則Redis在
    修改前將更改資料的資料頁拷貝一份,修改資料,主程序指向新的資料頁,子程序指向原有資料頁進行持久化工作。
    BGSAVE執行前先判斷是否由子程序已經存在,如果存在則放棄本次持久化操作,如果不存在則fork子程序進行持久化操作

Redis五種資料型別及底層實現
五種資料型別即五種資料物件,redis資料物件的基本格式中,type欄位來表示物件的型別,encoding欄位來表示底層使用那種資料結構實現,prt指向底層的資料結構
1.String
1.如果字串儲存的是整數值,並且可以用long型別來表示,則直接儲存在redisObject的ptr中,即ptr直接指向這個long型別的數值
2.如果字串儲存小於32位元組的字串值,則採用embstr編碼,使用簡單動態字串(SDS)結構,ptr指向sdshdr,即SDS所在的位置,在embstr編碼下,記憶體分配一次,分配一塊連續的記憶體空間。
3.如果字串儲存大於32位元組的字串值,則採用raw編碼,使用簡單動態字串(SDS)結構,ptr指向sdshdr,與embstr不同的是,raw編碼下的記憶體分配兩次,sdshdr的位置和redisObject位置不連續。
總結:
1.redis中,浮點數型別是轉換為字串在進行儲存的(long類型範圍內的整數直接用long型別儲存)
2.raw和embstr編碼效果相同,只有記憶體的分配與釋放不同,raw兩次,embstr一次
3.embstr記憶體塊連續,能更好的使用快取帶來的優勢
4.int編碼和embstr編碼如果做追加字串等操作,滿足條件下會被轉換為raw編碼
5.embstr編碼是隻讀的,不能修改,如果要修改需要先轉換為raw編碼
2.list
1.ziplist
ziplist編碼底層實現是壓縮列表,壓縮列表裡每個節點儲存一個元素
2.linkedlist
linkedlist編碼底層實現是雙端列表,每個節點儲存一個字串物件,每個字串物件內儲存一個列表元素
總結:
1.使用ziplist需要滿足兩個條件:所有字串長度都小於64位元組,元素數量小於512,否則使用linkedlist編碼
3.hash
1.ziplist
ziplist編碼的hash物件底層實現是壓縮列表,在ziplist編碼的hash物件中,key-value鍵值對是緊密相連的放入壓縮連結串列的,先放key,再放value,總是向表尾新增鍵值對。
2.hashtable
hashtable編碼的hash物件底層實現是字典,hash物件中的每個鍵值對都用一個字典鍵值對來儲存
字典的鍵和值都是字串物件
總結:
1.hash物件使用ziplist編碼需要滿足兩個條件,所有鍵和值的字串長度小於64位元組,鍵值對數量小於512,否則使用hashtable編碼
4.set
1.intset
intset編碼的底層實現是整數集合,所有元素儲存在整數集合中
2.hashtable
hashtable編碼的底層實現物件是字典,字典的每個鍵是一個字串物件,儲存一個集合元素,值為null
總結:
1.集合物件使用inset編碼需要滿足兩個條件:所有元素都是整數值,元素個數小於512個
5.zset
1.ziplist
ziplist編碼的有序集合物件的底層實現是壓縮列表,與hash中的類似,key的位置儲存集合元素,value的位置儲存元素的分值,分值小的靠近表頭,分值大的靠近表尾
2.skiplist
skiplist編碼的有序集合物件的底層實現是跳躍表/字典
跳錶每個節點都儲存一個元素集合,並按從小到大排列,節點object屬性存放集合元素,score屬性存放分值
字典每個鍵值對儲存一個集合元素,字典的鍵儲存元素的成員,值儲存分值
總結:
1.有序集合物件使用ziplist編碼需要滿足兩個條件,所有元素長度不超過64,元素個數小於128個
2.為何skiplist要同時使用跳躍表和字典?
跳躍表的優點是有序,但查詢分值複雜度為O(logn),字典雖然無序,但查詢分值複雜度是O(1)
雖然同時採用兩個結構,但通過指標指向同一塊記憶體地址,所以不會浪費記憶體
跳錶用於查詢一個區間內的值,可以在O(logn)時間內快速查詢
字典用於查詢某個值,O(1)時間內快速查詢
3.跳錶的優點:比起AVL,實現簡單
記憶體佔用的大小可由使用者控制
可以更方便的支援對某個範圍資料的查詢
跳錶查詢時間複雜度O(logn), 插入時間複雜度O(logn)
(跳錶有點像多級索引)

壓縮列表ziplist
壓縮列表類似陣列,但又比陣列更加節省記憶體,因為陣列中的每一個元素佔用的記憶體空間都是固定的,而壓縮列表不固定,元素只佔用所需要的記憶體空間
壓縮列表格式
zlbytes zltail zllen [entry1 entry2 entry3…] zlend
zlbytes 記錄壓縮列表佔用的記憶體位元組數:在對壓縮列表重新分配內或計算zlend位置時使用
zltail 記錄壓縮列表表尾節點距離壓縮列表起始位置的位元組數,無需遍歷整個壓縮列表就可找到表尾節點
zllen 記錄壓縮列表包含的節點數量
zlend 標記壓縮列表的末端
entry的格式
previous_entry_length encoding content
previous_entry_length 記錄上一個節點的長度
encoding 記錄當前節點所儲存的資料型別和長度
content 當前節點儲存的資料