Redis核心數(shù)據(jù)結(jié)構(gòu)與高性能原理

點(diǎn)贊再看彤委,養(yǎng)成習(xí)慣柳爽,搜一搜【一角錢技術(shù)】關(guān)注更多原創(chuàng)技術(shù)文章。本文 GitHub org_hejianhui/JavaStudy 已收錄,有我的系列文章闪萄。

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

image

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

字符串常用操作

SET key value   //存入字符串鍵值對(duì)
MSET key value [key value ...]  //批量存儲(chǔ)字符串鍵值對(duì)
SETNX key value     //存入一個(gè)不存在的字符串鍵值對(duì)
GET key     //獲取一個(gè)字符串鍵值
MGET key [key ...]  //批量獲取字符串鍵值
DEL key [key ...]   //刪除一個(gè)鍵
EXPIRE key seconds //設(shè)置一個(gè)鍵的過期時(shí)間(秒)

原子加減

INCR key //將key中儲(chǔ)存的數(shù)字值加1
DECR key //將key中儲(chǔ)存的數(shù)字值減1
INCRBY key increment //將key所儲(chǔ)存的值加上increment
DECRBY key decrement //將key所儲(chǔ)存的值減去decrement

String 應(yīng)用場(chǎng)景

  • 單值緩存
  • 對(duì)象緩存
  • 分布式鎖
  • 計(jì)數(shù)器
  • Web集群Session共享
  • 分布式系統(tǒng)全局序列號(hào)
  1. 單值緩存
SET key value
Get key
  1. 對(duì)象緩存
SET user:1 value(json格式數(shù)據(jù))
MSET user:1:name yijiaoqian user:1:balance 1888
MGET user:1:name user:1:balance 
  1. 分布式鎖
SETNX product:10001  true //返回1代表獲取鎖成功
SETNX product:10001  true //返回0代表獲取鎖失敗
...執(zhí)行業(yè)務(wù)操作...
DEL product:10001   //執(zhí)行完業(yè)務(wù)釋放鎖

SET product:10001 true  ex  10  nx  //防止程序意外終止導(dǎo)致死鎖

  1. 計(jì)數(shù)器
INCR article:readcount:{文章id}   
GET article:readcount:{文章id}  
  1. Web集群Session共享

Spring session + redis 實(shí)現(xiàn)sessio共享

  1. 分布式系統(tǒng)全局序列號(hào)
INCRBY orderId 1000 //redis批量生成序列號(hào)提升性能

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

Hash常用操作

HSET key field value    //存儲(chǔ)一個(gè)哈希表key的鍵值
HSETNX key field value  //存儲(chǔ)一個(gè)不存在的哈希表key的鍵值
HMSET key field  value [field value ...] //在一個(gè)哈希表key中存儲(chǔ)多個(gè)鍵值對(duì)
HGET key field  //獲取哈希表key對(duì)應(yīng)的field鍵值
HMGET key field [field ...] //批量獲取哈希表key中多個(gè)field鍵值
HDEL key field [field ...]  //刪除哈希表key中的field鍵值
HLEN key    //返回哈希表key中field的數(shù)量
HGETALL key //返回哈希表key中所有的鍵值

HINCRBY key field increment //為哈希表key中field鍵的值加上增量increment

Hash應(yīng)用場(chǎng)景

  • 對(duì)象存儲(chǔ)
HMSET user {userId}:name  yijiaoqian {userId}:balance  1888
HMSET user 1:name yijiaoqian 1:balance 1888
HMGET user 1:name 1:balance  
  • 電商購(gòu)物車
  1. 以用戶id為key
  2. 商品id為field
  3. 商品數(shù)量為value
image

購(gòu)物車操作:

  1. 添加商品:hset cart:1001 10088 1
  2. 增加數(shù)量:hincrby cart:1001 10088 1
  3. 商品總數(shù):hlen cart:1001
  4. 刪除商品:hdel cart:1001 10088
  5. 獲取購(gòu)物車所有商品:hgetall cart:1001

Hash結(jié)構(gòu)優(yōu)缺點(diǎn)

