redis常用功能表述

基礎(chǔ)類型應(yīng)用:

String:

String類型是Redis中最常使用的類型,內(nèi)部的實(shí)現(xiàn)是通過SDS(Simple Dynamic String )來存儲(chǔ)的。SDS 類似于Java中的ArrayList,可以通過預(yù)分配冗余空間的方式來減少內(nèi)存的頻繁分配。

這是最簡(jiǎn)單的類型,就是普通的 set 和 get,做簡(jiǎn)單的 KV 緩存。

但是真實(shí)的開發(fā)環(huán)境中,很多仔可能會(huì)把很多比較復(fù)雜的結(jié)構(gòu)也統(tǒng)一轉(zhuǎn)成String去存儲(chǔ)使用,比如有的仔他就喜歡把對(duì)象或者List轉(zhuǎn)換為JSONString進(jìn)行存儲(chǔ),拿出來再反序列話啥的。

String的實(shí)際應(yīng)用場(chǎng)景比較廣泛的有:

緩存功能:String字符串是最常用的數(shù)據(jù)類型,不僅僅是Redis,各個(gè)語言都是最基本類型,因此,利用Redis作為緩存,配合其它數(shù)據(jù)庫作為存儲(chǔ)層,利用Redis支持高并發(fā)的特點(diǎn),可以大大加快系統(tǒng)的讀寫速度、以及降低后端數(shù)據(jù)庫的壓力。

計(jì)數(shù)器:許多系統(tǒng)都會(huì)使用Redis作為系統(tǒng)的實(shí)時(shí)計(jì)數(shù)器,可以快速實(shí)現(xiàn)計(jì)數(shù)和查詢的功能。而且最終的數(shù)據(jù)結(jié)果可以按照特定的時(shí)間落地到數(shù)據(jù)庫或者其它存儲(chǔ)介質(zhì)當(dāng)中進(jìn)行永久保存。

共享用戶Session:用戶重新刷新一次界面,可能需要訪問一下數(shù)據(jù)進(jìn)行重新登錄,或者訪問頁面緩存Cookie,但是可以利用Redis將用戶的Session集中管理,在這種模式只需要保證Redis的高可用,每次用戶Session的更新和獲取都可以快速完成。大大提高效率。

Hash:

這個(gè)是類似Map的一種結(jié)構(gòu),這個(gè)一般就是可以將結(jié)構(gòu)化的數(shù)據(jù),比如一個(gè)對(duì)象(前提是這個(gè)對(duì)象沒嵌套其他的對(duì)象)給緩存在Redis里,然后每次讀寫緩存的時(shí)候,可以就操作Hash里的某個(gè)字段

但是這個(gè)的場(chǎng)景其實(shí)還是多少單一了一些,因?yàn)楝F(xiàn)在很多對(duì)象都是比較復(fù)雜的,比如你的商品對(duì)象可能里面就包含了很多屬性,其中也有對(duì)象。我自己使用的場(chǎng)景用得不是那么多。

List:

List是有序列表,這個(gè)還是可以玩兒出很多花樣的。

比如可以通過List存儲(chǔ)一些列表型的數(shù)據(jù)結(jié)構(gòu),類似粉絲列表、文章的評(píng)論列表之類的東西。

比如可以通過lrange命令,讀取某個(gè)閉區(qū)間內(nèi)的元素,可以基于List實(shí)現(xiàn)分頁查詢,這個(gè)是很棒的一個(gè)功能,基于Redis實(shí)現(xiàn)簡(jiǎn)單的高性能分頁,可以做類似微博那種下拉不斷分頁的東西,性能高,就一頁一頁走。

比如可以搞個(gè)簡(jiǎn)單的消息隊(duì)列,從List頭懟進(jìn)去,從List屁股那里弄出來。

List本身就是我們?cè)陂_發(fā)過程中比較常用的數(shù)據(jù)結(jié)構(gòu)了,熱點(diǎn)數(shù)據(jù)更不用說了。

消息隊(duì)列:Redis的鏈表結(jié)構(gòu),可以輕松實(shí)現(xiàn)阻塞隊(duì)列,可以使用左進(jìn)右出的命令組成來完成隊(duì)列的設(shè)計(jì)。比如:數(shù)據(jù)的生產(chǎn)者可以通過Lpush命令從左邊插入數(shù)據(jù),多個(gè)數(shù)據(jù)消費(fèi)者,可以使用BRpop命令阻塞的“搶”列表尾部的數(shù)據(jù)。

文章列表或者數(shù)據(jù)分頁展示的應(yīng)用。

