詳解Redis終端操作和Redis-plus-plus接口使用教程
redis簡(jiǎn)介
- 相比如mysql,redis也可以當(dāng)作數(shù)據(jù)庫(kù)來(lái)使用,而且更快,因?yàn)槭莾?nèi)存中的數(shù)據(jù)庫(kù)。但是和mysql相比,具有存儲(chǔ)空間小
的問題,因?yàn)閮?nèi)存空間遠(yuǎn)小于硬盤空間,同時(shí)也是因?yàn)檫@個(gè)問題,redis的功能相比mysql要少,比如不存在多表查詢等等操作 - “二八原則”:20%的數(shù)據(jù)滿足80%的訪問需求,典型的方案:redis結(jié)合mysql使用,使用redis存儲(chǔ)經(jīng)常使用的熱點(diǎn)數(shù)據(jù),剩下的數(shù)據(jù)存儲(chǔ)在mysql中,只在需要的時(shí)候去取。其中涉及系統(tǒng)中復(fù)雜度的增加、redis和mysql數(shù)據(jù)的同步問題等
- redis最初是想作為一個(gè)消息隊(duì)列(實(shí)現(xiàn)分布式系統(tǒng)中的生產(chǎn)者、消費(fèi)者模型)
- msyql數(shù)據(jù)庫(kù)的主從分離與讀寫分離:讀的需求是要遠(yuǎn)大于寫的需求的,所以只有主庫(kù)接受寫的請(qǐng)求,并將發(fā)生改變的內(nèi)容同步到從庫(kù),從庫(kù)不負(fù)責(zé)處理寫的請(qǐng)求,負(fù)責(zé)讀的請(qǐng)求,上層也可以引入負(fù)載均衡的策略對(duì)讀節(jié)點(diǎn)進(jìn)行選擇
redis存放、獲取鍵值的操作
1. 存放鍵值對(duì)
(1) redis中,key都是字符串,value可以是各種類型。存放鍵值對(duì),使用set keyname keyvalue;redis不區(qū)分大小寫,也不需要
注意單雙引號(hào)的問題,寫不寫都可以。
(2) set keyname value [ex seconds | px milliseconds] [nx|xx],ex指定超時(shí)時(shí)間以秒為單位,px以毫秒為單位;nx表示有設(shè)置keyname則不插入,xx表示只在keyname存在時(shí)修改value
(3) mset keyname value [keyname value] 設(shè)置多組鍵值對(duì)
(4) setnx setex psetex即設(shè)置時(shí)就加上選項(xiàng),setnx表示只在不存在時(shí)設(shè)置,setex表示設(shè)置過期時(shí)間,秒為單位,psetex也是設(shè)置過期時(shí)間,但是以毫秒為單位
2. 獲取鍵值對(duì)
(1) redis中,獲取鍵值對(duì)使用get,通過get keyname的方式獲取
(2) mget,獲取多組鍵值對(duì)
獲取鍵值對(duì)時(shí)的匹配原則
?表示任意一個(gè)字符,如key可由ke?檢索出來(lái)*表示任意多個(gè)字符,使用其實(shí)和Linux檢索文件名時(shí)使用一樣[abcdefg]表示從中括號(hào)中的選項(xiàng)中匹配任意一個(gè)[^e]表示不匹配e[a-c]表示范圍匹配
但是實(shí)際開發(fā)中,幾乎不會(huì)使用(禁止)*的方式,因?yàn)闀?huì)檢索整個(gè)redis中的所有key,而redis又是單線程的,
所以可能會(huì)導(dǎo)致該線程阻塞在檢索的過程中,導(dǎo)致大量請(qǐng)求從redis的緩存數(shù)據(jù)中取不出結(jié)果,都去向數(shù)據(jù)庫(kù)發(fā)出請(qǐng)求,
導(dǎo)致數(shù)據(jù)庫(kù)壓力過大掛掉
3. 查看鍵值對(duì)是否存在
查看一個(gè)鍵值對(duì)(多個(gè))是否存在,使用exists keyname1 keyname2 ...
關(guān)于同時(shí)支持多個(gè)keyname的問題:redis是客戶端、服務(wù)器形式的應(yīng)用,所以對(duì)于服務(wù)器的請(qǐng)求操作都要通過網(wǎng)絡(luò)進(jìn)行,這意味著經(jīng)由網(wǎng)絡(luò)傳輸、各個(gè)層級(jí)之間協(xié)議的封裝與解包,都是較大的時(shí)間消耗,所以redis支持一次請(qǐng)求攜帶多個(gè)key值去操作,以節(jié)省傳輸?shù)臅r(shí)間(網(wǎng)絡(luò)的速度一般網(wǎng)卡和硬盤速度對(duì)比還是網(wǎng)絡(luò)慢很多的
4. 刪除鍵值對(duì)
使用del刪除鍵值對(duì),del keyname1 keyname2
5. 設(shè)置過期時(shí)間
- 秒為單位,
expire keyname seconds - 毫秒為單位,
pexpire keyname microseconds
6. 查看存活時(shí)間
ttl(time to live) + keyname,查看剩余存活時(shí)間,如果沒有設(shè)置過期時(shí)間,那么就返回-1;如果查無(wú)此key,
就返回-2
Redis怎樣刪除已經(jīng)過期的key呢?
- 定期刪除。定期查看一部分key是否過期,這個(gè)過程很快(因?yàn)閞edis是單線程的,耗費(fèi)時(shí)間過長(zhǎng)就會(huì)導(dǎo)致類似于前面說的*通配符使用的問題
- 惰性刪除,當(dāng)一個(gè)key過期了,也不進(jìn)行刪除,而是在使用設(shè)個(gè)key的時(shí)候,發(fā)現(xiàn)這個(gè)key過期了,然后再刪除
- Redis其實(shí)并沒有使用定時(shí)器的方式去刪除過期的key,有些猜測(cè)是早期的redis是單線程的,便奠定了單線程的基調(diào),后面也維持了這種風(fēng)格,但是定時(shí)器的實(shí)現(xiàn)方案,也是值得了解的,有優(yōu)先級(jí)隊(duì)列和時(shí)間輪兩種方式
優(yōu)先級(jí)隊(duì)列
- 將有過期時(shí)間的任務(wù)和過期時(shí)間放在一個(gè)根據(jù)過期時(shí)間建立的小根堆當(dāng)中,根據(jù)堆頂元素的過期時(shí)間,去讓
線程休眠一段時(shí)間,然后喚醒線程節(jié)省cpu資源;如果期間有新的事件進(jìn)入隊(duì)列,那么就再次喚醒線程檢查是否需要
處理堆頂事件
時(shí)間輪
首先搭建一個(gè)環(huán)形隊(duì)列,其中每個(gè)節(jié)點(diǎn)都指向一個(gè)鏈表的頭節(jié)點(diǎn),從頭節(jié)點(diǎn)往后的每個(gè)節(jié)點(diǎn)都代表過期時(shí)間處在
某個(gè)范圍的事件,讓檢測(cè)線程每個(gè)一段事件往后走一個(gè)檢查該格中是否有需要處理的事件(結(jié)合周期和環(huán)形隊(duì)列
循環(huán)的概念,不難得出一個(gè)鏈表中可能有多種時(shí)間),但是這種方式對(duì)于時(shí)間粒度和環(huán)形隊(duì)列的長(zhǎng)度的設(shè)計(jì)
都有很高的要求
7. 查看類型
使用type + keyname查看key對(duì)應(yīng)的value的類型
redis數(shù)據(jù)類型與實(shí)現(xiàn)方式
很多類型,redis承諾對(duì)外是某種類型,但是具體實(shí)現(xiàn)的時(shí)候可能采用了其他的方式
1. string
(1) raw,就是字符串的形式存儲(chǔ)
(2) int,如果字符串中存儲(chǔ)的是數(shù)字的話,就直接作為數(shù)字存儲(chǔ)
(3) embstr,對(duì)于短字符串做出優(yōu)化的存儲(chǔ)方式,也負(fù)責(zé)存儲(chǔ)小數(shù),這也意味著性能的損失,因?yàn)檎麛?shù)通過int的方式存儲(chǔ)直接就可以
比較,但是通過embstr的話就要轉(zhuǎn)化為數(shù)字之后再進(jìn)行比較
2. hash
(1) hashtable,以redis自己的方式做的哈希表
(2) ziplist,壓縮表,當(dāng)value的類型是哈希表但是其中的元素又很少時(shí),就可以采用ziplist來(lái)遍歷(因?yàn)樵厣偎詴r(shí)間差異不大)壓縮表通過內(nèi)部的優(yōu)化,將數(shù)據(jù)按照更緊湊的方式進(jìn)行表示,當(dāng)數(shù)據(jù)增多時(shí),會(huì)導(dǎo)致效率下降
3. list
(1) 以前的版本采用的是linkedlist + ziplist實(shí)現(xiàn),redis3.2以后,采取quicklist的實(shí)現(xiàn)方式,類似于std::deque
4. set
(1) hashtable (2) intset整數(shù)集合,當(dāng)集合中數(shù)據(jù)較少而且只存儲(chǔ)整數(shù)的時(shí)候,就是用這種結(jié)構(gòu)
5. zset
(1) skiplist,跳表,類似于鏈表,但是每個(gè)節(jié)點(diǎn)有多個(gè)指針域,使之能夠?qū)崿F(xiàn)logN的查詢效率
(2) ziplist,元素個(gè)數(shù)比較少或者元素大小比較小
查看具體存儲(chǔ)方式
object encoding keyname,查看某個(gè)value具體的存儲(chǔ)方式
redis的單線程工作模型
- redis的客戶端發(fā)送請(qǐng)求是并發(fā)的,但是redis服務(wù)器的核心工作邏輯是單線程的,這樣請(qǐng)求也就再redis服務(wù)器上串行執(zhí)行,
沒有并發(fā)的問題 - 所以說一方面,如果一個(gè)操作阻塞過久,就可能導(dǎo)致redis服務(wù)端掛掉;另一方面,也不是所有業(yè)務(wù)場(chǎng)景都適合單線程,因?yàn)?br />redis需要處理的業(yè)務(wù)基本上都是短平快的,對(duì)于cpu并行的要求很低,也就可以采用單線程的方式處理核心業(yè)務(wù)
為什么說redis快,效率高呢?
- 首先redis效率高是和mysql這種關(guān)系型數(shù)據(jù)庫(kù)相比,因?yàn)楹笳呤菍?duì)磁盤的操作,前者是對(duì)內(nèi)存的操作,硬件上的操作速度
就差了幾個(gè)數(shù)量級(jí),因此redis自然更快 - redis提供的功能相對(duì)要少一些,比如mysql插入的時(shí)候如果有約束的話還要射擊先查詢的過程,查詢的時(shí)候還有多表聯(lián)查
等等操作,在功能和業(yè)務(wù)上的區(qū)別,也是造成差異的原因
string 類型
1. incr和incrby
(1) incr,表示給相應(yīng)value + 1,只有實(shí)際類型是int的才可以
(2) incrby,表示給相應(yīng)value + n,n必須是整數(shù),可以是負(fù)數(shù)
(3) 如果key不存在,就從零開始處理
2. decr和decrby
(1) 除了進(jìn)行的是減操作以外,其他的都是和incr與incrby一樣的
3. incrbyfloat
可以進(jìn)行小數(shù)和整數(shù)的操作,如果需要減操作,使用負(fù)數(shù)即可
4. append
(1) 對(duì)字符串進(jìn)行追加操作,并返回追加之后的長(zhǎng)度
(2) 對(duì)于redis來(lái)說,不會(huì)對(duì)我們的數(shù)據(jù)進(jìn)行字符集上的處理,因此輸入中文的話默認(rèn)打出的就是原編碼,所以可以在啟動(dòng)客戶端
的時(shí)候加上–raw指令,讓客戶端嘗試解讀返回的二進(jìn)制數(shù)據(jù)并展示,就能夠完成中文的顯示了
5. getrange
(1) getrange keyname start end,表示獲取[start, end]的內(nèi)容,以字節(jié)為單位,這意味著可能會(huì)出現(xiàn)中文被解析成亂碼的情況
,因?yàn)闊o(wú)論是utf8還是gbk都是多字節(jié)編碼一個(gè)中文字符
(2) 當(dāng)end是負(fù)數(shù)時(shí),表示倒數(shù)第幾個(gè)字符,例如-1表示倒數(shù)第一個(gè)字符
(3) 根據(jù)兩個(gè)端點(diǎn)的情況,redis會(huì)自動(dòng)截取內(nèi)容(數(shù)字同理),但是如果最后出現(xiàn)start > end時(shí),會(huì)導(dǎo)致解析不出內(nèi)容
6. setrange
(1) setrange key start value,設(shè)置從start開始的內(nèi)容為value,具體設(shè)置到哪里取決于value的長(zhǎng)度
(2) 如果設(shè)置的key不存在,那么就根據(jù)start的值,用空格填充前面的內(nèi)容;如果start超過了當(dāng)前內(nèi)容的長(zhǎng)度,也是同理往后
添加用空格補(bǔ)全長(zhǎng)度
7. strlen
(1) 獲取字符串類型的value的長(zhǎng)度,單位是字節(jié),對(duì)于中文也是具體字節(jié)而不是字符的個(gè)數(shù)
Redis中的hash類型
redis中的hash,使用field-value表示鍵值的結(jié)構(gòu),其中,value的類型只允許是字符串
1. hset、hget、hexists、hdel
(1) hset keyname fieldname value,表示keyname用來(lái)查找這個(gè)哈希表,fieldname是哈希表的鍵值。
(2) hget keyname fieldname,獲取keyname的哈希表的fieldname所對(duì)應(yīng)的值
(3) hexists keyname fieldname,判斷keyname對(duì)應(yīng)的哈希表是否具有fieldname的鍵值
(4) hdel keyname fieldname,刪除keyname對(duì)應(yīng)的哈希表的fieldname鍵值
2. hkeys、hvals
hkeys key,獲取key對(duì)應(yīng)的哈希表中的所有field;hvals key,獲取key對(duì)應(yīng)的哈希表中的所有value值。盡量不要使用這些操作,
和keys*有異曲同工之妙
hlen、hsetnx、hincrby、hincrbyfloat
hlen keyname,用來(lái)查詢哈希表的大小hsetnx,不存在則插入,存在則失敗hincrby,哈希表中某個(gè)value + n,只適合處理數(shù)字類型的valuehincrbyfloat,哈希表中某個(gè)值增加,可以是小數(shù)
使用哈希存儲(chǔ)的場(chǎng)景
- 當(dāng)作結(jié)構(gòu)體/對(duì)象使用,存儲(chǔ)一個(gè)具有多個(gè)屬性的對(duì)象,可以節(jié)省分開存的key的個(gè)數(shù),適合存結(jié)構(gòu)化的數(shù)據(jù),而且只需要
改一個(gè)字段,不需要將整個(gè)字段讀出來(lái)再寫回去 - 與數(shù)據(jù)庫(kù)相比,使用哈希存儲(chǔ)具有稀疏性,不需要的字段就不存;但是對(duì)于數(shù)據(jù)庫(kù)而言,在一個(gè)表中,即使某個(gè)對(duì)象不具有某個(gè)值也需要用null來(lái)占位
列表
- 列表類似于順序表的使用,實(shí)現(xiàn)容器類似于deque,提升插入、刪除的效率
- 列表是有序的,意思是元素相同,順序不一樣就是兩個(gè)列表
lpush lpop rpush rpop
push_front pop_front push_back pop_back
lrem lindex lrange
lrem key count value,表示將key列表中的count個(gè)value(有多少刪多少)刪除并返回lrange key start stop,表示將key列表中[start, stop]的下標(biāo)的內(nèi)容返回(顯示查詢出結(jié)果的時(shí)候,前面帶的數(shù)字不是下標(biāo),是結(jié)果集的序號(hào)),當(dāng)下標(biāo)超出的時(shí)候會(huì)取余調(diào)整lindex key index,表示獲得key列表中下標(biāo)為index的元素
lpushx 和 rpushx
- x代表exist,如果列表存在的話插入,否則插入失敗
lpop 和 rpop
lpop keyname [count],從左端刪除,后面的count在低版本的不支持rpop keyname [count],從右端刪除- 與lpush和rpush搭配就可以當(dāng)作棧、隊(duì)列使用
linsert
linsert keyname [before|after] pivot value, pivot是從左到右尋找的一個(gè)值(如果沒有找到就插入失敗)
lrem
lrem keyname count element,count > 0時(shí),從左到右移除count個(gè)元素;count == 0,移除列表中所有element相等的元素;count < 0時(shí),從右向左移除count個(gè)元素
ltrim 和 lset
ltrim,ltrim keyname start stop截取[start, stop]的區(qū)間之后,剩下部分丟棄lset,lset keyname index element將index下標(biāo)設(shè)置為element,其中index超過范圍時(shí),會(huì)報(bào)錯(cuò)
blpop 和 brpop
- 兩者都是阻塞等待,但是在redis中,首先第一點(diǎn),這兩個(gè)阻塞是特殊的,并不會(huì)影響redis核心邏輯的工作,第二點(diǎn)就是這個(gè)阻塞隊(duì)列并不會(huì)考慮隊(duì)列滿的情況,只會(huì)考慮隊(duì)列為空的阻塞
blpop keyname timeout,0表示一直阻塞等待,以秒為單位等待- 兩者可以同時(shí)等待多個(gè)隊(duì)列,從左到右第一個(gè)有數(shù)據(jù)出隊(duì)列時(shí)返回
list列表的應(yīng)用場(chǎng)景
- 時(shí)間軸、消息軸等,通過lpush插入最新內(nèi)容,通過lrange完成分頁(yè)獲取,通過ltrim完成部分內(nèi)容的保留
- 消息隊(duì)列,通過lpush + brpop就可以完成阻塞隊(duì)列的搭建
set集合
set與列表相比,是無(wú)序的,意思是順序不重要,同時(shí)set中不允許有相同元素,但是列表可以
sadd smember sismember scard
sadd key elements往set中添加元素smembers key,獲取set中的所有元素sismember key member,判斷一個(gè)value是否是key’s set中的元素scard keyname,獲取集合中的元素個(gè)數(shù)
spop srandmember
spop keyname,從set中隨機(jī)獲取一個(gè)元素并且從set中刪除srandmember keyname,從set中隨機(jī)獲取一個(gè)元素,但是并不會(huì)刪除set中的返回元素
srem smove
srem keyname element,刪除指定set中的指定元素smove source destination element,將source set中的element移動(dòng)給element,如果移動(dòng)destination中已經(jīng)存在的元素也能移動(dòng)成功,但是發(fā)生替換
sinter sinternstore 交集
sinter setname1 setname2 ...,求出兩者的交集sinterstore destination setname1 setname2 ...,求出兩者的交集,放到destination集合中
sunion sunionstore sdiff sdiffstore
分別取求并集和差集,使用方式與求交集相同
set的應(yīng)用場(chǎng)景
- 用來(lái)存儲(chǔ)用戶畫像,通過一個(gè)一個(gè)標(biāo)簽(其實(shí)就是小字符串)的形式存儲(chǔ)在set當(dāng)中
- 好友的推薦,因?yàn)閟et非常方便求交集,所以可以快速推薦共同好友等
- UV的統(tǒng)計(jì)(對(duì)于一個(gè)產(chǎn)品的評(píng)價(jià),可以用兩種方法來(lái)統(tǒng)計(jì),一個(gè)是PV,另一個(gè)是UV;PV是page view,統(tǒng)計(jì)出用戶每次訪問服務(wù)器的次數(shù),UV是userview,統(tǒng)計(jì)有多少用戶訪問了服務(wù)器,這意味著需要大量的去重操作,也是set擅長(zhǎng)的)
zset
zset也是有序的,但是zset的有序是真正的有順序的,通過在插入時(shí)基于score的結(jié)構(gòu)實(shí)現(xiàn)。score和member一一對(duì)應(yīng),類似std::pair
zadd
zadd key [nx|xx] [gt|lt] [ch] [incr] score member [score member ...]
- 返回值的問題,zadd默認(rèn)的返回值是新增元素的個(gè)數(shù),但是如果設(shè)置了ch,就會(huì)返回修改元素的個(gè)數(shù)
- gt|lt,greater than | less than,前者是只有當(dāng)前更新的score大于原來(lái)的score時(shí)才更新,后者反之
- nx|xx,nx表示只新增不修改,xx表示只修改不新增
- incr使用類似于incrby,不過作用對(duì)象是score
zrange
zrange key start end [withscore],展示zset中的全部元素,withscores表示展示相應(yīng)元素分?jǐn)?shù)zrevrange key start end [withscore],表示逆序展示(start, range)的memberzrangebyscore key start end [withscore],表示根據(jù)score從低到高排序展示member,前面兩個(gè)start、end表示的是下標(biāo)
zcount
zcount key start end,用來(lái)統(tǒng)計(jì)某個(gè)score區(qū)間中member的個(gè)數(shù),如果要表示一個(gè)數(shù)字是開區(qū)間的一端的話就在前面加上(,例如(2, 5)表示為(2 (5- zcount支持使用正負(fù)無(wú)窮大作為最大值、最小值(-inf, inf);
zcard
zcard key,統(tǒng)計(jì)member個(gè)數(shù)
zpopmax
- zset在redis中默認(rèn)排的是升序,zpopmax會(huì)刪除score最大的member,并返回刪除的member和score
zpopmax key [count]表示連續(xù)取count個(gè)最大member,時(shí)間復(fù)雜度O(logN * M);
bzpopmax
bzpopmax key \[key...\] timeout,阻塞的取最大member,超時(shí)時(shí)間為timeout(0為一直阻塞),可以等待多個(gè)key,當(dāng)任意一個(gè)有返回時(shí),阻塞結(jié)束,因此時(shí)間復(fù)雜度是O(logN);
zpopmin bzpopmin
兩者使用和zpopmax、bapopmax相同,區(qū)別是操作的是zset中的最小值
zrank zrevrank zscore
zrank key member [withscore],查詢member在有序列表中的下標(biāo),從前往后算,就和正常數(shù)組一樣zrevrank key member withscore,查詢member在有序數(shù)組中從后往前的下標(biāo),即最后一個(gè)元素下標(biāo)為0zscore key member,查詢member的分?jǐn)?shù),為了優(yōu)化效率,redis犧牲空間,換取了該操作O(1)的時(shí)間復(fù)雜度
zrem zremrangebyrank zremrangebyscore
zrem key member,刪除member成員,時(shí)間復(fù)雜度O(logN);zremrangebyrank key start end,刪除下標(biāo)[start, end]的成員,時(shí)間復(fù)雜度O(logN + M),因?yàn)橹恍枰业揭粋€(gè)七點(diǎn)就可以往后刪除zremrangebyscore key min max,刪除分?jǐn)?shù)[min, max]的成員,支持使用’('去指定開區(qū)間
zincrby
zincrby key increment member,給member的分?jǐn)?shù)增加increment,increment可以是小數(shù),也可以是負(fù)數(shù),表示減小分?jǐn)?shù)
zinterstore
zinterstore destination keynum key \[key...\] weights \[weight...\] \[aggregate sum | min | max\]
- destination表示存儲(chǔ)交集的名稱,keynum用于指定一共有多少個(gè)key制定了要求交集的zset(類似于http協(xié)議報(bào)頭中的content-length),weights表示接下來(lái)的內(nèi)容用于指定權(quán)重,可以是整數(shù)也可以是小數(shù),其與共同的member的score相乘后得到結(jié)果作為新的score,而具體怎么作用有aggregate后的內(nèi)容決定,默認(rèn)是sum,新的score由所有的相加形成,min為所有的score的最小值,max為最大值
- 在最壞情況下,時(shí)間復(fù)雜度為
O(N*K) + O(M*log(M)),其中N為最小集合的元素個(gè)數(shù),K為求交集的元素個(gè)數(shù),M為最后交集中
元素的個(gè)數(shù)
zunionstore
zunionstore destination keynum key \[key...\] weights \[weight\] \[aggregate sum | min | max\]
- 求并集,使用方法與zinterstore類似
- 時(shí)間復(fù)雜度是
O(N) + O(M*log(M)),N是元素總個(gè)數(shù),M是并集中元素的個(gè)數(shù)
zset的應(yīng)用場(chǎng)景
- 排行榜系統(tǒng),微博熱搜、游戲天梯排行、成績(jī)排行
- 加權(quán)的排行,可以維護(hù)多個(gè)zset,按照不同的權(quán)重使用zinterstore zunionstore等
其他類型
- stream,阻塞隊(duì)列,用來(lái)讓redis作為消息隊(duì)列使用
- geospatial,用來(lái)存儲(chǔ)坐標(biāo)(經(jīng)緯度),適合存儲(chǔ)一些點(diǎn)之后,根據(jù)用戶給定的坐標(biāo),按照半徑或者矩陣區(qū)域等進(jìn)行查找
- hyperloglog,用來(lái)估算集合中的元素個(gè)數(shù),通過存儲(chǔ)元素的特征而不是元素本身的方式,最多使用12kb的空間,就能夠完成與真實(shí)數(shù)據(jù)浮動(dòng)至多0.8%的差異的數(shù)據(jù)量統(tǒng)計(jì),對(duì)于精準(zhǔn)度要求不是那么高的UV PV統(tǒng)計(jì)就可以采用hyperloglog
- bitmap,實(shí)際上是優(yōu)化過的set,既能夠存儲(chǔ)元素,有節(jié)省了一定空間(當(dāng)然沒有hyperloglog節(jié)省的多),但是只支持整形的存儲(chǔ)
bitfield,位域,可以理解成是一個(gè)字節(jié)數(shù)組,和C語(yǔ)言中的位段類似,能夠?yàn)閹讉€(gè)比特位賦予特殊的含義并進(jìn)行讀取、存儲(chǔ)、計(jì)算等操作,相對(duì)于hash String,目的仍然是省空間
漸進(jìn)式遍歷
- 不同于直接使用類似于
keys *的方式去查找整個(gè)redis中的鍵,漸進(jìn)式遍歷采取一次查找一小部分的方式來(lái)查找到需要的內(nèi)容。這樣每一次花費(fèi)的時(shí)間都較少,能夠避免redis服務(wù)器被阻塞 scan cursor [MATCH match] [COUNT count] [TYPE type],表示從cursor光標(biāo)開始,尋找count個(gè)元素。這里的cursor并不是下表的意思,而是redis服務(wù)器在每次漸進(jìn)式遍歷結(jié)束后返回的下次漸進(jìn)式遍歷的開始位置,輸入0表示從頭開始遍,返回的新位置是0表示此次遍歷結(jié)束;match表示通配符匹配,使用規(guī)則和keys的一樣;type表示檢索出的結(jié)果的類型- scan不指明的話,默認(rèn)count為10;同時(shí)檢索在redis服務(wù)器不會(huì)保留任何結(jié)果,所以檢索到一半不再進(jìn)行是可以的;漸進(jìn)式遍歷不一定是可靠的,如果出現(xiàn)遍歷過程中已經(jīng)完成遍歷的數(shù)據(jù)又發(fā)生修改的情況可能會(huì)無(wú)法從檢索結(jié)果中反饋
redis數(shù)據(jù)庫(kù)操作
- redis中也是有數(shù)據(jù)庫(kù)的概念的,但是沒有mysql中那么強(qiáng);默認(rèn)redis與16個(gè)數(shù)據(jù)庫(kù),不允許增加或刪除,默認(rèn)數(shù)據(jù)的操作都是在0號(hào)數(shù)據(jù)庫(kù)中進(jìn)行
- redis中切換庫(kù)使用
select index,表示切換0~15號(hào)數(shù)據(jù)庫(kù);使用dbsize來(lái)查看當(dāng)前數(shù)據(jù)庫(kù)中key的個(gè)數(shù);使用flushdb [async|sync]來(lái)選擇同步或者異步清空數(shù)據(jù)庫(kù);使用flushall來(lái)清空所有數(shù)據(jù)庫(kù)的所有內(nèi)容
redis自定義客戶端編寫
RESP協(xié)議
- redis客戶端和服務(wù)器之間采取的是為redis定制的RESP協(xié)議,基于TCP建立連接,采取一問一答的方式完成cs之間的信息交互
- 對(duì)于客戶端,發(fā)送請(qǐng)求通過bulk string數(shù)組將命令寫入請(qǐng)求;服務(wù)器返回結(jié)果時(shí),根據(jù)請(qǐng)求命令的返回情況,返回不同內(nèi)容
協(xié)議格式
+OK\r\n
Simple String,以 + 開頭
-ERR\r\n
Error,以 - 開頭
:1000\r\n
整形,以:開頭
$5\r\nhello\r\n
BulkString,以$開頭,相比Simple String可以傳輸二進(jìn)制數(shù)據(jù)
*2\r\n$5\r\nhelloworld$5\r\nhello\r\n
Array,以*開頭
Redis-plus-plus
聲明redis連接對(duì)象
#include <sw/redis++/redis.h>
sw::redis::Redis redis("tcp://127.0.0.1:6379");聲明ip和Redis服務(wù)器端口號(hào)
使用ping方法檢查連通性
std::string ret = redis.ping(); std::cout << ret << std::endl;
String
get、set方法
sw::redis::Redis redis("tcp://127.0.0.1:6379");
using namespace std::chrono_literals;
redis.set("key1", "val1", 0s, sw::redis::UpdateType::NO_EXIST); //0s表示不設(shè)置過期時(shí)間,沒有提供只傳遞更新策略的set函數(shù)
auto ret = redis.get("key1");
if(ret) std::cout << ret.value() << std::endl;set方法基本使用通過傳入sw::redis::StringView的key、val即可設(shè)置簡(jiǎn)單鍵值對(duì),StringView使用類似于std::string,但是他是只讀的而且效率更高;返回值的類型是sw::redis::OptionalString,其中Optional表示無(wú)效值,沒有采用std::string的原因是一方面如果直接采用對(duì)象的話,那么無(wú)法很好的表示nil,另一方面如果返回對(duì)象指針,還要設(shè)計(jì)內(nèi)存指向空間是否有效的問題。sw::redis::OptionString在結(jié)果有效時(shí),可以通過value()接口獲取返回內(nèi)容,同時(shí)失敗的情況也可通過他隱式轉(zhuǎn)換為bool來(lái)得知
set還有第三、四個(gè)參數(shù),分別用來(lái)表示過期時(shí)間和更新策略。過期時(shí)間使用std::chrono::milliseconds,更新策略分為sw::redis::UpdateType::EXIST、sw::redis::UpdateType::NOT_EXIST sw::redis::UpdateType::ALWAYS,分對(duì)應(yīng)setnx setxx 和直接set
exists和del
sw::redis::Redis redis("tcp://127.0.0.1:6379");
auto ret = redis.exists("key1");
ret = redis.del({"key1", "key2"});exists判斷一個(gè)key是否存在,存在返回1,失敗返回0del刪除key,返回刪除key的數(shù)目
keys
sw::redis::Redis redis("tcp://127.0.0.1:6379");
vector<string> ret; auto it = std::back_inserter(ret);
redis.keys("*", it);keys用來(lái)獲取所有的key,通過第一個(gè)參數(shù)的匹配規(guī)則來(lái)檢索,方法和前面命令行中介紹的五種一樣;同時(shí)為了解耦合,第二個(gè)參數(shù)要求傳入輸出型數(shù)據(jù)結(jié)構(gòu)的尾插迭代器
expire和ttl
sw::redis::Redis redis("tcp://127.0.0.1:6379");
using namespace std::chrono_literals;
bool ret = redis.expire("key", 10s); std::this_thread::sleep_for(5s);
long long len = redis.ttl("key");expire用來(lái)設(shè)置過期時(shí)間,通過std::chrono給定ttl用來(lái)判斷剩余生存時(shí)間,以秒為單位。已經(jīng)過期的key,返回值為-1;不存在的key,返回值為-2
type
sw::redis::Redis redis("tcp://127.0.0.1:6379");
redis.set("key", "val");
std::string ret = redis.type("key");mget mset
sw::redis::Redis redis("tcp://127.0.0.1:6379");
redis.mset({std::make_pair("key1", "val1"), std::make_pair("key2", "val2")});
std::vector<std::string> keys;
redis.mset(keys.begin(), keys.end());
std::vector<sw::redis::OptionalString> vals; auto bit = std::back_inserter(vals);
redis.mget({"key1", "key2"}, bit);
std::vector<std::string> vals2 = {"key1", "key2"};
redis.mget(vals2.begin(), vals2.end(), bit);mget方法支持通過初始化列表出入多個(gè)pair來(lái)表示要插入的多個(gè)鍵值對(duì),或者可以采用迭代器的方式使用string數(shù)組的起止迭代器傳參mset方法支持通過初始化列表或者sw::redis::OptionalString數(shù)組來(lái)傳入多個(gè)key,也需要在最后傳入一個(gè)尾插迭代器來(lái)帶出結(jié)果
getrange setrange
sw::redis::Redis redis("tcp://127.0.0.1:6379");
std::string ret = redis.getrange("key", 5, 10);
long long val = redis.setrange("key", 5, "ins_val");getrange返回[start, end]范圍中的內(nèi)容,返回的時(shí)普通的std::stringsetrange將從offset開始的內(nèi)容,設(shè)置為給定的字符串,并返回完成更改后新的字符串的長(zhǎng)度
incr decr
sw::redis::Redis redis("tcp://127.0.0.1:6379");
long long ret1 = redis.incr("key");
long long ret2 = redis.decr("key");incr和decr返回更改過后的值,兩者都是以一為單位;相比get方法,后者返回的是sw::redis::OptionalString,如果想要當(dāng)作數(shù)字使用的話還要轉(zhuǎn)換
list
- lpush lrange rpush rpop lpop
sw::redis::Redis server("tcp://127.0.0.1:6379");
server.lpush("list1", "1");
server.lpush("list1", {"1", "2", "3"});
std::vector<std::string> vals = "1", "2", "3";
server.lpush("list1", vals.begin(), vals.end());
std::vector<std::string> rets;
auto bit = std::back_inserter(rets);
server.lrange("list1", bit);
auto ret1 = server.rpop("list1"); auto ret2 = server.lpop();lpush和rpush都是通過先指定列表名稱,再指定元素的方式使用的。指定元素有三種方式:1. 直接給予,這樣可以給一個(gè);2. 通過初始化列表給予多個(gè)值;3. 現(xiàn)在外部容器構(gòu)造完成,然后給予起止迭代器lrange,需要給予一個(gè)尾插迭代器,來(lái)帶出遍歷結(jié)果rpop和lpop,兩個(gè)都是通過直接給予列表名稱,然后返回sw::redis::OptionalString的方式獲取兩端元素
- brpop blpop
sw::redis::Redis server("tcp://127.0.0.1:6379");
auto ret = server.blpop("list1", std::chrono::seconds(1));
if(ret)
{
std::cout << ret.value().first << ret.value().second << std::endl;
}- 兩者都是阻塞式的獲取列表收尾的元素。對(duì)于傳參,可以直接給字符串,也可以通過初始化列表,也可以通過迭代器的方式;返回結(jié)果采用的是
OptionalString+pair的方式返回,因?yàn)榭梢缘却鄠€(gè)隊(duì)列,所以既返回列表名,也返回刪除元素;也可以設(shè)置超時(shí)時(shí)間,超過仍未獲取元素就直接返回
- llen
sw::redis::Redis server("tcp://127.0.0.1:6379");
long long server.llen("list");llen用來(lái)獲取列表長(zhǎng)度
Set
- sadd smembers
sw::redis::Redis server("tcp://127.0.0.1:6379");
server.sadd("set", "1");
set<string> mems; auto in = std::inserter(mems, mems.end());
server.smembers("set", in);- Redis-plus-plus的接口風(fēng)格統(tǒng)一性是很高的。
sadd前面提到的三種方式都可以,smembers也是通過迭代器的方式將遍歷結(jié)果帶出
- sismember spop scard
sw::redis::Redis server("tcp://127.0.0.1:6379");
bool exists = server.ismember("set", "1");
auto ret = spop("set"); if(ret) std::cout << ret.value() << std::endl;
long long num = scard("set");sismember,用來(lái)判斷某個(gè)成員是否屬于某個(gè)集合spop,用來(lái)隨機(jī)返回集合中的一個(gè)元素scard,用來(lái)返回某個(gè)集合中的元素個(gè)數(shù)
Hash
- hget hset
sw::redis::Redis server("tcp://127.0.0.1:6379");
long long ret = server.hset("hash1", "1", "zhangsan");
ret = server.hset("hash1", {std::make_pair("2", "lisi"), std::make_pair("3", "wangwu")});
auto val = server.hget("hash1", "1"); if(val) std::cout << val.value() << std::endl;hset用來(lái)向哈希表中添加元素,依然可以通過直接給予、初始化列表、迭代器三種方式給予field-valuehget用獲取哈希表中field-value中的value
- hexists hdel hlen
sw::redis::Redis server("tcp://127.0.0.1:6379");
bool exists = server.hexists("hash1", "1");
long long ret = server.hdel("hash1", {"1", "2"});
ret = server.hlen("hash1");hexists用來(lái)判斷某個(gè)field是否存在hdel用來(lái)刪除多個(gè)field,并返回刪除的個(gè)數(shù)hel,返回一個(gè)哈希表中field的個(gè)數(shù)
- hmset hmget hkeys hvals
std::vector<std::string> keys;
auto it = std::back_inserter(keys);
server.hkeys("hash1", it);
std::vector<std::string> vals;
it = std::back_inserter(vals);
server.hvals("hash1", it);
server.hmget("hash1", {"3", "4", "5"}, it);hmsethmget用來(lái)獲取、設(shè)置多個(gè)元素,hkeyshvals用來(lái)獲取一個(gè)哈希表中所有的fieldvalue
ZSet
- zadd zrange
server.zadd("zset1", "zhangsan", 1);
server.zadd("zset1", {std::make_pair("lisi", 2), std::make_pair("wangwu", 3)});
std::vector<std::pair<std::string, std::string>> pairs = {
std::make_pair("zhouliu", "4"),
std::make_pair("tianqi", "5")
};
server.zadd("zset1", pairs.begin(), pairs.end());
std::vector<std::string> withnoscore;
auto it = std::back_inserter(withnoscore);
std::vector<std::pair<std::string, double>> withscore; //通過模板類型來(lái)指定是否需要帶出分?jǐn)?shù)
auto it2 = std::back_inserter(withscore);
server.zrange("zset1", 0, -1, it2);zadd,往有序列表中加入成員和分?jǐn)?shù),依然支持前面的三種方法zrange,遍歷獲取有序列表中的元素,通過不同的輸出容器來(lái)間接指定是否需要獲取分?jǐn)?shù)
- zcard zscore zrank zrem
long long ret = server.zcard("zset1");
auto score = server.zscore("zset1", "tianqi");
auto rank = server.zrank("zset1", "zhangsan");
server.zrem("zset1", "lisi");zcard,獲取有序集合中成員的個(gè)數(shù)zscore,根據(jù)成員獲取分?jǐn)?shù)zrank,根據(jù)成員獲取排名(在整個(gè)有序列表中的數(shù)組下標(biāo))zrem,刪除某個(gè)有序列表中的成員
到此這篇關(guān)于詳解Redis終端操作和Redis-plus-plus接口使用教程的文章就介紹到這了,更多相關(guān)Redis終端和Redis-plus-plus接口內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
高并發(fā)下Redis精確計(jì)數(shù)與時(shí)間窗口過期的方法詳解
在實(shí)時(shí)數(shù)據(jù)處理系統(tǒng)中,我們經(jīng)常需要統(tǒng)計(jì)某個(gè)事件在特定時(shí)間窗口內(nèi)的發(fā)生次數(shù),本文將詳細(xì)介紹如何基于 Redis 實(shí)現(xiàn)高性能、高可用的計(jì)數(shù)方案,需要的可以參考下2025-03-03
dubbo服務(wù)使用redis注冊(cè)中心的系列異常解決
這篇文章主要為大家介紹了dubbo服務(wù)在使用redis注冊(cè)中心遇到的一系列異常的解決,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步2022-03-03
Redis Scan 命令使用教程(高效遍歷海量數(shù)據(jù)的方法)
Scan 是 Redis 2.8 版本引入的迭代式遍歷命令,主要用于遍歷 Redis 中的鍵集合或集合類型(如 Hash、Set、Sorted Set)的元素,本文將從基礎(chǔ)介紹、實(shí)現(xiàn)原理、實(shí)操方式到實(shí)踐總結(jié),全面講解 Scan 命令的使用,感興趣的朋友跟隨小編一起看看吧2025-09-09
Redis權(quán)限和訪問控制的實(shí)現(xiàn)示例
Redis提供了一些機(jī)制來(lái)保護(hù)敏感數(shù)據(jù)和限制對(duì)Redis服務(wù)器的訪問,本文主要介紹了Redis權(quán)限和訪問控制的實(shí)現(xiàn)示例,具有一定的參考價(jià)值,感興趣的可以了解一下2023-12-12