優(yōu)點(diǎn):

  1. 同類數(shù)據(jù)歸類整合儲(chǔ)存泞当,方便數(shù)據(jù)管理
  2. 相比string操作消耗內(nèi)存與cpu更小
  3. 相比string儲(chǔ)存更節(jié)省空間

缺點(diǎn):

  1. 過期功能不能使用在field上迹蛤,只能用在key上
  2. Redis集群架構(gòu)下不適合大規(guī)模使用
image

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

List常用操作

LPUSH key value [value ...] //將一個(gè)或多個(gè)值value插入到key列表的表頭(最左邊)
RPUSH key value [value ...]  //將一個(gè)或多個(gè)值value插入到key列表的表尾(最右邊)
LPOP key    //移除并返回key列表的頭元素
RPOP key    //移除并返回key列表的尾元素
LRANGE key start stop   //返回列表key中指定區(qū)間內(nèi)的元素,區(qū)間以偏移量start和stop指定

BLPOP key [key ...] timeout //從key列表表頭彈出一個(gè)元素襟士,若列表中沒有元素盗飒,阻塞等待timeout秒,如果timeout=0,一直阻塞等待
BRPOP key [key ...] timeout //從key列表表尾彈出一個(gè)元素,若列表中沒有元素陋桂,阻塞等待timeout秒,如果timeout=0,一直阻塞等待

List應(yīng)用場(chǎng)景

  • 常用數(shù)據(jù)結(jié)構(gòu)
  1. Stack(棧) = LPUSH + LPOP (FILO
  2. Queue(隊(duì)列)= LPUSH + RPOP (FIFO
  3. Blocking MQ(阻塞隊(duì)列)= LPUSH + BRPOP
image
  • 微博和微信公號(hào)消息流
image
一角錢關(guān)注了雷軍逆趣、馬云等大V
1)雷布斯發(fā)微博,消息ID為10018
LPUSH  msg:{一角錢-ID}  10018
2)馬云發(fā)微博嗜历,消息ID為10086
LPUSH  msg:{一角錢-ID} 10086
3)查看最新微博消息
LRANGE  msg:{一角錢-ID}  0  4

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

Set常用操作

SADD key member [member ...]    //往集合key中存入元素宣渗,元素存在則忽略,若key不存在則新建
SREM key member [member ...]    //從集合key中刪除元素
SMEMBERS key    //獲取集合key中所有元素
SCARD key   //獲取集合key的元素個(gè)數(shù)
SISMEMBER key member    //判斷member元素是否存在于集合key中
SRANDMEMBER key [count] //從集合key中選出count個(gè)元素梨州,元素不從key中刪除
SPOP key [count]    //從集合key中選出count個(gè)元素痕囱,元素從key中刪除

Set運(yùn)算操作

SINTER key [key ...] //交集運(yùn)算
SINTERSTORE destination key [key ..] //將交集結(jié)果存入新集合destination中
SUNION key [key ..] //并集運(yùn)算
SUNIONSTORE destination key [key ...]   //將并集結(jié)果存入新集合destination中
SDIFF key [key ...] //差集運(yùn)算
SDIFFSTORE destination key [key ...]    //將差集結(jié)果存入新集合destination中

Set應(yīng)用場(chǎng)景

  • 微信抽獎(jiǎng)小程序

[外鏈圖片轉(zhuǎn)存失敗,源站可能有防盜鏈機(jī)制,建議將圖片保存下來直接上傳(img-4MXrY6h4-1608565742118)(https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/bcbd972138c54f229f249954a4634578~tplv-k3u1fbpfcp-zoom-1.image)]

1.點(diǎn)擊參與抽獎(jiǎng)加入集合: SADD key {userID} 
2.查看參與抽獎(jiǎng)所有用戶:SMEMBERS key
3.抽取count名中獎(jiǎng)?wù)撸篠RANDMEMBER key [count]/ SPOP key [count]
  • 微信微博點(diǎn)贊、收藏暴匠、標(biāo)簽
image
1.點(diǎn)贊: SADD  like:{消息ID}  {用戶ID} 
2.取消點(diǎn)贊: SREM like:{消息ID}  {用戶ID} 
3.檢查用戶是否點(diǎn)過贊: SISMEMBER  like:{消息ID}  {用戶ID} 
4.獲取點(diǎn)贊的用戶列表: SMEMBERS like:{消息ID} 
5.獲取點(diǎn)贊用戶數(shù): SCARD like:{消息ID}
  • 集合操作