比如,我們常用的博客網(wǎng)站的文章列表,當(dāng)用戶量越來越多時(shí),而且每一個(gè)用戶都有自己的文章列表,而且當(dāng)文章多時(shí),都需要分頁展示,這時(shí)可以考慮使用Redis的列表,列表不但有序同時(shí)還支持按照范圍內(nèi)獲取元素,可以完美解決分頁查詢功能。大大提高查詢效率。

Set:

Set是無序集合,會(huì)自動(dòng)去重的那種。

直接基于Set將系統(tǒng)里需要去重的數(shù)據(jù)扔進(jìn)去,自動(dòng)就給去重了,如果你需要對(duì)一些數(shù)據(jù)進(jìn)行快速的全局去重,你當(dāng)然也可以基于JVM內(nèi)存里的HashSet進(jìn)行去重,但是如果你的某個(gè)系統(tǒng)部署在多臺(tái)機(jī)器上呢?得基于Redis進(jìn)行全局的Set去重。

可以基于Set玩兒交集、并集、差集的操作,比如交集吧,我們可以把兩個(gè)人的好友列表整一個(gè)交集,看看倆人的共同好友是誰?對(duì)吧。

反正這些場(chǎng)景比較多,因?yàn)閷?duì)比很快,操作也簡(jiǎn)單,兩個(gè)查詢一個(gè)Set搞定。

Sorted Set:

Sorted set是排序的Set,去重但可以排序,寫進(jìn)去的時(shí)候給一個(gè)分?jǐn)?shù),自動(dòng)根據(jù)分?jǐn)?shù)排序。

有序集合的使用場(chǎng)景與集合類似,但是set集合不是自動(dòng)有序的,而Sorted set可以利用分?jǐn)?shù)進(jìn)行成員間的排序,而且是插入時(shí)就排序好。所以當(dāng)你需要一個(gè)有序且不重復(fù)的集合列表時(shí),就可以選擇Sorted set數(shù)據(jù)結(jié)構(gòu)作為選擇方案。

排行榜:有序集合經(jīng)典使用場(chǎng)景。例如視頻網(wǎng)站需要對(duì)用戶上傳的視頻做排行榜,榜單維護(hù)可能是多方面:按照時(shí)間、按照播放量、按照獲得的贊數(shù)等。

Sorted Sets來做帶權(quán)重的隊(duì)列,比如普通消息的score為1,重要消息的score為2,然后工作線程可以選擇按score的倒序來獲取工作任務(wù)。讓重要的任務(wù)優(yōu)先執(zhí)行。

微博熱搜榜,就是有個(gè)后面的熱度值,前面就是名稱

高級(jí)用法:

Bitmap:

位圖是支持按 bit 位來存儲(chǔ)信息,可以用來實(shí)現(xiàn)布隆過濾器(BloomFilter);

HyperLogLog:

供不精確的去重計(jì)數(shù)功能,比較適合用來做大規(guī)模數(shù)據(jù)的去重統(tǒng)計(jì),例如統(tǒng)計(jì) UV;

Geospatial:

可以用來保存地理位置,并作位置距離計(jì)算或者根據(jù)半徑計(jì)算位置等。有沒有想過用Redis來實(shí)現(xiàn)附近的人?或者計(jì)算最優(yōu)地圖路徑?

這三個(gè)其實(shí)也可以算作一種數(shù)據(jù)結(jié)構(gòu),不知道還有多少朋友記得,我在夢(mèng)開始的地方,Redis基礎(chǔ)中提到過,你如果只知道五種基礎(chǔ)類型那只能拿60分,如果你能講出高級(jí)用法,那就覺得你有點(diǎn)東西。

pub/sub:

功能是訂閱發(fā)布功能,可以用作簡(jiǎn)單的消息隊(duì)列。

Pipeline:

可以批量執(zhí)行一組指令,一次性返回全部結(jié)果,可以減少頻繁的請(qǐng)求應(yīng)答。

Lua:

Redis支持提交Lua腳本來執(zhí)行一系列的功能。

我在前電商老東家的時(shí)候,秒殺場(chǎng)景經(jīng)常使用這個(gè)東西,講道理有點(diǎn)香,利用他的原子性。

話說你們想看秒殺的設(shè)計(jì)么?我記得我面試好像每次都問啊,想看的直接點(diǎn)贊后評(píng)論秒殺吧。



緩存常見問題

緩存更新方式

這是決定在使用緩存時(shí)就該考慮的問題。

