精心整理了20道Redis經(jīng)典面試題(珍藏版)

有關(guān)Redis之前有單獨寫過幾篇文章

Redis緩存穿透凝危、擊穿您单、雪崩踢代,數(shù)據(jù)庫與緩存一致性

談?wù)凴edis五種數(shù)據(jù)結(jié)構(gòu)及真實應(yīng)用場景

一文讓你明白Redis持久化(RDB砾脑、AOF)

怎么實現(xiàn)Redis的高可用兔乞?(主從、哨兵凄敢、集群)

之所以單獨寫碌冶,是因為這幾塊內(nèi)容比較大,而且也很重要涝缝,所以想寫的詳細點扑庞、深入點,讓大家在理解的基礎(chǔ)上記住它們,這讓就不容易忘記拒逮。

所以有關(guān)以上相關(guān)的面試題就不再單獨整理了,具體可以看相關(guān)文章罐氨。

  • redis是什么?
  • Redis為什么這么快?
  • 為什么Redis 6.0之后改多線程呢滩援?
  • 你了解Redis的過期策略嗎?
  • 聊聊Redis內(nèi)存淘汰策略?
  • Redis事務(wù)機制是怎樣的?
  • 說說Redis哈希槽的概念栅隐?
  • 為什么Redis Cluster會設(shè)計成16384個槽呢?
  • Redis在集群中查找key的時候,是怎么定位到具體節(jié)點的?
  • Redis底層使用的什么協(xié)議?
  • Redis的Hash沖突怎么辦?
  • Redis相比memcached有哪些優(yōu)勢租悄?
  • 有哪些辦法可以降低 Redis 的內(nèi)存使用情況呢谨究?
  • Redis中獲取海量數(shù)據(jù)的正確操作方式?
  • 如何使用Redis的性能更高泣棋?
  • 如何解決Redis的并發(fā)競爭Key問題胶哲?
  • 使用過Redis做異步隊列么,你是怎么用的潭辈?
  • 用Redis做過延時隊列嗎? 具體應(yīng)該怎么實現(xiàn)鸯屿?
  • 使用Redis統(tǒng)計網(wǎng)站的UV,應(yīng)該怎么做萎胰?
  • 什么是熱Key問題,如何解決熱key問題棚辽?

1技竟、redis是什么

Redis是C語言開發(fā)的一個開源的高性能鍵值對(key-value)的內(nèi)存數(shù)據(jù)庫,它是一種NoSQL(泛指非關(guān)系型)的數(shù)據(jù)庫屈藐。

與MySQL數(shù)據(jù)庫不同的是榔组,Redis的數(shù)據(jù)是存在內(nèi)存中的。它的讀寫速度非沉撸快搓扯,每秒支持并發(fā)10W QPS。因此Redis被廣泛應(yīng)用于緩存包归,另外锨推,Redis也經(jīng)常用來做分布式鎖,也可以用來做消息中間件等。

除此之外公壤,Redis還支持事務(wù)换可、持久化、LUA腳本厦幅、多種集群方案沾鳄。

2、Redis為什么這么快确憨?

1) 完全基于內(nèi)存存儲實現(xiàn)

完全基于內(nèi)存译荞,絕大部分請求是純粹的內(nèi)存操作,非承萜快速吞歼。數(shù)據(jù)存在內(nèi)存中,類似于HashMap塔猾,HashMap的優(yōu)勢就是查找和操作的時間復(fù)雜度都是O(1)浆熔;

2) 合理的數(shù)據(jù)編碼

Redis 支持多種數(shù)據(jù)數(shù)據(jù)類型,每種基本類型可能對多種數(shù)據(jù)編碼。什么時候,使用什么樣數(shù)據(jù)類型,使用什么樣編碼医增,是redis設(shè)計者總結(jié)優(yōu)化的結(jié)果慎皱。

3) 單線程模型

Redis是單線程模型的,而單線程避免了CPU不必要的上下文切換和競爭鎖的消耗叶骨。也正因為是單線程茫多,如果某個命令執(zhí)行過長(如keys,hgetall命令),會造成排隊阻塞。

Redis 6.0 引入了多線程提速忽刽,它的執(zhí)行命令操作內(nèi)存的仍然是個單線程的天揖。

4) 合理的線程模型

使用多路I/O復(fù)用模型,非阻塞IO跪帝;

多路I/O復(fù)用技術(shù)可以讓單個線程高效的處理多個連接請求今膊,而Redis使用epoll作為I/O多路復(fù)用技術(shù)的實現(xiàn)。并且伞剑,Redis自身的事件處理模型將epoll中的連接斑唬、讀寫、關(guān)閉都轉(zhuǎn)換為事件黎泣,不在網(wǎng)絡(luò)I/O上浪費過多的時間恕刘。