image
SINTER set1 set2 set3 -> { c }  // 交集
SUNION set1 set2 set3 -> { a,b,c,d,e }  // 并集
SDIFF set1 set2 set3  -> { a }  // 差集
  • 集合操作實(shí)現(xiàn)微博微信關(guān)注模型
image
1) 張三關(guān)注的人: 
zhangsanSet-> {lisi, wangwu}
2) 一角錢關(guān)注的人:
 yijiaoqianSet--> {zhangsan, zhaoliu, lisi, wangwu}
3) 李四關(guān)注的人: 
lisiSet-> {zhangsan, yijiaoqian, zhaoliu, wangwu, xunyu)
4) 我和一角錢共同關(guān)注: 
SINTER zhangsanSet yijiaoqianSet--> {lisi, wangwu}
5) 我關(guān)注的人也關(guān)注他(一角錢): 
SISMEMBER lisiSet yijiaoqian 
SISMEMBER wangwuSet yijiaoqian
6) 我可能認(rèn)識(shí)的人: 
SDIFF yijiaoqianSet zhangsanSet->(zhangsan, zhaoliu}
  • 集合操作實(shí)現(xiàn)電商商品篩選
image
SADD brand:huawei  P40
SADD brand:xiaomi  mi-10
SADD brand:iPhone iphone12
SADD os:android P40 mi-10
SADD cpu:brand:intel P40 mi-10
SADD ram:8G P40 mi-10 iphone12

SINTER os:android cpu:brand:intel ram:8G >  {P40鞍恢,mi-10}

ZSet 有序集合結(jié)構(gòu)

ZSet常用操作

ZADD key score member [[score member]…] //往有序集合key中加入帶分值元素
ZREM key member [member …]  //從有序集合key中刪除元素
ZSCORE key member   //返回有序集合key中元素member的分值
ZINCRBY key increment member    //為有序集合key中元素member的分值加上increment 
ZCARD key   //返回有序集合key中元素個(gè)數(shù)
ZRANGE key start stop [WITHSCORES]  //正序獲取有序集合key從start下標(biāo)到stop下標(biāo)的元素
ZREVRANGE key start stop [WITHSCORES]//倒序獲取有序集合key從start下標(biāo)到stop下標(biāo)的元素

ZSet集合操作

ZUNIONSTORE destkey numkeys key [key ...]   //并集計(jì)算
ZINTERSTORE destkey numkeys key [key …]     //交集計(jì)算
image

ZSet應(yīng)用場(chǎng)景

  • ZSet集合操作實(shí)現(xiàn)排行榜
image
image
1. 點(diǎn)擊新聞:
ZINCRBY hotNews:20201221 1 完善低齡未成年人犯罪規(guī)定
2. 展示當(dāng)日排行前十:
ZREVRANGE hotNews:20201221 0 9 WITHSCORES
3. 七日搜索榜單計(jì)算:
ZUNIONSTORE hotNews:20201215-20201221  7 
hotNews:20201215 hotNews:20201216... hotNews:20201221
4. 展示七日排行前十:
ZREVRANGE hotNews:20201215-20201221 0 9 WITHSCORES

Redis的單線程和高性能

Redis是單線程嗎?

Redis的單線程主要是指 Redis 的網(wǎng)絡(luò)IO和鍵值對(duì)讀寫是由一個(gè)線程來完成的,這也是Redis對(duì)外提供鍵值存儲(chǔ)服務(wù)的主要流程帮掉。但是Redis的其他功能弦悉,比如持久化、異步刪除蟆炊、集群數(shù)據(jù)同步等稽莉,其實(shí)由額外的線程執(zhí)行的。

Redis 單線程為什么還能這么快涩搓?

因?yàn)樗械臄?shù)據(jù)都在內(nèi)存中污秆,所有的運(yùn)算都是內(nèi)存級(jí)別的運(yùn)算,而且單線程避免來多線程的切換性能損耗問題缩膝,正因?yàn)镽edis是單線程混狠,所以要小心使用Redis 指令,對(duì)于那些耗時(shí)的指令(比如keys)疾层,一定要謹(jǐn)慎使用将饺,一不小心就可能會(huì)導(dǎo)致 Redis 卡頓。