緩存的數(shù)據(jù)在數(shù)據(jù)源發(fā)生變更時(shí)需要對(duì)緩存進(jìn)行更新,數(shù)據(jù)源可能是 DB,也可能是遠(yuǎn)程服務(wù)。更新的方式可以是主動(dòng)更新。數(shù)據(jù)源是 DB 時(shí),可以在更新完 DB 后就直接更新緩存。

當(dāng)數(shù)據(jù)源不是 DB 而是其他遠(yuǎn)程服務(wù),可能無法及時(shí)主動(dòng)感知數(shù)據(jù)變更,這種情況下一般會(huì)選擇對(duì)緩存數(shù)據(jù)設(shè)置失效期,也就是數(shù)據(jù)不一致的最大容忍時(shí)間。

這種場(chǎng)景下,可以選擇失效更新,key 不存在或失效時(shí)先請(qǐng)求數(shù)據(jù)源獲取最新數(shù)據(jù),然后再次緩存,并更新失效期。

但這樣做有個(gè)問題,如果依賴的遠(yuǎn)程服務(wù)在更新時(shí)出現(xiàn)異常,則會(huì)導(dǎo)致數(shù)據(jù)不可用。改進(jìn)的辦法是異步更新,就是當(dāng)失效時(shí)先不清除數(shù)據(jù),繼續(xù)使用舊的數(shù)據(jù),然后由異步線程去執(zhí)行更新任務(wù)。這樣就避免了失效瞬間的空窗期。另外還有一種純異步更新方式,定時(shí)對(duì)數(shù)據(jù)進(jìn)行分批更新。實(shí)際使用時(shí)可以根據(jù)業(yè)務(wù)場(chǎng)景選擇更新方式。

數(shù)據(jù)不一致

第二個(gè)問題是數(shù)據(jù)不一致的問題,可以說只要使用緩存,就要考慮如何面對(duì)這個(gè)問題。緩存不一致產(chǎn)生的原因一般是主動(dòng)更新失敗,例如更新 DB 后,更新Redis因?yàn)榫W(wǎng)絡(luò)原因請(qǐng)求超時(shí);或者是異步更新失敗導(dǎo)致。

解決的辦法是,如果服務(wù)對(duì)耗時(shí)不是特別敏感可以增加重試;如果服務(wù)對(duì)耗時(shí)敏感可以通過異步補(bǔ)償任務(wù)來處理失敗的更新,或者短期的數(shù)據(jù)不一致不會(huì)影響業(yè)務(wù),那么只要下次更新時(shí)可以成功,能保證最終一致性就可以。

MySQL 里有 2000w 數(shù)據(jù),redis 中只存 20w 的數(shù)據(jù),如何保證redis 中的數(shù)據(jù)都是熱點(diǎn)數(shù)據(jù)?

答: Redis 內(nèi)存數(shù)據(jù)集大小上升到一定大小的時(shí)候, 就會(huì)施行數(shù)據(jù)淘汰策略。相關(guān)知識(shí): Redis 提供 6 種數(shù)據(jù)淘汰策略:

volatile-lru:從已設(shè)置過期時(shí)間的數(shù)據(jù)集( server.db[i].expires)中挑選最近最

少使用的數(shù)據(jù)淘汰

volatile-ttl: 從已設(shè)置過期時(shí)間的數(shù)據(jù)集( server.db[i].expires) 中挑選將要過期的數(shù)據(jù)淘汰

volatile-random: 從已設(shè)置過期時(shí)間的數(shù)據(jù)集( server.db[i].expires) 中任意選擇數(shù)據(jù)淘汰

allkeys-lru: 從數(shù)據(jù)集( server.db[i].dict) 中挑選最近最少使用的數(shù)據(jù)淘汰

allkeys-random: 從數(shù)據(jù)集( server.db[i].dict) 中任意選擇數(shù)據(jù)淘汰

no-enviction( 驅(qū)逐) : 禁止驅(qū)逐數(shù)據(jù)


--------------------

Redis使用規(guī)范(有的面試官會(huì)問到這個(gè).可以看一看)

一、鍵名設(shè)計(jì)

1、key名設(shè)計(jì)

禁止包含特殊字符(比如空格、換行、單雙引號(hào)以及其他轉(zhuǎn)義字符)

建議以業(yè)務(wù)名為前綴,以冒號(hào)分割來構(gòu)造一定規(guī)則的key名(比如業(yè)務(wù)名:表名:id)

比如:teach:leeson_id:21

控制key的長(zhǎng)度

key太長(zhǎng)量一大起來就會(huì)非常占用內(nèi)存