3、為什么Redis 6.0 之后改多線程呢抒倚?

Redis6.0之前褐着,Redis在處理客戶端的請求時,包括讀socket托呕、解析含蓉、執(zhí)行、寫socket等都由一個順序串行的主線程處理项郊,這就是所謂的“單線程”谴餐。

redis使用多線程并非是完全摒棄單線程,redis還是使用單線程模型來處理客戶端的請求呆抑,只是使用多線程來處理數(shù)據(jù)的讀寫和協(xié)議解析岂嗓,執(zhí)行命令還是使用單線程。

這樣做的目的是因為redis的性能瓶頸在于網(wǎng)絡(luò)IO而非CPU鹊碍,使用多線程能提升IO讀寫的效率厌殉,從而整體提高redis的性能。

4侈咕、你了解Redis的過期策略嗎?

我們在set key的時候公罕,可以給它設(shè)置一個過期時間,比如expire key 60耀销。指定這key 60s后過期楼眷,60s后redis是如何處理的?我們先來介紹幾種過期策略:

定時過期

每個設(shè)置過期時間的key都需要創(chuàng)建一個定時器,到過期時間就會立即對key進行清除罐柳。該策略可以立即清除過期的數(shù)據(jù)掌腰,對內(nèi)存很友好;但是會占用大量的CPU資源去處理過期的數(shù)據(jù)张吉,從而影響緩存的響應(yīng)時間和吞吐量齿梁。

惰性過期

只有當(dāng)訪問一個key時,才會判斷該key是否已過期肮蛹,過期則清除勺择。該策略可以最大化地節(jié)省CPU資源,卻對內(nèi)存非常不友好伦忠。極端情況可能出現(xiàn)大量的過期key沒有再次被訪問省核,從而不會被清除,占用大量內(nèi)存昆码。

定期過期

每隔一定的時間气忠,會掃描一定數(shù)量的數(shù)據(jù)庫的expires字典中一定數(shù)量的key,并清除其中已過期的key未桥。該策略是前兩者的一個折中方案笔刹。通過調(diào)整定時掃描的時間間隔和每次掃描的限定耗時芥备,可以在不同情況下使得CPU和內(nèi)存資源達到最優(yōu)的平衡效果冬耿。

Redis中同時使用了惰性過期定期過期兩種過期策略。

假設(shè)Redis當(dāng)前存放30萬個key萌壳,并且都設(shè)置了過期時間亦镶,如果你每隔100ms就去檢查這全部的key,CPU負載會特別高袱瓮,最后可能會掛掉缤骨。

因此,redis采取的是定期過期尺借,每隔100ms就隨機抽取一定數(shù)量的key來檢查和刪除的绊起。

但是呢,最后可能會有很多已經(jīng)過期的key沒被刪除燎斩。這時候虱歪,redis采用惰性刪除。在你獲取某個key的時候栅表,redis會檢查一下笋鄙,這個key如果設(shè)置了過期時間并且已經(jīng)過期了,此時就會刪除怪瓶。

但是呢萧落,如果定期刪除漏掉了很多過期的key,然后也沒走惰性刪除。就會有很多過期key積在內(nèi)存中找岖,直接會導(dǎo)致內(nèi)存爆的陨倡。或者有些時候宣增,業(yè)務(wù)量大起來了玫膀,redis的key被大量使用,內(nèi)存直接不夠了爹脾,
這個時候就需要內(nèi)存淘汰策略來保護自己了帖旨。

5、聊聊Redis內(nèi)存淘汰策略?

