5種Redis數據結構詳解


本文主要和大家分享 5種Redis數據結構詳解,希望文中的案例和代碼,能幫助到大家。

轉載鏈接:https://www.php.cn/php-weizijiaocheng-388126.html

 

2.1.1 全局命令

1 查看所有鍵 key*

2 鍵總數 dbsize (dbsize命令在計算鍵總數的時候不會遍歷所有鍵,而是直接獲取Redis內置的鍵總數變量,時間復雜度為O(1),而keys命令會遍歷所有鍵,時間復雜度為O(n),當Redis保存了大量鍵時,線上環境禁止使用)

3 檢查鍵是否存在 exists key 存在返回1,不存在返回0

4 刪除鍵 del key 返回成功刪除鍵的個數,不存在的返回0

5 鍵過期 expire key seconds ttl 命令會返回剩余過期時間 -1 鍵沒設置過期時間 -2 鍵不存在

6 鍵的數據類型結構 type key 返回類型,不存在返回none 

 

2.1.2 數據結構和內部編碼

每種數據結構都有自己的底層的內部編碼實現,而且是多種實現,這樣Redis會在合適的場景選擇合適的內部編碼

每種數據結構都有兩種以上的內部編碼實現,例如list數據結構包含了linkedlist和ziplist兩種內部編碼,可以通過object encoding命令查詢內部編碼

 

 

Redis這樣設計有兩個好處:

  第一:可以改進內部編碼,而對外的數據結構和命令沒有影響。

  第二:多種內部編碼實現可以在不同的場景下發揮各自的優勢。比如,ziplist比較節省內存,但是列表元素比較多的情況下,性能有所下降,這時候Redis會根據配置選項將列表類型的內部實現轉換為linkedlist

 

2.1.3 單線程架構

Redis使用了單線程架構和I/O多路復用模型來實現高性能的內存數據庫服務

1 引出單線程模型

調用客戶端的過程:發送命令,執行命令,返回結果

所有的命令在一個隊列里排隊等待被執行,不存在多個命令被同時執行的情況

2 為什么單線程還能跑這么快

第一,純內存訪問,Redis將所有數據放在內存中,內存的響應時間長約100納秒,這是Redis達到每秒萬級別訪問的重要基礎

第二,非阻塞I/O,Redis使用epoll作為I/O多路復用技術的實現,再加上Redis自身的事件處理模型將epoll中的連接、讀寫、關閉都轉換為事件,不在網絡I/O上浪費過多的時間

第三 單線程避免了線程切換和竟態產生的消耗

單線程帶來幾個好處:第一,單線程簡化數據結構和算法的實現。第二,單線程避免了線程切換和竟態產生的消耗。但是對於每個命令的執行命令是有要求的,如果某個命令執行時間過長,就會造成其他命令的阻塞,Redis是面向快速執行場景的數據庫,單線程是理解Redis的核心

 

2.2 字符串

Redis的字符串類型是其他幾種的基礎,值可以是字符串(簡單,復雜的json,xml),數字(整型,浮點),二進制(圖片,音頻,視頻),最大值不能超過512MB

 

2.2.1 命令

1 常用命令

1 設置值 set key value 秒級過期時間 毫秒級過期時間 nx|xx

setnx setxx同上

應用場景:由於Redis是單線程命令處理機制,如果多個客戶端同時執行setnx key value,根據特性,只有一個客戶端能設置成功,可以作為分布式鎖的一種實現方案

2 獲取值 get key 不存在返回nil

3 批量設置值 mset key value 

4 批量獲取值 mget key

學會使用批量操作,有助於提高業務處理效率,但要注意每次批量操作所發送的命令不是無節制的,數量過多造成Redis阻塞或網絡擁塞

5 計數 incr key

返回結果有三種情況

值不是整數 返回錯誤

值是整數,返回自增后的結果

鍵不存在,按照值為0自增,返回結果為1

還有decr(自減),incrby(自增指定數字),decrby(自減指定數字),incrbyfloat(自增浮點數)

2 不常用命令

1 追加值 append key value

2 字符串長度 strlen key

3 設置並返回原值 getset key value

4 設定指定位置的字符 setrange key offset value

5 獲取部分字符串 getrange key start end

 

2.2.2 內部編碼

字符串內部編碼有3種:int 8個字節的長整型 embstr 小於等於39個字節的字符串 raw 大於39個字節的字符串。Redis會根據當前值的類型和長度決定使用哪種內部編碼實現

 