Redis 單線程如何處理那么多的并發(fā)客戶端連接痛黎?

Redis 的IO多路服用:redis利用epoll實(shí)現(xiàn)IO多路復(fù)用予弧,將連接信息和事件放到隊(duì)列中,依次放到文件事件分派器湖饱,事件分派器將事件分發(fā)給事件處理器掖蛤。


image
# 查看redis支持的最大連接數(shù),在redis.conf文件中可修改井厌,# maxclients 10000
127.0.0.1:6379> CONFIG GET maxclients
    ##1) "maxclients"
    ##2) "10000"

其他高級(jí)命令

keys:全量遍歷鍵

用來列出所有滿足特定正則字符串規(guī)則的key蚓庭,當(dāng)redis數(shù)據(jù)量比較大時(shí),性能比較差仅仆,要避免使用器赞。

127.0.0.1:6379> set codehole1 a
OK
127.0.0.1:6379> set codehole2 b
OK
127.0.0.1:6379> set codehole3 c
OK
127.0.0.1:6379> set code1hole a
OK
127.0.0.1:6379> set code2hole b
OK
127.0.0.1:6379> set code3hole c
OK
127.0.0.1:6379> keys *
1) "codehole1"
2) "codehole3"
3) "codehole2"
4) "code3hole"
5) "code1hole"
6) "code2hole"
127.0.0.1:6379> keys codehole*
1) "codehole1"
2) "codehole3"
3) "codehole2"
127.0.0.1:6379> keys code*hole
1) "code3hole"
2) "code1hole"
3) "code2hole"

scan:漸進(jìn)式遍歷鍵

SCAN cursor [MATCH pattern] [COUNT count]

scan 參數(shù)提供了三個(gè)參數(shù):

  • 第一個(gè)參數(shù) cursor 整數(shù)值(hash桶的索引值)
  • 第二個(gè)是 key 的正則模式
  • 第三個(gè)是一次遍歷的key的數(shù)量(參考值,底層遍歷的數(shù)量不一定)墓拜,并不少符合條件的結(jié)果數(shù)量港柜。

第一次遍歷時(shí),cursor 值為0咳榜,然后將返回結(jié)果中的第一個(gè)整數(shù)值作為下一次遍歷的 cursor夏醉。一直遍歷到返回的 cursor 值為0時(shí)結(jié)束。

注意:但是scan并非完美無暇涌韩,如果在scan的過程中如果有鍵的變化(增加畔柔、刪除、修改)贸辈,那么遍歷效果可能會(huì)碰到如下問題:新增的鍵可能沒有遍歷到释树,遍歷出了重復(fù)的鍵等情況肠槽,也就是說scan并不能保證完整的遍歷出來所有的鍵擎淤,這些是我們?cè)陂_發(fā)時(shí)需要考慮的奢啥。

info:查看redis服務(wù)運(yùn)行信息

分為 9 大塊,每個(gè)塊都有非常多的參數(shù):

  • Server 服務(wù)器運(yùn)行的環(huán)境參數(shù)
  • Clients 客戶端相關(guān)信息
  • Memory 服務(wù)器運(yùn)行內(nèi)存的統(tǒng)計(jì)數(shù)據(jù)
  • Persistence 持久化信息
  • Stats 通用統(tǒng)計(jì)數(shù)據(jù)
  • Replication 主從復(fù)制相關(guān)信息
  • CPU CPU使用情況
  • Cluster 集群信息
  • KeySpace 鍵值對(duì)統(tǒng)計(jì)數(shù)量信息

image

核心屬性說明

connected_clients:2                  # 正在連接的客戶端數(shù)量

instantaneous_ops_per_sec:789        # 每秒執(zhí)行多少次指令