redis有8種內(nèi)存淘汰策略灵妨。

  • volatile-lru:當(dāng)內(nèi)存不足以容納新寫入數(shù)據(jù)時解阅,從設(shè)置了過期時間的key中使用LRU(最近最少使用)算法進行淘汰;
  • allkeys-lru:當(dāng)內(nèi)存不足以容納新寫入數(shù)據(jù)時泌霍,從所有key中使用LRU(最近最少使用)算法進行淘汰货抄;
  • volatile-lfu:4.0版本新增,當(dāng)內(nèi)存不足以容納新寫入數(shù)據(jù)時朱转,在過期的key中蟹地,使用LFU算法進行刪除key;
  • allkeys-lfu:4.0版本新增藤为,當(dāng)內(nèi)存不足以容納新寫入數(shù)據(jù)時怪与,從所有key中使用LFU算法進行淘汰;
  • volatile-random:當(dāng)內(nèi)存不足以容納新寫入數(shù)據(jù)時缅疟,從設(shè)置了過期時間的key中分别,隨機淘汰數(shù)據(jù);
  • allkeys-random:當(dāng)內(nèi)存不足以容納新寫入數(shù)據(jù)時存淫,從所有key中隨機淘汰數(shù)據(jù)耘斩;
  • volatile-ttl:當(dāng)內(nèi)存不足以容納新寫入數(shù)據(jù)時,在設(shè)置了過期時間的key中桅咆,根據(jù)過期時間進行淘汰括授,越早過期的優(yōu)先被淘汰;
  • noeviction:默認策略岩饼,當(dāng)內(nèi)存不足以容納新寫入數(shù)據(jù)時荚虚,新寫入操作會報錯;

6忌愚、聊聊Redis事務(wù)機制?

Redis通過MULTI曲管、EXEC、WATCH等一組命令集合硕糊,來實現(xiàn)事務(wù)機制院水。事務(wù)支持一次執(zhí)行多個命令腊徙,一個事務(wù)中所有命令都會被序列化。在事務(wù)執(zhí)行過程檬某,會按照順序串行化執(zhí)行隊列中的命令撬腾,其他客戶端提交的命令請求不會插入到事務(wù)執(zhí)行命令序列中。

簡言之恢恼,Redis事務(wù)就是順序性民傻、一次性、排他性的執(zhí)行一個隊列中的一系列命令场斑。

Redis執(zhí)行事務(wù)的流程如下:

  • 開始事務(wù)(MULTI)
  • 命令入隊
  • 執(zhí)行事務(wù)(EXEC)漓踢、撤銷事務(wù)(DISCARD )
命令 描述
EXEC 執(zhí)行所有事務(wù)塊內(nèi)的命令
DISCARD 取消事務(wù),放棄執(zhí)行事務(wù)塊內(nèi)的所有命令
MULTI 標記一個事務(wù)塊的開始
UNWATCH 取消 WATCH 命令對所有 key 的監(jiān)視漏隐。
WATCH 監(jiān)視key 喧半,如果在事務(wù)執(zhí)行之前,該key 被其他命令所改動青责,那么事務(wù)將被打斷挺据。

有關(guān)redis事務(wù)需要注意的就是

1)與mysql中事務(wù)不同,在redis事務(wù)遇到執(zhí)行錯誤的時候脖隶,不會進行回滾扁耐,而是簡單的放過了,并保證其他的命令正常執(zhí)行(所以說redis的事務(wù)并不是保證原子性)产阱。

2)當(dāng)事務(wù)的執(zhí)行過程中婉称,如果redis意外的掛了。很遺憾只有部分命令執(zhí)行了心墅,后面的也就被丟棄了酿矢。

7榨乎、說說Redis哈希槽的概念怎燥?

Redis 集群沒有使用一致性 hash,而是引入了哈希槽的概念,Redis 集群有16384個哈希槽蜜暑,每個 key 通過 CRC16 校驗后對 16384 取模來決定放置哪個槽铐姚,集群的每個節(jié)點負責(zé)一部分 hash 槽。

使用哈希槽的好處就在于可以方便的添加或移除節(jié)點肛捍。這種結(jié)構(gòu)無論是添加刪除或者修改某一個節(jié)點隐绵,都不會造成集群不可用的狀態(tài)。

當(dāng)需要增加節(jié)點時拙毫,只需要把其他節(jié)點的某些哈希槽挪到新節(jié)點就可以了依许;

當(dāng)需要移除節(jié)點時,只需要把移除節(jié)點上的哈希槽挪到其他節(jié)點就行了缀蹄;

在這一點上揍庄,我們以后新增或移除節(jié)點的時候不用先停掉所有的 redis 服務(wù)。

8翼岁、為什么RedisCluster會設(shè)計成16384個槽呢?

2的14次方就是16384,這個當(dāng)然不說一定要設(shè)計成16384個槽,作者對這個也做了解釋烟具。

地址如下: https://github.com/antirez/redis/issues/2576

1) 如果槽位為65536,發(fā)送心跳信息的消息頭達8k钉凌,發(fā)送的心跳包過于龐大。

如上所述,在消息頭中脊岳,當(dāng)槽位為65536時,這塊的大小是: 65536÷8÷1024=8kb 因為每秒鐘垛玻,redis節(jié)點需要發(fā)送一定數(shù)量的ping消息作為心跳包割捅,如果槽位為65536,這個ping消息的消息頭太大了帚桩,浪費帶寬棺牧。