2.2.3 典型使用場景

1 緩存功能

Redis作為緩存層,Mysql作為存儲層,絕大部分的請求的數據都是從Redis中獲取。由於Redis具有支持並發的特性,所以緩存通常能起到加速讀寫和降低后段壓力的作用

 

開發提示:鍵名命名方式:業務名:對象名:id:[屬性]作為鍵名

偽代碼實現:

1

2

3

4

5

6

7

8

9

10

11

UserInfo getUserInfo(long id){

    userRedisKey="user:info:"+id

    value=redis.get(userRedisKey);

    UserInfo userInfo;

    if(value!=null){

        userInfo=deserialize(value)

    }else{

        userInfo=mysql.get(id)

        if(userInfo!=null)

        redis.setex(userRedisKey,3600,serizelize(userInfo))

        }

return userInfo

1

}

2 計數

1

2

3

4

long incrVideoCounter(long id){

key="video:playCount:"+id;

return redis.incr(key)

}

開發提示:防作弊,按照不同維度計數,數據持久化到底層數據源

3 共享Session

 

4 限速

 

1

2

3

4

5

6

7

8

9

phoneNum="13800000000";

key="shortMsg:limit:"+phoneNum;

 

isExists=redis.set(key,1,"EX 60",NX);

if(isExists !=null ||redis.incr(key)<=5){

通過

}else{

限速

}

某一些網站限制一個ip地址不能在一秒鍾之內訪問超過n次也可以采用類似的思路

 

2.3 哈希

哈希類型是指鍵值本身又是一個鍵值對結構

2.3.1 命令

1 設置值

hset key field value

2 獲取值 hget key field

3 刪除field hdel key field

4 計算field的個數 hlen key

5 批量設置或獲取field-value hmget key field hmset key field value 

6 判斷field是否存在 hexists key field

7 獲取所有field hkeys key 

8 獲取所有的value hvals key

9 獲取所有的field-value hgetall key

開發提示:如果一定要獲取全部的field-value,可以使用hscan命令,該命令會漸進式遍歷哈希類型

10 hincrby hincrby float

11 計算value的字符串長度 hstrlen key field

2.3.2 內部編碼

內部編碼有兩種:

ziplist(壓縮列表) 哈希元素個數<hash-max-ziplist-entries,所有值<hash-max-ziplist-value配置時,Redis會使用ziplist作為hash的內部實現,ziplist使用更加緊湊的結構實現多個元素存儲,節省內存方面比hashtable更加優秀

hashtable(哈希表) 當hash類型無法滿足ziplist 條件時,選擇,因為hashtable的讀寫時間度為O(1)

2.3.3 使用場景

 

 

1

2

3

4

5

6

7

8

9

10

11

12

13

14

UserInfo getUserInfo(long id){

userRedisKey="user:info:"+id;

userInfoMap=redis.hgetAll(userRedisKey);

userInfoMap userInfo;

 

if(userInfoMap!=null){

userInfo=transferMapToUserInfo(userInfoMap);

}else{

userInfo=mysql.get(id);

redis.hmset(userRedisKey,tranferUserInfoToMap(userInfo));

redis.expire(userRedisKey,3600);

}

return userInfo;

}

哈希類型和關系型數據庫兩點不同:

1 哈希類型是稀疏的,而關系型數據庫是完全結構化的

2 關系型數據庫可以做復雜的查詢,而Redis去模擬關系型復雜查詢開發困難,維護成本高

三種方法緩存用戶信息

1 原聲字符串類型:每個屬性一個鍵

 

優點:簡單直觀,每個屬性都支持更新操作

缺點:占用過多的鍵,內存占用量較大,同時用戶信息內聚性比較差,所以一般不會在生產環境用

2 序列化字符串類型:將用戶信息序列化后用一個鍵保存

 

優點:簡化編程,如果合理的使用序列化可以提高內存的使用效率

缺點:序列化和反序列化有一定的開銷,同時每次更新屬性,都需要把數據取出來反序列化,更新后再序列化到Redis中

3 哈希類型:每個用戶屬性使用一對field-value,但是只用一個鍵保存

優點:簡單直觀,如果使用合理,可以減少內存空間的使用

缺點:要控制哈希在ziplist和hashtable兩種內部編碼的轉換,hashtable會消耗更多的內存

 

2.4 列表

列表類型用來存儲多個有序的字符串,一個列表最多存儲2的32次方-1個元素,列表是一種比較靈活的數據結構,它可以靈活的充當棧和隊列的角色,在實際開發上有很多應用場景