used_memory:929864                   # Redis分配的內(nèi)存總量(byte)嘴拢,包含redis進(jìn)程內(nèi)部的開銷和數(shù)據(jù)占用的內(nèi)存
used_memory_human:908.07K            # Redis分配的內(nèi)存總量(Kb桩盲,human會(huì)展示出單位)
used_memory_rss_human:2.28M          # 向操作系統(tǒng)申請(qǐng)的內(nèi)存大小(Mb)(這個(gè)值一般是大于used_memory的,因?yàn)镽edis的內(nèi)存分配策略會(huì)產(chǎn)生內(nèi)存碎片)
used_memory_peak:929864              # redis的內(nèi)存消耗峰值(byte)
used_memory_peak_human:908.07K       # redis的內(nèi)存消耗峰值(KB)

maxmemory:0                         # 配置中設(shè)置的最大可使用內(nèi)存值(byte),默認(rèn)0,不限制
maxmemory_human:0B                  # 配置中設(shè)置的最大可使用內(nèi)存值
maxmemory_policy:noeviction         # 當(dāng)達(dá)到maxmemory時(shí)的淘汰策略

文章持續(xù)更新席吴,可以公眾號(hào)搜一搜「 一角錢技術(shù) 」第一時(shí)間閱讀赌结, 本文 GitHub org_hejianhui/JavaStudy 已經(jīng)收錄,歡迎 Star孝冒。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末柬姚,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子庄涡,更是在濱河造成了極大的恐慌量承,老刑警劉巖,帶你破解...
    沈念sama閱讀 206,968評(píng)論 6 482
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件穴店,死亡現(xiàn)場(chǎng)離奇詭異撕捍,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)泣洞,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,601評(píng)論 2 382
  • 文/潘曉璐 我一進(jìn)店門忧风,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人球凰,你說我怎么就攤上這事狮腿。” “怎么了呕诉?”我有些...
    開封第一講書人閱讀 153,220評(píng)論 0 344
  • 文/不壞的土叔 我叫張陵缘厢,是天一觀的道長(zhǎng)。 經(jīng)常有香客問我义钉,道長(zhǎng)昧绣,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 55,416評(píng)論 1 279
  • 正文 為了忘掉前任捶闸,我火速辦了婚禮夜畴,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘删壮。我一直安慰自己贪绘,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,425評(píng)論 5 374
  • 文/花漫 我一把揭開白布央碟。 她就那樣靜靜地躺著税灌,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上菱涤,一...
    開封第一講書人閱讀 49,144評(píng)論 1 285
  • 那天苞也,我揣著相機(jī)與錄音,去河邊找鬼粘秆。 笑死如迟,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的攻走。 我是一名探鬼主播殷勘,決...
    沈念sama閱讀 38,432評(píng)論 3 401
  • 文/蒼蘭香墨 我猛地睜開眼,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼昔搂!你這毒婦竟也來了玲销?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 37,088評(píng)論 0 261
  • 序言:老撾萬榮一對(duì)情侶失蹤摘符,失蹤者是張志新(化名)和其女友劉穎贤斜,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體议慰,經(jīng)...
    沈念sama閱讀 43,586評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡蠢古,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,028評(píng)論 2 325
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了别凹。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片草讶。...
    茶點(diǎn)故事閱讀 38,137評(píng)論 1 334
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖炉菲,靈堂內(nèi)的尸體忽然破棺而出堕战,到底是詐尸還是另有隱情,我是刑警寧澤拍霜,帶...
    沈念sama閱讀 33,783評(píng)論 4 324
  • 正文 年R本政府宣布嘱丢,位于F島的核電站,受9級(jí)特大地震影響祠饺,放射性物質(zhì)發(fā)生泄漏越驻。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,343評(píng)論 3 307
  • 文/蒙蒙 一道偷、第九天 我趴在偏房一處隱蔽的房頂上張望缀旁。 院中可真熱鬧,春花似錦勺鸦、人聲如沸并巍。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,333評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽懊渡。三九已至刽射,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間剃执,已是汗流浹背誓禁。 一陣腳步聲響...
    開封第一講書人閱讀 31,559評(píng)論 1 262
  • 我被黑心中介騙來泰國(guó)打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留忠蝗,地道東北人现横。 一個(gè)月前我還...
    沈念sama閱讀 45,595評(píng)論 2 355
  • 正文 我出身青樓漓拾,卻偏偏與公主長(zhǎng)得像阁最,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子骇两,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,901評(píng)論 2 345

推薦閱讀更多精彩內(nèi)容