2、value設(shè)計(jì)

拒絕大key操作

禁用超過10K的string大key(雖然redis支持512MB大小的string),如果1mb的key每秒重復(fù)寫入10次,就會(huì)導(dǎo)致寫入網(wǎng)絡(luò)IO達(dá)10MB。

錯(cuò)誤示范:直接將laravel的整個(gè)模型或者對(duì)象當(dāng)成value存儲(chǔ)

設(shè)計(jì)key時(shí)使用合適的數(shù)據(jù)類型(在資源利用和性能之間作平衡)

錯(cuò)誤示范:一個(gè)普通字符串弄成hash類型進(jìn)行存儲(chǔ)

一定要控制key的生命周期

錯(cuò)誤示范:key設(shè)置為永不過期

控制value長(zhǎng)度

比如string類型,如果value為'8個(gè)字節(jié)的長(zhǎng)整型'則內(nèi)部使用int類型,如果value為'小于等于39個(gè)字節(jié)的字符串'則內(nèi)部使用embstr類型,如果value為'大于39個(gè)字節(jié)的字符串'則內(nèi)部使用raw類型。這樣能很好的利用redis的性能。

數(shù)據(jù)按需存儲(chǔ)

不需要的數(shù)據(jù)千萬不要存儲(chǔ)在redis,只會(huì)浪費(fèi)內(nèi)存空間

二、命令使用

禁止使用keys、flushall、hmgetall等命令

為防止業(yè)務(wù)研發(fā)的誤操作,通??梢栽诮桓秗edis實(shí)例之前將默認(rèn)命令rename掉;而真正需要?jiǎng)h除或者遍歷key時(shí)可以使用scan家族命令

慎用hgetall、lrange、smembers、zrange等命令

除非業(yè)務(wù)場(chǎng)景需要,盡量不要使用這些命令。如果沒有控制好會(huì)導(dǎo)致操作量過大,形成阻塞。

三、緩存設(shè)計(jì)

多個(gè)庫的使用

如果應(yīng)用中會(huì)涉及到各種不同的redis數(shù)據(jù)存儲(chǔ),應(yīng)該分庫存儲(chǔ),最好是一種業(yè)務(wù)使用一個(gè)庫

比如:課程緩存:庫1;訂單隊(duì)列:庫2;日志處理:庫3

避免多個(gè)應(yīng)用公用一個(gè)redis實(shí)例

避免一個(gè)應(yīng)用出現(xiàn)問題或者錯(cuò)誤使用拖累其他應(yīng)用

合理評(píng)估業(yè)務(wù)場(chǎng)景,并設(shè)置最大內(nèi)存以及內(nèi)存淘汰策略(maxmemory和maxmemory-policy)

目前我們用的阿里云redis,不太存在這個(gè)問題

使用帶有連接池的數(shù)據(jù)庫,可以有效控制連接,同時(shí)提高效率

給redis設(shè)置一個(gè)密碼

目前我們用的阿里云redis,不太存在這個(gè)問題

冷熱數(shù)據(jù)區(qū)分

雖然?Redis支持持久化,但將所有數(shù)據(jù)存儲(chǔ)在redis中,成本非常昂貴。

建議將熱數(shù)據(jù)?(如?QPS超過?5k)?的數(shù)據(jù)加載到redis中。

低頻數(shù)據(jù)可存儲(chǔ)在Mysql、ElasticSearch中。

緩存非特殊情況不做中間態(tài)

redis大多數(shù)時(shí)候都是做緩存用,去掉后業(yè)務(wù)邏輯不應(yīng)發(fā)生改變,萬不可切入到業(yè)務(wù)里。

第一,緩存的高可用會(huì)影響業(yè)務(wù);

第二,產(chǎn)生深耦合會(huì)發(fā)生無法預(yù)料的效果;

第三,會(huì)對(duì)維護(hù)產(chǎn)生負(fù)效果。

四、場(chǎng)景實(shí)戰(zhàn)問題

1、項(xiàng)目redis使用問題

當(dāng)前的使用方式是,每個(gè)接入的應(yīng)用要配置核心項(xiàng)目的redis配置。這樣是不合理的,核心項(xiàng)目的redis應(yīng)該只能在核心項(xiàng)目中使用,對(duì)外應(yīng)該是提供api接口或者rpc進(jìn)行訪問。

2、慎用laravel自帶的cache功能

laravel自帶的cache功能最容易導(dǎo)致大key,經(jīng)常由于簡(jiǎn)單使用至今將整個(gè)對(duì)象模型存儲(chǔ)到redis,造成大key。