列表有兩個特點:第一、列表中的元素是有序的,這就意味着可以通過索引下標獲取某個元素或者某個范圍內的元素列表。第二、列表中的元素可以是重復的

2.4.1 命令

1 添加操作

1.1 從右邊往左插入元素 rpush key value

1.2 從左往右插入元素 lpush key value

1.3 向某個元素前或者后插入元素 linsert key before|after pivot value

 

2 查找

1 獲取指定范圍內的元素列表 lrange key start end 

索引下標有兩個特點:第一,索引下標從左到右分別是0-n-1,從右到左是-1--n,第二,lrange的end選項包含了自身,這個和很多編程語言不包含end不太相同

2 獲取列表指定索引下標的元素 lindex key index

3 獲取列表長度 llen key

3 刪除

1 從列表左側彈出元素 lpop key

2 從列表右側彈出 rpop key

3 刪除指定元素 lrem key count value

4 按照索引范圍修剪列表 ltrim key start end

4 修改

修改指定索引下標的元素 lset key index newValue

5 阻塞操作 brpop blpop key timeout

1 列表為空:如果timeout=3,那么客戶端要等到3s后返回,如果timeout=0,客戶端則阻塞等下去,如果添加了數據,客戶端立刻返回

2 列表不為空:客戶端立即返回

2.4.2 內部編碼

列表類型的內部編碼有兩種

ziplist(壓縮列表):當列表元素個數<list-max-ziplist-entries,同時list-max-ziplist-value(64字節),Redis會選用列表的內部實現來減少內存的使用

linkedlist(鏈表) 當列表類型無法滿足ziplist的條件時,Redis會使用linkedlist作為列表的內部實現

2.4.3 使用場景

1 消息隊列 

Redis的lpush+brpop命令組合即可實現阻塞隊列

 

2 文章列表 

兩個問題:第一,如果每次分頁獲取的文章個數較多,需要執行多次hgetall操作,此時考慮使用pipeline批量獲取,或者考慮將文章數據序列化為字符串類型,使用mget批量獲取。第二,分頁獲取文章列表時,lrange命令在列表兩端性能較好,但是如果列表較大,獲取列表中間范圍元素的性能會變差,此時可以考慮二級拆分

開發提示:

lpush+lpop=Stack(棧)

lpush+rpop=Queue(隊列)

lpsh+ltrim=Capped Collection(有限集合)

lpush+brpop=Message Queue(消息隊列)

 

2.5 集合

集合用來保存多個字符串元素,和列表不同的是不允許有重復元素,並且集合中元素是無序的

2.5.1 命令

1 集合內操作

1.1 添加元素 sadd key element 

1.2 刪除元素 srem key element

1.3 計算元素個數 scard key 

1.4 判斷元素是否在集合中 sismember key element

1.5 隨機從集合返回指定個數元素 srandmember key 

1.6 從集合隨機彈出元素 spop key

1.7 獲取所有元素 smembers key

2 集合間操作

1 求多個集合的交集 sinter key ...

2 求多個集合的並集 suinon key..

3 求多個集合的差集 sdiff key ..

4 將交集、差集、並集結果保存

sinterstore destination key 

sdiffstore destination key 

suionstore destionation key

 

2.5.2 內部編碼

集合類型的內部有兩種:

intset(整數集合):當集合中的元素都是整數且元素個數小於set-max-intset-entries配置(默認512個)時,Redis會選用intset來作為集合內部的實現,從而減少內存的使用

hashtable(哈希表) 當集合類型無法滿足intset的條件時,Redis會使用hashtable作為集合的內部實現

2.5.3 使用場景

集合類型比較典型的應用場景是標簽。

1 給用戶添加標簽

sadd user:1:tags tag1 tag2

2 給標簽添加用戶 

sadd tag1:users user:1 user:3

開發提示:用戶和標簽的關系維護應該在一個事務內執行,防止部分命令失敗造成的數據不一致

3 刪除用戶下的標簽

srem user:1:tags tag1 tag5

4 刪除標簽下的用戶

srem tag1:users user:1

5 計算用戶共同感興趣的標簽

sinter user:1 tags user:2 tags

開發提示:sadd=Tagging(標簽) spop/srandmember=Random item(生成隨機數,比如抽獎)

spop/srandmember=Random item(生成隨機數,比如抽獎) sadd+sinter=Social Graph(社交需求)

 

2.6 有序集合