2) redis的集群主節(jié)點數(shù)量基本不可能超過1000個。

如上所述朗儒,集群節(jié)點越多颊乘,心跳包的消息體內(nèi)攜帶的數(shù)據(jù)越多。如果節(jié)點過1000個醉锄,也會導(dǎo)致網(wǎng)絡(luò)擁堵乏悄。因此redis作者,不建議redis cluster節(jié)點數(shù)量超過1000個恳不。 那么檩小,對于節(jié)點數(shù)在1000以內(nèi)的redis cluster集群,16384個槽位夠用了烟勋。沒有必要拓展到65536個规求。

3) 槽位越小,節(jié)點少的情況下卵惦,壓縮率高

Redis主節(jié)點的配置信息中阻肿,它所負責(zé)的哈希槽是通過一張bitmap的形式來保存的,在傳輸過程中沮尿,會對bitmap進行壓縮丛塌,但是如果bitmap的填充率slots / N很高的話(N表示節(jié)點數(shù)),bitmap的壓縮率就很低畜疾。

如果節(jié)點數(shù)很少赴邻,而哈希槽數(shù)量很多的話,bitmap的壓縮率就很低啡捶。

9姥敛、Redis在集群中查找key的時候,是怎么定位到具體節(jié)點的瞎暑?

使用CRC16算法對key進行hash,再將hash值對16384取模彤敛,得到具體的槽位根據(jù)節(jié)點和槽位的映射信息(與集群建立連接后忿偷,客戶端可以取得槽位映射信息),找到具體的節(jié)點地址 去具體的節(jié)點找key如果key不在這個節(jié)點上臊泌,則redis集群會返回moved指令鲤桥,加上新的節(jié)點地址給客戶端。

同時渠概,客戶端會刷新本地的節(jié)點槽位映射關(guān)系如果槽位正在遷移中茶凳,那么redis集群會返回asking指令給客戶端,這是臨時糾正播揪,客戶端不會刷新本地的節(jié)點槽位映射關(guān)系

10贮喧、Redis底層,使用的什么協(xié)議?

RESP 英文全稱是Redis Serialization Protocol,它是專門為redis設(shè)計的一套序列化協(xié)議. 這個協(xié)議其實在redis的1.2版本時就已經(jīng)出現(xiàn)了,但是到了redis2.0才最終成為redis通訊協(xié)議的標準猪狈。

RESP主要有實現(xiàn)簡單箱沦、解析速度快、可讀性好等優(yōu)點雇庙。

11谓形、Redis的Hash沖突怎么辦

Redis 中的 Hash和 Java的 HashMap 更加相似,都是數(shù)組+鏈表的結(jié)構(gòu),當(dāng)發(fā)生 hash 發(fā)生碰撞時將會把元素追加到鏈表上疆前。

在Redis中hash的內(nèi)部結(jié)構(gòu)也是一樣的: 第一維是數(shù)組,第二維是鏈表.組成一個 全局哈希表寒跳。

在 Java 中 HashMap 擴容是個很耗時的操作,需要去申請新的數(shù)組,擴容的成本并不低竹椒,因為需要遍歷一個時間復(fù)雜度為O(n)的數(shù)組童太,并且為其中的每個enrty進行hash計算。加入到新數(shù)組中胸完。

為了追求高性能,Redis 采用了漸進式 rehash 策略.這也是 hash 中最重要的部分.

redis在擴容的時候執(zhí)行 rehash 策略會保留新舊兩個 兩個全局哈希表书释,查詢時也會同時查詢兩個全局哈希表 ,Redis會將舊 全局哈希表 中的內(nèi)容一點一點的遷移到新的 全局哈希表 中,當(dāng)遷移完成時,就會用新的 全局哈希表 取代之前的。當(dāng) 全局哈希表 移除了最后一個元素之后,這個數(shù)據(jù)結(jié)構(gòu)將會被刪除.

正常情況下,當(dāng) 全局哈希表 中元素的個數(shù)等于數(shù)組的長度時,就會開始擴容,擴容的新數(shù)組是原數(shù)組大小的 2 倍赊窥。

如果 Redis 正在做 bgsave(持久化) 時,可能不會去擴容,因為要減少內(nèi)存頁的過多分離(Copy On Write).但是如果 全局哈希表 已經(jīng)非常滿了,元素的個數(shù)達到了數(shù)組長度的 5 倍時,Redis 會強制擴容爆惧。

12、Redis相比memcached有哪些優(yōu)勢

  1. Memcached 所有的值均是簡單的字符串誓琼,redis 作為其替代者检激,支持更為豐富的數(shù)據(jù)類
  2. Redis 的速度比 Memcached 快很多
  3. Redis 可以持久化其數(shù)據(jù)

13肴捉、有哪些辦法可以降低 Redis 的內(nèi)存使用情況呢腹侣?

當(dāng)你的業(yè)務(wù)應(yīng)用在 Redis 中存儲數(shù)據(jù)很少時,你可能并不太關(guān)心內(nèi)存資源的使用情況齿穗。但隨著業(yè)務(wù)的發(fā)展傲隶,你的業(yè)務(wù)存儲在 Redis 中的數(shù)據(jù)就會越來越多。

那在使用 Redis 時窃页,怎樣做才能更節(jié)省內(nèi)存呢跺株?這里總結(jié)了4點建議:

1) 控制 key 的長度

最簡單直接的內(nèi)存優(yōu)化复濒,就是控制 key 的長度。

在開發(fā)業(yè)務(wù)時乒省,你需要提前預(yù)估整個 Redis 中寫入 key 的數(shù)量巧颈,如果 key 數(shù)量達到了百萬級別,那么袖扛,過長的 key 名也會占用過多的內(nèi)存空間砸泛。

所以,你需要保證 key 在簡單蛆封、清晰的前提下唇礁,盡可能把 key 定義得短一些。

例如惨篱,原有的 key 為 user:book:123盏筐,則可以優(yōu)化為 u:bk:123。

這樣一來砸讳,你的 Redis 就可以節(jié)省大量的內(nèi)存琢融,這個方案對內(nèi)存的優(yōu)化非常直接和高效。

2) 避免存儲 bigkey

bigkey,意思就是這個key的value值很大簿寂。除了控制 key 的長度之外吏奸,你同樣需要關(guān)注 value 的大小,如果大量存儲 bigkey陶耍,也會導(dǎo)致 Redis 內(nèi)存增長過快奋蔚。

除此之外,客戶端在讀寫 bigkey 時烈钞,還有產(chǎn)生性能問題泊碑。

所以,你要避免在 Redis 中存儲 bigkey毯欣,一般建議是:

  • String:大小控制在 10KB 以下
  • List/Hash/Set/ZSet:元素數(shù)量控制在 1 萬以下

3) 盡可能地都設(shè)置過期時間

Redis 數(shù)據(jù)存儲在內(nèi)存中馒过,這也意味著其資源是有限的。你在使用 Redis 時酗钞,要把它當(dāng)做緩存來使用腹忽,而不是數(shù)據(jù)庫。

所以砚作,你的應(yīng)用寫入到 Redis中的數(shù)據(jù)窘奏,盡可能地都設(shè)置 過期時間。采用這種方案葫录,可以讓 Redis 中只保留經(jīng)常訪問的 熱數(shù)據(jù)着裹,內(nèi)存利用率也會比較高。

4) 實例設(shè)置 maxmemory + 淘汰策略

雖然你的 Redis key 都設(shè)置了過期時間米同,但如果你的業(yè)務(wù)應(yīng)用寫入量很大骇扇,并且過期時間設(shè)置得比較久摔竿,那么短期間內(nèi) Redis 的內(nèi)存依舊會快速增長。

如果不控制 Redis 的內(nèi)存上限少孝,也會導(dǎo)致使用過多的內(nèi)存資源继低。

對于這種場景,你需要提前預(yù)估業(yè)務(wù)數(shù)據(jù)量稍走,然后給這個實例設(shè)置 maxmemory 控制實例的內(nèi)存上限郁季,這樣可以避免 Redis 的內(nèi)存持續(xù)膨脹。

配置了 maxmemory钱磅,此時你還要設(shè)置數(shù)據(jù)淘汰策略梦裂,而淘汰策略如何選擇,你需要結(jié)合你的業(yè)務(wù)特點來決定盖淡。

14年柠、Redis中獲取海量數(shù)據(jù)的正確操作方式?

有時候需要從Redis實例成千上萬的key中找出特定前綴的key列表來手動處理數(shù)據(jù)褪迟,可能是修改它的值冗恨,也可能是刪除 key。這里就有一個問題味赃,如何從海量的 key 中找出滿足特定前綴的 key 列表來掀抹?

比如我們的用戶token緩存是采用了【user_token:userid】格式的key,保存用戶的token的值心俗。這時候我們想看下有多少用戶在線傲武。

在Redis2.8版本之前,我們可以使用keys命令按照正則匹配得到我們需要的key城榛。Redis 提供了一個簡單暴力的指令 keys 用來列出所有滿足特定正則字符串規(guī)則的 key揪利。

keys user_token*

但是這個命令有一些缺點:

  • 沒有 offset、limit 參數(shù)狠持,一次性吐出所有滿足條件的 key疟位,萬一實例中有幾百 w 個 key 滿足條件,當(dāng)你看到滿屏的字符串刷的沒有盡頭時喘垂,你就知道難受了甜刻。
  • keys 算法是遍歷算法,復(fù)雜度是 O(n)正勒,如果實例中有千萬級以上的 key得院,這個指令就會導(dǎo)致 Redis 服務(wù)卡頓,
  • 所有讀寫 Redis 的其它的指令都會被延后甚至?xí)瑫r報錯昭齐,因為 Redis 是單線程程序尿招,順序執(zhí)行所有指令,其它指令必須等到當(dāng)前的 keys 指令執(zhí)行完了才可以繼續(xù)阱驾。
  • 建議生產(chǎn)環(huán)境屏蔽keys命令

在滿足需求和存在造成Redis卡頓之間究竟要如何選擇呢就谜?面對這個兩難的抉擇,Redis在2.8版本給我們提供了解決辦法——scan命令里覆。

相比于keys命令丧荐,scan命令有兩個比較明顯的優(yōu)勢:

  • scan命令的時間復(fù)雜度雖然也是O(N),但它是分次進行的喧枷,不會阻塞線程虹统。
  • scan命令提供了limit參數(shù),可以控制每次返回結(jié)果的最大條數(shù)隧甚。

這兩個優(yōu)勢就幫助我們解決了上面的難題车荔,不過scan命令也并不是完美的,它返回的結(jié)果有可能重復(fù)戚扳,因此需要客戶端去重這點非常重要忧便。

15、如何使用Redis的性能更高帽借?

1)master關(guān)閉持久化

一般我們在生產(chǎn)上采用的持久化策略為master關(guān)閉持久化,slave開RDB即可珠增,必要的時候AOF和RDB都開啟。

2) 不使用復(fù)雜度過高的命令

Redis 是單線程模型處理請求砍艾,在執(zhí)行復(fù)雜度過高的命令時蒂教,會消耗更多的 CPU 資源,主線程中的其它請求只能等待脆荷,這時也會發(fā)生排隊延遲凝垛。

所以,你需要避免執(zhí)行例如 sort蜓谋、sinter苔严、sinterstore、zunionstore孤澎、zinterstore 等聚合類命令届氢。

對于這種聚合類操作,我建議你把它放到客戶端來執(zhí)行覆旭,不要讓 Redis 承擔(dān)太多的計算工作退子。

3)執(zhí)行 O(N) 命令時,關(guān)注 N 的大小

規(guī)避使用復(fù)雜度過高的命令型将,就可以高枕無憂了么寂祥?

答案是否定的。

當(dāng)你在執(zhí)行 O(N) 命令時七兜,同樣需要注意 N 的大小丸凭。

就好比上面說的使用keys命令,如果一次性能查詢過多的數(shù)據(jù),也會在網(wǎng)絡(luò)傳輸過程中耗時過長惜犀,操作延遲變大铛碑。

所以,對于容器類型(List/Hash/Set/ZSet)虽界,在元素數(shù)量未知的情況下汽烦,一定不要無腦執(zhí)行 LRANGE key 0 -1 / HGETALL / SMEMBERS / ZRANGE key 0 -1。

在查詢數(shù)據(jù)時莉御,你要遵循以下原則:

  • 先查詢數(shù)據(jù)元素的數(shù)量(LLEN/HLEN/SCARD/ZCARD)
  • 元素數(shù)量較少撇吞,可一次性查詢?nèi)繑?shù)據(jù)
  • 元素數(shù)量非常多,分批查詢數(shù)據(jù)(LRANGE/HASCAN/SSCAN/ZSCAN)

4) 批量命令代替單個命令

當(dāng)你需要一次性操作多個 key 時礁叔,你應(yīng)該使用批量命令來處理牍颈。

批量操作相比于多次單個操作的優(yōu)勢在于,可以顯著減少客戶端琅关、服務(wù)端的來回網(wǎng)絡(luò) IO 次數(shù)煮岁。

所以我給你的建議是:

  • String / Hash 使用 MGET/MSET 替代 GET/SET,HMGET/HMSET 替代 HGET/HSET

  • 其它數(shù)據(jù)類型使用 Pipeline死姚,打包一次性發(fā)送多個命令到服務(wù)端執(zhí)行

5) 避免集中過期 key

Redis 清理過期 key 是采用定時 + 懶惰的方式來做的人乓,而且這個過程都是在主線程中執(zhí)行。

如果你的業(yè)務(wù)存在大量 key 集中過期的情況都毒,那么 Redis 在清理過期 key 時色罚,也會有阻塞主線程的風(fēng)險。

想要避免這種情況發(fā)生账劲,你可以在設(shè)置過期時間時戳护,增加一個隨機時間,把這些 key 的過期時間打散瀑焦,從而降低集中過期對主線程的影響腌且。

6) 只使用 db0

盡管 Redis 提供了 16 個 db,但我只建議你使用 db0榛瓮。

為什么呢铺董?我總結(jié)了以下 3 點原因:

  • 在一個連接上操作多個 db 數(shù)據(jù)時,每次都需要先執(zhí)行 SELECT禀晓,這會給 Redis 帶來額外的壓力
  • 使用多個 db 的目的是精续,按不同業(yè)務(wù)線存儲數(shù)據(jù),那為何不拆分多個實例存儲呢粹懒?拆分多個實例部署重付,多個業(yè)務(wù)線不會互相影響,還能提高 Redis 的訪問性能
  • Redis Cluster 只支持 db0凫乖,如果后期你想要遷移到 Redis Cluster确垫,遷移成本高

16弓颈、如何解決 Redis 的并發(fā)競爭 Key 問題

這個也是線上非常常見的一個問題,就是多客戶端同時并發(fā)寫一個 key删掀,可能本來應(yīng)該先到的數(shù)據(jù)后到了翔冀,導(dǎo)致數(shù)據(jù)版本錯了;或者是多客戶端同時獲取一個 key爬迟,修改值之后再寫回去橘蜜,只要順序錯了菊匿,數(shù)據(jù)就錯了付呕。

推薦一種方案:分布式鎖(zookeeper和redis都可以實現(xiàn)分布式鎖)。(如果不存在Redis的并發(fā)競爭Key問題跌捆,不要使用分布式鎖徽职,這樣會影響性能)

17、使用過 Redis 做異步隊列么佩厚,你是怎么用的姆钉?

一般使用list結(jié)構(gòu)作為隊列,rpush生產(chǎn)消息抄瓦,lpop消費消息潮瓶。當(dāng)lpop沒有消息的時候,要適當(dāng)sleep一會再重試钙姊。

如果不想sleep呢毯辅?list還有個指令叫blpop,在沒有消息的時候煞额,它會阻塞住直到消息到來思恐。

但如果是這樣你發(fā)現(xiàn)redis作為消息隊列是不安全的,它不能重復(fù)消費膊毁,一旦消費就會被刪除胀莹。同時做消費者確認ACK也麻煩所以一般在實際開發(fā)中一般很少用redis中消息隊列,因為現(xiàn)在已經(jīng)有Kafka婚温、RabbitMQ等成熟的消息隊列了描焰,它們的功能更加完善。

18栅螟、用Redis做延時隊列荆秦,具體應(yīng)該怎么實現(xiàn)?

延遲隊列可以使用 zset(有序列表)實現(xiàn)嵌巷,我們將消息序列化成一個字符串作為列表的value萄凤,這個消息的到期處理時間作為score,然后用定時器定時去掃描搪哪,一旦有執(zhí)行時間小于或等于當(dāng)前時間的任務(wù)靡努,就立即執(zhí)行。

19、使用Redis統(tǒng)計網(wǎng)站的UV惑朦,應(yīng)該怎么做兽泄?

UV與PV不同,UV需要去重漾月。一般有2種方案:

1病梢、用BitMap。存的是用戶的uid梁肿,計算UV的時候蜓陌,做下bitcount就行了。

2吩蔑、用布隆過濾器钮热。將每次訪問的用戶uid都放到布隆過濾器中。優(yōu)點是省內(nèi)存烛芬,缺點是無法得 到精確的UV隧期。但是對于不需要精確知道具體UV,只需要大概的數(shù)量級的場景赘娄,是個不錯的選擇仆潮。

20、什么是熱Key問題遣臼,如何解決熱key問題

所謂熱key問題就是性置,突然有幾十萬的請求去訪問redis上的某個特定key。那么暑诸,這樣會造成流量過于集中蚌讼,達到物理網(wǎng)卡上限,從而導(dǎo)致這臺redis的服務(wù)器宕機个榕。

而熱點Key是怎么產(chǎn)生的呢篡石?主要原因有兩個:

  • 用戶消費的數(shù)據(jù)遠大于生產(chǎn)的數(shù)據(jù),如秒殺西采、熱點新聞等讀多寫少的場景凰萨。
  • 請求分片集中,超過單Redi服務(wù)器的性能械馆,比如固定名稱key胖眷,Hash落入同一臺服務(wù)器,瞬間訪問量極大霹崎,超過機器瓶頸珊搀,產(chǎn)生熱點Key問題。

那么在日常開發(fā)中尾菇,如何識別到熱點key呢境析?

  • 憑經(jīng)驗判斷哪些是熱Key囚枪;
  • 客戶端統(tǒng)計上報;
  • 服務(wù)代理層上報

如何解決熱key問題劳淆?

  • Redis集群擴容:增加分片副本链沼,均衡讀流量;
  • 將熱key分散到不同的服務(wù)器中沛鸵;
  • 使用二級緩存括勺,即JVM本地緩存,減少Redis的讀請求。


參考

[1] Redis 最佳實踐指南: https://mp.weixin.qq.com/s/Fz1EbsmJP5k2Rh6ir_a1pQ

[2] Redis經(jīng)典面試題: https://mp.weixin.qq.com/s/fBShKZbuR54yaIzzMR3R7g

[3] Redis面試20題: http://www.gameboys.cn/article/57

關(guān)注公眾號:后端元宇宙曲掰。持續(xù)輸出優(yōu)質(zhì)好文

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末疾捍,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子蜈缤,更是在濱河造成了極大的恐慌拾氓,老刑警劉巖冯挎,帶你破解...
    沈念sama閱讀 216,651評論 6 501
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件底哥,死亡現(xiàn)場離奇詭異,居然都是意外死亡房官,警方通過查閱死者的電腦和手機趾徽,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,468評論 3 392
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來翰守,“玉大人孵奶,你說我怎么就攤上這事±澹” “怎么了了袁?”我有些...
    開封第一講書人閱讀 162,931評論 0 353
  • 文/不壞的土叔 我叫張陵,是天一觀的道長湿颅。 經(jīng)常有香客問我载绿,道長,這世上最難降的妖魔是什么油航? 我笑而不...
    開封第一講書人閱讀 58,218評論 1 292
  • 正文 為了忘掉前任崭庸,我火速辦了婚禮,結(jié)果婚禮上谊囚,老公的妹妹穿的比我還像新娘怕享。我一直安慰自己,他們只是感情好镰踏,可當(dāng)我...
    茶點故事閱讀 67,234評論 6 388
  • 文/花漫 我一把揭開白布函筋。 她就那樣靜靜地躺著,像睡著了一般奠伪。 火紅的嫁衣襯著肌膚如雪跌帐。 梳的紋絲不亂的頭發(fā)上灌诅,一...
    開封第一講書人閱讀 51,198評論 1 299
  • 那天,我揣著相機與錄音含末,去河邊找鬼猜拾。 笑死,一個胖子當(dāng)著我的面吹牛佣盒,可吹牛的內(nèi)容都是我干的挎袜。 我是一名探鬼主播,決...
    沈念sama閱讀 40,084評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼肥惭,長吁一口氣:“原來是場噩夢啊……” “哼盯仪!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起蜜葱,我...
    開封第一講書人閱讀 38,926評論 0 274
  • 序言:老撾萬榮一對情侶失蹤全景,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后牵囤,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體爸黄,經(jīng)...
    沈念sama閱讀 45,341評論 1 311
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,563評論 2 333
  • 正文 我和宋清朗相戀三年揭鳞,在試婚紗的時候發(fā)現(xiàn)自己被綠了炕贵。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 39,731評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡野崇,死狀恐怖称开,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情乓梨,我是刑警寧澤鳖轰,帶...
    沈念sama閱讀 35,430評論 5 343
  • 正文 年R本政府宣布,位于F島的核電站扶镀,受9級特大地震影響蕴侣,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜狈惫,卻給世界環(huán)境...
    茶點故事閱讀 41,036評論 3 326
  • 文/蒙蒙 一睛蛛、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧胧谈,春花似錦忆肾、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,676評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至稳强,卻和暖如春场仲,著一層夾襖步出監(jiān)牢的瞬間和悦,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,829評論 1 269
  • 我被黑心中介騙來泰國打工渠缕, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留鸽素,地道東北人。 一個月前我還...
    沈念sama閱讀 47,743評論 2 368
  • 正文 我出身青樓亦鳞,卻偏偏與公主長得像馍忽,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子燕差,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 44,629評論 2 354

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