3、注意key的過期時(shí)間設(shè)置

在報(bào)名等高峰期的時(shí)候,key值設(shè)置過短容易造成緩存穿透,導(dǎo)致大量請(qǐng)求直接打到mysql數(shù)據(jù)庫。

4、小心緩存穿透

經(jīng)常使用會(huì)只給有數(shù)據(jù)的結(jié)果進(jìn)行緩存,結(jié)果導(dǎo)致空數(shù)據(jù)無法緩存,相同查詢直接每次都到達(dá)數(shù)據(jù)庫,所以空值也應(yīng)該被緩存。

5、慎用緩存層層包裹

緩存里面的數(shù)據(jù)還有一層緩存數(shù)據(jù),會(huì)導(dǎo)致問題排查麻煩,出問題也不容易處理。

6、慎用將redis做為消息隊(duì)列

如沒有非常特殊的需求,嚴(yán)禁將 Redis 當(dāng)作消息隊(duì)列使用。redis 當(dāng)作消息隊(duì)列使用,會(huì)有容量、網(wǎng)絡(luò)、效率、功能方面的多種問題。

如需要消息隊(duì)列,可使用高吞吐的?Kafka?或者高可靠的?RocketMQ,nsq,(花園同步有時(shí)間前后要求,且量不大才使用的)。

五、查詢使用問題

1、線上Redis禁止使用Keys正則匹配操作

redis是單線程處理,在線上Key數(shù)量較多時(shí),操作效率極低【時(shí)間復(fù)雜度為O(N)】,該命令一旦執(zhí)行會(huì)嚴(yán)重阻塞線上其它命令的正常請(qǐng)求,而且在高QPS情況下會(huì)直接造成redis服務(wù)崩潰!如果有類似需求,請(qǐng)使用scan命令代替。

六、其他

1、redis同步工具

阿里云的redis-shake工具,方便快速

2、大key查詢

阿里云有大key分析工具


redis集群原理:

Redis集群

Redis 集群是一個(gè)可以在多個(gè) Redis 節(jié)點(diǎn)之間進(jìn)行數(shù)據(jù)共享的設(shè)施installation。

Redis 集群不支持那些需要同時(shí)處理多個(gè)鍵的 Redis 命令, 因?yàn)閳?zhí)行這些命令需要在多個(gè) Redis 節(jié)點(diǎn)之間移動(dòng)數(shù)據(jù), 并且在高負(fù)載的情況下, 這些命令將降低Redis集群的性能, 并導(dǎo)致不可預(yù)測(cè)的行為。

Redis 集群通過分區(qū)partition來提供一定程度的可用性availability: 即使集群中有一部分節(jié)點(diǎn)失效或者無法進(jìn)行通訊, 集群也可以繼續(xù)處理命令請(qǐng)求。

Redis集群提供了以下兩個(gè)好處:

將數(shù)據(jù)自動(dòng)切分split到多個(gè)節(jié)點(diǎn)的能力。

當(dāng)集群中的一部分節(jié)點(diǎn)失效或者無法進(jìn)行通訊時(shí), 仍然可以繼續(xù)處理命令請(qǐng)求的能力。



原理

所有的redis節(jié)點(diǎn)彼此互聯(lián)(PING-PONG機(jī)制),內(nèi)部使用二進(jìn)制協(xié)議優(yōu)化傳輸速度和帶寬。

節(jié)點(diǎn)的fail是通過集群中超過半數(shù)的節(jié)點(diǎn)檢測(cè)失效時(shí)才生效。

客戶端與redis節(jié)點(diǎn)直連,不需要中間proxy層.客戶端不需要連接集群所有節(jié)點(diǎn),連接集群中任何一個(gè)可用節(jié)點(diǎn)即可。

redis-cluster把所有的物理節(jié)點(diǎn)映射到[0-16383]slot上,cluster 負(fù)責(zé)維護(hù)node<->slot<->value

Redis集群中內(nèi)置了 16384 個(gè)哈希槽,當(dāng)需要在 Redis 集群中放置一個(gè) key-value 時(shí),redis 先對(duì)key 使用 crc16 算法算出一個(gè)結(jié)果,然后把結(jié)果對(duì) 16384 求余數(shù),這樣每個(gè) key 都會(huì)對(duì)應(yīng)一個(gè)編號(hào)在 0-16383 之間的哈希槽,redis 會(huì)根據(jù)節(jié)點(diǎn)數(shù)量大致均等的將哈希槽映射到不同的節(jié)點(diǎn)

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容