有序集合就是在集合之上加了個score作為排序的依據 

 

2.6.1 命令

1 集合內

1添加成員 zadd key score memeber

nx xx ch 返回此次操作后,有序集合元素和分數發生變化的個數,incr:對score做增加

有序集合相比集合提供了排序字段,但是也產生了代價,zadd的時間復雜度為O(log(n)),sadd的時間復雜度為O(1)

2 計算成員個數

scard key

3 計算某個成員的分數 zscore key member

4 計算成員的排名 zrank key member

5 刪除成員 zrem key member

6 增加成員的分數 zincrby key increment member

7 返回指定排名范圍的成員 zrange key start end

8 返回指定分數范圍的成員 zrangebysore key min max 

9 返回指定分數范圍成員個數 zcount key min max

10 刪除指定排名內的升序元素 zremrangebyrank key start end

11 刪除指定分數范圍的成員 zremrangebyscore key min max

2 集合間的操作

1 交集 zinterstore destination numkeys key 

2 並集 zunionstore destionation numkeys key

2.6.2 內部編碼

有序集合類型的內部編碼有兩種:

ziplist(壓縮列表) 當有序集合的元素個數小於zset-max-ziplist-entries配置,同時每個元素的值都小於zset-max-ziplist-value配置時,Redis會用ziplist來作為有序集合的內部實現,ziplist可以有效的減少內存的使用

skiplist(跳躍表) 當ziplist條件不滿足時,有序集合會使用skiplist作為內部實現,因此此時ziplist的讀寫效率會下降

2.6.3 使用場景

有序集合比較典型的使用場景是排行榜系統。例如視頻網站需要對用戶上傳的視頻做排行榜榜

1 添加用戶贊數 zdd user:ranking:2016_03_15 mike 3

之后 zincrby user:ranking:2016_03_15 mike 1

2 取消用戶贊數

zrem user:rank:2016_03_15 mike

3 展示獲取贊數最多的十個用戶

zrevrangebyrank user:ranking:2016_03_15 0 9

4 展示用戶信息以及用戶分數

此功能可以將用戶名作為鍵后綴,將用戶信息保存在哈希類型中,至於用戶的分數和排名可以使用zcore和zrank兩個功能 

 

 

2.7 鍵管理

2.7.1 單個鍵管理

1 鍵重命名 rename key newkey

2 隨機返回一個鍵 randomkey

3 鍵過期 -1 鍵沒有設置過期時間 -2 鍵不存在

expire key seconds:鍵在seconds秒后過期

expire key itmestamp 鍵在秒級時間戳timestamp后過期

1 如果expire key的鍵不存在,返回結果為0

2 如果過期時間為負值,鍵會立即被刪除,就如使用使用del命令一樣

3 persist 命令可以將鍵的過期時間清除

4 對於字符串類型鍵,執行set命令會去掉過期時間,這個問題很容易在開發中被忽視

5 Redis不支持二級數據結構內部元素的過期功能,例如不能這列表類型的一個元素做過期時間設置

6 setex命令作為set+expire組合,不但原子執行,同時減少了網絡通訊的時間

4 遷移鍵

遷移鍵功能非常重要,有move、dump+restore、migrate三組遷移鍵的方法,它們的實現方式以及使用場景不太相同

1 move 用於在Redis內部進行數據遷移

2 dump+restore 實現在不同的Redis實例之間進行數據遷移的功能,這個遷移分兩步

1 在源Redis上,dump命令會將鍵值序列化,格式采用的是RDB格式

2 在目標Redis上,restore命令將上面的序列化的值進行復原,其中ttl參數代表過期時間

3 migrate 命令用於在Redis實例間進行數據遷移的

2.7.2 遍歷鍵

Redis提供了兩個命令遍歷所有的鍵分別是keys和scan

1 全量遍歷鍵 keys pattern

* 代表匹配任意字符

.代表匹配一個字符

[] 代表匹配一個字符

\x用來轉義,例如要匹配星號,問號需要進行轉義

大量容易造成阻塞

2 漸進式遍歷

 

scan可以想象成只掃描一個字典中的一部分鍵,直到將字典中所有鍵遍歷完畢

對應的命令還有hsan、sscan、zcan

漸進式遍歷可以有效解決keys命令可能產生的阻塞問題,在有增刪的時候,新來的鍵無法保證遍歷到

2.7.3 數據庫管理

1 切換數據庫 select dbIndex

2 flushdb/flushall 用於清除數據庫 數據多的時候會出現阻塞


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM