redis面试问题(一)
鏈接:https://www.nowcoder.com/discuss/92610
來源:牛客網(wǎng)
最近在學(xué)習(xí)redis,根據(jù)網(wǎng)上的面經(jīng)整理了兩篇常見的問題。本人水平有限,還請各位牛友大佬多多指教!
基礎(chǔ)知識(shí)必備:
五大常用數(shù)據(jù)類型
redis與其他緩存的比較
rdb和aof
=================================
常見問題:
1、為什么使用redis
(一)性能
我們在碰到需要執(zhí)行耗時(shí)特別久,且結(jié)果不頻繁變動(dòng)的SQL,就特別適合將運(yùn)行結(jié)果放入緩存。這樣,后面的請求就去緩存中讀取,使得請求能夠迅速響應(yīng)。
(二)并發(fā)
在大并發(fā)的情況下,所有的請求直接訪問數(shù)據(jù)庫,數(shù)據(jù)庫會(huì)出現(xiàn)連接異常。這個(gè)時(shí)候,就需要使用redis做一個(gè)緩沖操作,讓請求先訪問到redis,而不是直接訪問數(shù)據(jù)庫。
2.使用redis有什么缺點(diǎn)
(一)緩存和數(shù)據(jù)庫雙寫一致性問題
(二)緩存雪崩問題
(三)緩存擊穿問題
(四)緩存的并發(fā)競爭問題
3、單線程的redis為什么這么快
(一)純內(nèi)存操作
(二)單線程操作,避免了頻繁的上下文切換
(三)采用了非阻塞I/O多路復(fù)用機(jī)制
參照上圖,簡單來說,就是。我們的redis-client在操作的時(shí)候,會(huì)產(chǎn)生具有不同事件類型的socket。在服務(wù)端,有一段I/0多路復(fù)用程序,將其置入隊(duì)列之中。然后,文件事件分派器,依次去隊(duì)列中取,轉(zhuǎn)發(fā)到不同的事件處理器中。
4、redis的數(shù)據(jù)類型,以及每種數(shù)據(jù)類型的使用場景
回答:一共五種
(一)String
這個(gè)其實(shí)沒啥好說的,最常規(guī)的set/get操作,value可以是String也可以是數(shù)字。一般做一些復(fù)雜的計(jì)數(shù)功能的緩存。
(二)hash
這里value存放的是結(jié)構(gòu)化的對象,比較方便的就是操作其中的某個(gè)字段。博主在做單點(diǎn)登錄的時(shí)候,就是用這種數(shù)據(jù)結(jié)構(gòu)存儲(chǔ)用戶信息,以cookieId作為key,設(shè)置30分鐘為緩存過期時(shí)間,能很好的模擬出類似session的效果。
(三)list
使用List的數(shù)據(jù)結(jié)構(gòu),可以做簡單的消息隊(duì)列的功能。另外還有一個(gè)就是,可以利用lrange命令,做基于redis的分頁功能,性能極佳,用戶體驗(yàn)好。本人還用一個(gè)場景,很合適---取行情信息。就也是個(gè)生產(chǎn)者和消費(fèi)者的場景。LIST可以很好的完成排隊(duì),先進(jìn)先出的原則。
(四)set
因?yàn)閟et堆放的是一堆不重復(fù)值的集合。所以可以做全局去重的功能。為什么不用JVM自帶的Set進(jìn)行去重?因?yàn)槲覀兊南到y(tǒng)一般都是集群部署,使用JVM自帶的Set,比較麻煩,難道為了一個(gè)做一個(gè)全局去重,再起一個(gè)公共服務(wù),太麻煩了。
另外,就是利用交集、并集、差集等操作,可以計(jì)算共同喜好,全部的喜好,自己獨(dú)有的喜好等功能。
(五)sorted set
sorted set多了一個(gè)權(quán)重參數(shù)score,集合中的元素能夠按score進(jìn)行排列。可以做排行榜應(yīng)用,取TOP N操作。
5、redis的過期策略以及內(nèi)存淘汰機(jī)制
redis采用的是定期刪除+惰性刪除策略。
為什么不用定時(shí)刪除策略?
定時(shí)刪除,用一個(gè)定時(shí)器來負(fù)責(zé)監(jiān)視key,過期則自動(dòng)刪除。雖然內(nèi)存及時(shí)釋放,但是十分消耗CPU資源。在大并發(fā)請求下,CPU要將時(shí)間應(yīng)用在處理請求,而不是刪除key,因此沒有采用這一策略.
定期刪除+惰性刪除是如何工作的呢?
定期刪除,redis默認(rèn)每個(gè)100ms檢查,是否有過期的key,有過期key則刪除。需要說明的是,redis不是每個(gè)100ms將所有的key檢查一次,而是隨機(jī)抽取進(jìn)行檢查(如果每隔100ms,全部key進(jìn)行檢查,redis豈不是卡死)。因此,如果只采用定期刪除策略,會(huì)導(dǎo)致很多key到時(shí)間沒有刪除。
于是,惰性刪除派上用場。也就是說在你獲取某個(gè)key的時(shí)候,redis會(huì)檢查一下,這個(gè)key如果設(shè)置了過期時(shí)間那么是否過期了?如果過期了此時(shí)就會(huì)刪除。
采用定期刪除+惰性刪除就沒其他問題了么?
不是的,如果定期刪除沒刪除key。然后你也沒即時(shí)去請求key,也就是說惰性刪除也沒生效。這樣,redis的內(nèi)存會(huì)越來越高。那么就應(yīng)該采用內(nèi)存淘汰機(jī)制。
在redis.conf中有一行配置
# maxmemory-policy volatile-lru
該配置就是配內(nèi)存淘汰策略的(什么,你沒配過?好好反省一下自己)
1)noeviction:當(dāng)內(nèi)存不足以容納新寫入數(shù)據(jù)時(shí),新寫入操作會(huì)報(bào)錯(cuò)。應(yīng)該沒人用吧。
2)allkeys-lru:當(dāng)內(nèi)存不足以容納新寫入數(shù)據(jù)時(shí),在鍵空間中,移除最近最少使用的key。推薦使用,目前項(xiàng)目在用這種。
3)allkeys-random:當(dāng)內(nèi)存不足以容納新寫入數(shù)據(jù)時(shí),在鍵空間中,隨機(jī)移除某個(gè)key。應(yīng)該也沒人用吧,你不刪最少使用Key,去隨機(jī)刪。
4)volatile-lru:當(dāng)內(nèi)存不足以容納新寫入數(shù)據(jù)時(shí),在設(shè)置了過期時(shí)間的鍵空間中,移除最近最少使用的key。這種情況一般是把redis既當(dāng)緩存,又做持久化存儲(chǔ)的時(shí)候才用。不推薦
5)volatile-random:當(dāng)內(nèi)存不足以容納新寫入數(shù)據(jù)時(shí),在設(shè)置了過期時(shí)間的鍵空間中,隨機(jī)移除某個(gè)key。依然不推薦
6)volatile-ttl:當(dāng)內(nèi)存不足以容納新寫入數(shù)據(jù)時(shí),在設(shè)置了過期時(shí)間的鍵空間中,有更早過期時(shí)間的key優(yōu)先移除。不推薦
ps:如果沒有設(shè)置 expire 的key, 不滿足先決條件(prerequisites); 那么 volatile-lru, volatile-random 和 volatile-ttl 策略的行為, 和 noeviction(不刪除) 基本上一致。
6、redis和數(shù)據(jù)庫雙寫一致性問題
分析:一致性問題是分布式常見問題,還可以再分為最終一致性和強(qiáng)一致性。數(shù)據(jù)庫和緩存雙寫,就必然會(huì)存在不一致的問題。答這個(gè)問題,先明白一個(gè)前提。就是如果對數(shù)據(jù)有強(qiáng)一致性要求,不能放緩存。我們所做的一切,只能保證最終一致性。另外,我們所做的方案其實(shí)從根本上來說,只能說降低不一致發(fā)生的概率,無法完全避免。因此,有強(qiáng)一致性要求的數(shù)據(jù),不能放緩存。
首先,采取正確更新策略,先更新數(shù)據(jù)庫,再刪緩存。其次,因?yàn)榭赡艽嬖趧h除緩存失敗的問題,提供一個(gè)補(bǔ)償措施即可,例如利用消息隊(duì)列。
7、如何應(yīng)對緩存穿透和緩存雪崩問題
分析:這兩個(gè)問題,說句實(shí)在話,一般中小型傳統(tǒng)軟件企業(yè),很難碰到這個(gè)問題。如果有大并發(fā)的項(xiàng)目,流量有幾百萬左右。這兩個(gè)問題一定要深刻考慮。
回答:如下所示
緩存穿透,即黑客故意去請求緩存中不存在的數(shù)據(jù),導(dǎo)致所有的請求都懟到數(shù)據(jù)庫上,從而數(shù)據(jù)庫連接異常。
解決方案:
(一)利用互斥鎖,緩存失效的時(shí)候,先去獲得鎖,得到鎖了,再去請求數(shù)據(jù)庫。沒得到鎖,則休眠一段時(shí)間重試
(二)采用異步更新策略,無論key是否取到值,都直接返回。value值中維護(hù)一個(gè)緩存失效時(shí)間,緩存如果過期,異步起一個(gè)線程去讀數(shù)據(jù)庫,更新緩存。需要做緩存預(yù)熱(項(xiàng)目啟動(dòng)前,先加載緩存)操作。
(三)提供一個(gè)能迅速判斷請求是否有效的攔截機(jī)制,比如,利用布隆過濾器,內(nèi)部維護(hù)一系列合法有效的key。迅速判斷出,請求所攜帶的Key是否合法有效。如果不合法,則直接返回。
緩存雪崩,即緩存同一時(shí)間大面積的失效,這個(gè)時(shí)候又來了一波請求,結(jié)果請求都懟到數(shù)據(jù)庫上,從而導(dǎo)致數(shù)據(jù)庫連接異常。
解決方案:
(一)給緩存的失效時(shí)間,加上一個(gè)隨機(jī)值,避免集體失效。
(二)使用互斥鎖,但是該方案吞吐量明顯下降了。
(三)雙緩存。我們有兩個(gè)緩存,緩存A和緩存B。緩存A的失效時(shí)間為20分鐘,緩存B不設(shè)失效時(shí)間。自己做緩存預(yù)熱操作。然后細(xì)分以下幾個(gè)小點(diǎn)
I 從緩存A讀數(shù)據(jù)庫,有則直接返回
II A沒有數(shù)據(jù),直接從B讀數(shù)據(jù),直接返回,并且異步啟動(dòng)一個(gè)更新線程。
III 更新線程同時(shí)更新緩存A和緩存B。
8、如何解決redis的并發(fā)競爭key問題
分析:這個(gè)問題大致就是,同時(shí)有多個(gè)子系統(tǒng)去set一個(gè)key。這個(gè)時(shí)候要注意什么呢?大家思考過么。需要說明一下,博主提前百度了一下,發(fā)現(xiàn)答案基本都是推薦用redis事務(wù)機(jī)制。博主不推薦使用redis的事務(wù)機(jī)制。因?yàn)槲覀兊纳a(chǎn)環(huán)境,基本都是redis集群環(huán)境,做了數(shù)據(jù)分片操作。你一個(gè)事務(wù)中有涉及到多個(gè)key操作的時(shí)候,這多個(gè)key不一定都存儲(chǔ)在同一個(gè)redis-server上。因此,redis的事務(wù)機(jī)制,十分雞肋。
回答:如下所示
(1)如果對這個(gè)key操作,不要求順序
這種情況下,準(zhǔn)備一個(gè)分布式鎖,大家去搶鎖,搶到鎖就做set操作即可,比較簡單。
(2)如果對這個(gè)key操作,要求順序
假設(shè)有一個(gè)key1,系統(tǒng)A需要將key1設(shè)置為valueA,系統(tǒng)B需要將key1設(shè)置為valueB,系統(tǒng)C需要將key1設(shè)置為valueC.
期望按照key1的value值按照 valueA–>valueB–>valueC的順序變化。這種時(shí)候我們在數(shù)據(jù)寫入數(shù)據(jù)庫的時(shí)候,需要保存一個(gè)時(shí)間戳。假設(shè)時(shí)間戳如下
系統(tǒng)A key 1 {valueA 3:00}
系統(tǒng)B key 1 {valueB 3:05}
系統(tǒng)C key 1 {valueC 3:10}
那么,假設(shè)這會(huì)系統(tǒng)B先搶到鎖,將key1設(shè)置為{valueB 3:05}。接下來系統(tǒng)A搶到鎖,發(fā)現(xiàn)自己的valueA的時(shí)間戳早于緩存中的時(shí)間戳,那就不做set操作了。以此類推。
其他方法,比如利用隊(duì)列,將set方法變成串行訪問也可以。總之,靈活變通。
9.Reids的特點(diǎn)
Redis本質(zhì)上是一個(gè)Key-Value類型的內(nèi)存數(shù)據(jù)庫,很像memcached,整個(gè)數(shù)據(jù)庫統(tǒng)統(tǒng)加載在內(nèi)存當(dāng)中進(jìn)行操作,定期通過異步操作把數(shù)據(jù)庫數(shù)據(jù)flush到硬盤上進(jìn)行保存。因?yàn)槭羌儍?nèi)存操作,Redis的性能非常出色,每秒可以處理超過 10萬次讀寫操作,是已知性能最快的Key-Value DB。
Redis的出色之處不僅僅是性能,Redis最大的魅力是支持保存多種數(shù)據(jù)結(jié)構(gòu),此外單個(gè)value的最大限制是1GB,不像 memcached只能保存1MB的數(shù)據(jù),因此Redis可以用來實(shí)現(xiàn)很多有用的功能,比方說用他的List來做FIFO雙向鏈表,實(shí)現(xiàn)一個(gè)輕量級的高性 能消息隊(duì)列服務(wù),用他的Set可以做高性能的tag系統(tǒng)等等。另外Redis也可以對存入的Key-Value設(shè)置expire時(shí)間,因此也可以被當(dāng)作一 個(gè)功能加強(qiáng)版的memcached來用。
Redis的主要缺點(diǎn)是數(shù)據(jù)庫容量受到物理內(nèi)存的限制,不能用作海量數(shù)據(jù)的高性能讀寫,因此Redis適合的場景主要局限在較小數(shù)據(jù)量的高性能操作和運(yùn)算上。
10.使用redis有哪些好處?
(1) 速度快,因?yàn)閿?shù)據(jù)存在內(nèi)存中,類似于HashMap,HashMap的優(yōu)勢就是查找和操作的時(shí)間復(fù)雜度都是O(1)
(2) 支持豐富數(shù)據(jù)類型,支持string,list,set,sorted set,hash
(3) 支持事務(wù),操作都是原子性,所謂的原子性就是對數(shù)據(jù)的更改要么全部執(zhí)行,要么全部不執(zhí)行
(4) 豐富的特性:可用于緩存,消息,按key設(shè)置過期時(shí)間,過期后將會(huì)自動(dòng)刪除
11.redis相比memcached有哪些優(yōu)勢?
(1) memcached所有的值均是簡單的字符串,redis作為其替代者,支持更為豐富的數(shù)據(jù)類型
(2) redis的速度比memcached快很多 (3) redis可以持久化其數(shù)據(jù)
12.Memcache與Redis的區(qū)別都有哪些?
1)、存儲(chǔ)方式 Memecache把數(shù)據(jù)全部存在內(nèi)存之中,斷電后會(huì)掛掉,數(shù)據(jù)不能超過內(nèi)存大小。 Redis有部份存在硬盤上,這樣能保證數(shù)據(jù)的持久性。
2)、數(shù)據(jù)支持類型 Memcache對數(shù)據(jù)類型支持相對簡單。 Redis有復(fù)雜的數(shù)據(jù)類型。
3)、使用底層模型不同 它們之間底層實(shí)現(xiàn)方式 以及與客戶端之間通信的應(yīng)用協(xié)議不一樣。 Redis直接自己構(gòu)建了VM 機(jī)制 ,因?yàn)橐话愕南到y(tǒng)調(diào)用系統(tǒng)函數(shù)的話,會(huì)浪費(fèi)一定的時(shí)間去移動(dòng)和請求。
13.redis常見性能問題和解決方案:
1).Master寫內(nèi)存快照,save命令調(diào)度rdbSave函數(shù),會(huì)阻塞主線程的工作,當(dāng)快照比較大時(shí)對性能影響是非常大的,會(huì)間斷性暫停服務(wù),所以Master最好不要寫內(nèi)存快照。
2).Master AOF持久化,如果不重寫AOF文件,這個(gè)持久化方式對性能的影響是最小的,但是AOF文件會(huì)不斷增大,AOF文件過大會(huì)影響Master重啟的恢復(fù)速度。Master最好不要做任何持久化工作,包括內(nèi)存快照和AOF日志文件,特別是不要啟用內(nèi)存快照做持久
化,如果數(shù)據(jù)比較關(guān)鍵,某個(gè)Slave開啟AOF備份數(shù)據(jù),策略為每秒同步一次。
3).Master調(diào)用BGREWRITEAOF重寫AOF文件,AOF在重寫的時(shí)候會(huì)占大量的CPU和內(nèi)存資源,導(dǎo)致服務(wù)load過高,出現(xiàn)短暫服務(wù)暫停現(xiàn)象。
4). Redis主從復(fù)制的性能問題,為了主從復(fù)制的速度和連接的穩(wěn)定性,Slave和Master最好在同一個(gè)局域網(wǎng)內(nèi)
14. mySQL里有2000w數(shù)據(jù),redis中只存20w的數(shù)據(jù),如何保證redis中的數(shù)據(jù)都是熱點(diǎn)數(shù)據(jù)
相關(guān)知識(shí):redis 內(nèi)存數(shù)據(jù)集大小上升到一定大小的時(shí)候,就會(huì)施行數(shù)據(jù)淘汰策略(回收策略)。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ù)
15.redis事物的了解CAS(check-and-set 操作實(shí)現(xiàn)樂觀鎖 )?
和眾多其它數(shù)據(jù)庫一樣,Redis作為NoSQL數(shù)據(jù)庫也同樣提供了事務(wù)機(jī)制。在Redis中,MULTI/EXEC/DISCARD/WATCH這四個(gè)命令是我們實(shí)現(xiàn)事務(wù)的基石。相信對有關(guān)系型數(shù)據(jù)庫開發(fā)經(jīng)驗(yàn)的開發(fā)者而言這一概念并不陌生,即便如此,我們還是會(huì)簡要的列出
Redis中
事務(wù)的實(shí)現(xiàn)特征:
1). 在事務(wù)中的所有命令都將會(huì)被串行化的順序執(zhí)行,事務(wù)執(zhí)行期間,Redis不會(huì)再為其它客戶端的請求提供任何服務(wù),從而保證了事物中的所有命令被原子的執(zhí)行。
2). 和關(guān)系型數(shù)據(jù)庫中的事務(wù)相比,在Redis事務(wù)中如果有某一條命令執(zhí)行失敗,其后的命令仍然會(huì)被繼續(xù)執(zhí)行。
3). 我們可以通過MULTI命令開啟一個(gè)事務(wù),有關(guān)系型數(shù)據(jù)庫開發(fā)經(jīng)驗(yàn)的人可以將其理解為"BEGIN TRANSACTION"語句。在該語句之后執(zhí)行的命令都將被視為事務(wù)之內(nèi)的操作,最后我們可以通過執(zhí)行EXEC/DISCARD命令來提交/回滾該事務(wù)內(nèi)的所有操作。這兩
個(gè)Redis命令可被視為等同于關(guān)系型數(shù)據(jù)庫中的COMMIT/ROLLBACK語句。
4). 在事務(wù)開啟之前,如果客戶端與服務(wù)器之間出現(xiàn)通訊故障并導(dǎo)致網(wǎng)絡(luò)斷開,其后所有待執(zhí)行的語句都將不會(huì)被服務(wù)器執(zhí)行。然而如果網(wǎng)絡(luò)中斷事件是發(fā)生在客戶端執(zhí)行EXEC命令之后,那么該事務(wù)中的所有命令都會(huì)被服務(wù)器執(zhí)行。
5). 當(dāng)使用Append-Only模式時(shí),Redis會(huì)通過調(diào)用系統(tǒng)函數(shù)write將該事務(wù)內(nèi)的所有寫操作在本次調(diào)用中全部寫入磁盤。然而如果在寫入的過程中出現(xiàn)系統(tǒng)崩潰,如電源故障導(dǎo)致的宕機(jī),那么此時(shí)也許只有部分?jǐn)?shù)據(jù)被寫入到磁盤,而另外一部分?jǐn)?shù)據(jù)卻已經(jīng)丟失。
Redis服務(wù)器會(huì)在重新啟動(dòng)時(shí)執(zhí)行一系列必要的一致性檢測,一旦發(fā)現(xiàn)類似問題,就會(huì)立即退出并給出相應(yīng)的錯(cuò)誤提示。此時(shí),我們就要充分利用Redis工具包中提供的redis-check-aof工具,該工具可以幫助我們定位到數(shù)據(jù)不一致的錯(cuò)誤,并將已經(jīng)寫入的部
分?jǐn)?shù)據(jù)進(jìn)行回滾。修復(fù)之后我們就可以再次重新啟動(dòng)Redis服務(wù)器了。
16.WATCH命令和基于CAS的樂觀鎖:
在Redis的事務(wù)中,WATCH命令可用于提供CAS(check-and-set)功能。假設(shè)我們通過WATCH命令在事務(wù)執(zhí)行之前監(jiān)控了多個(gè)Keys,倘若在WATCH之后有任何Key的值發(fā)生了變化,EXEC命令執(zhí)行的事務(wù)都將被放棄,同時(shí)返回Null multi-bulk應(yīng)答以通知調(diào)用者事務(wù)
執(zhí)行失敗。例如,我們再次假設(shè)Redis中并未提供incr命令來完成鍵值的原子性遞增,如果要實(shí)現(xiàn)該功能,我們只能自行編寫相應(yīng)的代碼。其偽碼如下:
val = GET mykey
val = val + 1
SET mykey $val
以上代碼只有在單連接的情況下才可以保證執(zhí)行結(jié)果是正確的,因?yàn)槿绻谕粫r(shí)刻有多個(gè)客戶端在同時(shí)執(zhí)行該段代碼,那么就會(huì)出現(xiàn)多線程程序中經(jīng)常出現(xiàn)的一種錯(cuò)誤場景--競態(tài)爭用(race condition)。比如,客戶端A和B都在同一時(shí)刻讀取了mykey的原有值,假設(shè)該值為10,此后兩個(gè)客戶端又均將該值加一后set回Redis服務(wù)器,這樣就會(huì)導(dǎo)致mykey的結(jié)果為11,而不是我們認(rèn)為的12。為了解決類似的問題,我們需要借助WATCH命令的幫助,見如下代碼:
WATCH mykey
val = GET mykey
val = val + 1
MULTI
SET mykey $val
EXEC
和此前代碼不同的是,新代碼在獲取mykey的值之前先通過WATCH命令監(jiān)控了該鍵,此后又將set命令包圍在事務(wù)中,這樣就可以有效的保證每個(gè)連接在執(zhí)行EXEC之前,如果當(dāng)前連接獲取的mykey的值被其它連接的客戶端修改,那么當(dāng)前連接的EXEC命令將執(zhí)行失敗。這樣調(diào)用者在判斷返回值后就可以獲悉val是否被重新設(shè)置成功。
17.redis持久化的幾種方式
1、快照(snapshots)
缺省情況情況下,Redis把數(shù)據(jù)快照存放在磁盤上的二進(jìn)制文件中,文件名為dump.rdb。你可以配置Redis的持久化策略,例如數(shù)據(jù)集中每N秒鐘有超過M次更新,就將數(shù)據(jù)寫入磁盤;或者你可以手工調(diào)用命令SAVE或BGSAVE。
工作原理
. Redis forks.
. 子進(jìn)程開始將數(shù)據(jù)寫到臨時(shí)RDB文件中。
. 當(dāng)子進(jìn)程完成寫RDB文件,用新文件替換老文件。
. 這種方式可以使Redis使用copy-on-write技術(shù)。
2、AOF
快照模式并不十分健壯,當(dāng)系統(tǒng)停止,或者無意中Redis被kill掉,最后寫入Redis的數(shù)據(jù)就會(huì)丟失。這對某些應(yīng)用也許不是大問題,但對于要求高可靠性的應(yīng)用來說,
Redis就不是一個(gè)合適的選擇。
Append-only文件模式是另一種選擇。
你可以在配置文件中打開AOF模式
3、虛擬內(nèi)存方式
當(dāng)你的key很小而value很大時(shí),使用VM的效果會(huì)比較好.因?yàn)檫@樣節(jié)約的內(nèi)存比較大.
當(dāng)你的key不小時(shí),可以考慮使用一些非常方法將很大的key變成很大的value,比如你可以考慮將key,value組合成一個(gè)新的value.
vm-max-threads這個(gè)參數(shù),可以設(shè)置訪問swap文件的線程數(shù),設(shè)置最好不要超過機(jī)器的核數(shù),如果設(shè)置為0,那么所有對swap文件的操作都是串行的.可能會(huì)造成比較長時(shí)間的延遲,但是對數(shù)據(jù)完整性有很好的保證.
自己測試的時(shí)候發(fā)現(xiàn)用虛擬內(nèi)存性能也不錯(cuò)。如果數(shù)據(jù)量很大,可以考慮分布式或者其他數(shù)據(jù)庫
18.redis的緩存失效策略和主鍵失效機(jī)制
作為緩存系統(tǒng)都要定期清理無效數(shù)據(jù),就需要一個(gè)主鍵失效和淘汰策略.
在Redis當(dāng)中,有生存期的key被稱為volatile。在創(chuàng)建緩存時(shí),要為給定的key設(shè)置生存期,當(dāng)key過期的時(shí)候(生存期為0),它可能會(huì)被刪除。
1、影響生存時(shí)間的一些操作
生存時(shí)間可以通過使用 DEL 命令來刪除整個(gè) key 來移除,或者被 SET 和 GETSET 命令覆蓋原來的數(shù)據(jù),也就是說,修改key對應(yīng)的value和使用另外相同的key和value來覆蓋以后,當(dāng)前數(shù)據(jù)的生存時(shí)間不同。
比如說,對一個(gè) key 執(zhí)行INCR命令,對一個(gè)列表進(jìn)行LPUSH命令,或者對一個(gè)哈希表執(zhí)行HSET命令,這類操作都不會(huì)修改 key 本身的生存時(shí)間。另一方面,如果使用RENAME對一個(gè) key 進(jìn)行改名,那么改名后的 key的生存時(shí)間和改名前一樣。
RENAME命令的另一種可能是,嘗試將一個(gè)帶生存時(shí)間的 key 改名成另一個(gè)帶生存時(shí)間的 another_key ,這時(shí)舊的 another_key (以及它的生存時(shí)間)會(huì)被刪除,然后舊的 key 會(huì)改名為 another_key ,因此,新的 another_key 的生存時(shí)間也和原本的 key 一樣。使用PERSIST命令可以在不刪除 key 的情況下,移除 key 的生存時(shí)間,讓 key 重新成為一個(gè)persistent key 。
2、如何更新生存時(shí)間
可以對一個(gè)已經(jīng)帶有生存時(shí)間的 key 執(zhí)行EXPIRE命令,新指定的生存時(shí)間會(huì)取代舊的生存時(shí)間。過期時(shí)間的精度已經(jīng)被控制在1ms之內(nèi),主鍵失效的時(shí)間復(fù)雜度是O(1),
EXPIRE和TTL命令搭配使用,TTL可以查看key的當(dāng)前生存時(shí)間。設(shè)置成功返回 1;當(dāng) key 不存在或者不能為 key 設(shè)置生存時(shí)間時(shí),返回 0 。
最大緩存配置
在 redis 中,允許用戶設(shè)置最大使用內(nèi)存大小
server.maxmemory
默認(rèn)為0,沒有指定最大緩存,如果有新的數(shù)據(jù)添加,超過最大內(nèi)存,則會(huì)使redis崩潰,所以一定要設(shè)置。redis 內(nèi)存數(shù)據(jù)集大小上升到一定大小的時(shí)候,就會(huì)實(shí)行數(shù)據(jù)淘汰策略。
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ù)
注意這里的6種機(jī)制,volatile和allkeys規(guī)定了是對已設(shè)置過期時(shí)間的數(shù)據(jù)集淘汰數(shù)據(jù)還是從全部數(shù)據(jù)集淘汰數(shù)據(jù),后面的lru、ttl以及random是三種不同的淘汰策略,再加上一種no-enviction永不回收的策略。
使用策略規(guī)則:
1、如果數(shù)據(jù)呈現(xiàn)冪律分布,也就是一部分?jǐn)?shù)據(jù)訪問頻率高,一部分?jǐn)?shù)據(jù)訪問頻率低,則使用allkeys-lru
2、如果數(shù)據(jù)呈現(xiàn)平等分布,也就是所有的數(shù)據(jù)訪問頻率都相同,則使用allkeys-random
三種數(shù)據(jù)淘汰策略:
ttl和random比較容易理解,實(shí)現(xiàn)也會(huì)比較簡單。主要是Lru最近最少使用淘汰策略,設(shè)計(jì)上會(huì)對key 按失效時(shí)間排序,然后取最先失效的key進(jìn)行淘汰
19.redis 最適合的場景
Redis最適合所有數(shù)據(jù)in-momory的場景,雖然Redis也提供持久化功能,但實(shí)際更多的是一個(gè)disk-backed的功能,跟傳統(tǒng)意義上的持久化有比較大的差別,那么可能大家就會(huì)有疑問,似乎Redis更像一個(gè)加強(qiáng)版的Memcached,那么何時(shí)使用Memcached,何時(shí)使用Redis呢?
如果簡單地比較Redis與Memcached的區(qū)別,大多數(shù)都會(huì)得到以下觀點(diǎn):
1 、Redis不僅僅支持簡單的k/v類型的數(shù)據(jù),同時(shí)還提供list,set,zset,hash等數(shù)據(jù)結(jié)構(gòu)的存儲(chǔ)。
2 、Redis支持?jǐn)?shù)據(jù)的備份,即master-slave模式的數(shù)據(jù)備份。
3 、Redis支持?jǐn)?shù)據(jù)的持久化,可以將內(nèi)存中的數(shù)據(jù)保持在磁盤中,重啟的時(shí)候可以再次加載進(jìn)行使用。
(1)會(huì)話緩存(Session Cache)
最常用的一種使用Redis的情景是會(huì)話緩存(session cache)。用Redis緩存會(huì)話比其他存儲(chǔ)(如Memcached)的優(yōu)勢在于:Redis提供持久化。當(dāng)維護(hù)一個(gè)不是嚴(yán)格要求一致性的緩存時(shí),如果用戶的購物車信息全部丟失,大部分人都會(huì)不高興的,現(xiàn)在,
他們還會(huì)這樣嗎?
幸運(yùn)的是,隨著 Redis 這些年的改進(jìn),很容易找到怎么恰當(dāng)?shù)氖褂肦edis來緩存會(huì)話的文檔。甚至廣為人知的商業(yè)平臺(tái)Magento也提供Redis的插件。
(2)全頁緩存(FPC)
除基本的會(huì)話token之外,Redis還提供很簡便的FPC平臺(tái)。回到一致性問題,即使重啟了Redis實(shí)例,因?yàn)橛写疟P的持久化,用戶也不會(huì)看到頁面加載速度的下降,這是一個(gè)極大改進(jìn),類似PHP本地FPC。
再次以Magento為例,Magento提供一個(gè)插件來使用Redis作為全頁緩存后端。
此外,對WordPress的用戶來說,Pantheon有一個(gè)非常好的插件 wp-redis,這個(gè)插件能幫助你以最快速度加載你曾瀏覽過的頁面。
(3)隊(duì)列
Reids在內(nèi)存存儲(chǔ)引擎領(lǐng)域的一大優(yōu)點(diǎn)是提供 list 和 set 操作,這使得Redis能作為一個(gè)很好的消息隊(duì)列平臺(tái)來使用。Redis作為隊(duì)列使用的操作,就類似于本地程序語言(如Python)對 list 的 push/pop 操作。
如果你快速的在Google中搜索“Redis queues”,你馬上就能找到大量的開源項(xiàng)目,這些項(xiàng)目的目的就是利用Redis創(chuàng)建非常好的后端工具,以滿足各種隊(duì)列需求。例如,Celery有一個(gè)后臺(tái)就是使用Redis作為broker,你可以從這里去查看。
(4)排行榜/計(jì)數(shù)器
Redis在內(nèi)存中對數(shù)字進(jìn)行遞增或遞減的操作實(shí)現(xiàn)的非常好。集合(Set)和有序集合(Sorted Set)也使得我們在執(zhí)行這些操作的時(shí)候變的非常簡單,Redis只是正好提供了這兩種數(shù)據(jù)結(jié)構(gòu)。所以,我們要從排序集合中獲取到排名最靠前的10個(gè)用戶–我們
稱之為“user_scores”,我們只需要像下面一樣執(zhí)行即可:
當(dāng)然,這是假定你是根據(jù)你用戶的分?jǐn)?shù)做遞增的排序。如果你想返回用戶及用戶的分?jǐn)?shù),你需要這樣執(zhí)行:
ZRANGE user_scores 0 10 WITHSCORES
Agora Games就是一個(gè)很好的例子,用Ruby實(shí)現(xiàn)的,它的排行榜就是使用Redis來存儲(chǔ)數(shù)據(jù)的,你可以在這里看到。
(5)發(fā)布/訂閱
最后(但肯定不是最不重要的)是Redis的發(fā)布/訂閱功能。發(fā)布/訂閱的使用場景確實(shí)非常多。我已看見人們在社交網(wǎng)絡(luò)連接中使用,還可作為基于發(fā)布/訂閱的腳本觸發(fā)器,甚至用Redis的發(fā)布/訂閱功能來建立聊天系統(tǒng)!(不,這是真的,你可以去核
實(shí))。
Redis提供的所有特性中,我感覺這個(gè)是喜歡的人最少的一個(gè),雖然它為用戶提供如果此多功能。
20.Reids的特點(diǎn)
Redis本質(zhì)上是一個(gè)Key-Value類型的內(nèi)存數(shù)據(jù)庫,很像memcached,整個(gè)數(shù)據(jù)庫統(tǒng)統(tǒng)加載在內(nèi)存當(dāng)中進(jìn)行操作,定期通過異步操作把數(shù)據(jù)庫數(shù)據(jù)flush到硬盤上進(jìn)行保存。因?yàn)槭羌儍?nèi)存操作,Redis的性能非常出色,每秒可以處理超過 10萬次讀寫操作,是已知性能最快的Key-Value DB。
Redis的出色之處不僅僅是性能,Redis最大的魅力是支持保存多種數(shù)據(jù)結(jié)構(gòu),此外單個(gè)value的最大限制是1GB,不像 memcached只能保存1MB的數(shù)據(jù),因此Redis可以用來實(shí)現(xiàn)很多有用的功能,比方說用他的List來做FIFO雙向鏈表,實(shí)現(xiàn)一個(gè)輕量級的高性 能消息隊(duì)列服務(wù),用他的Set可以做高性能的tag系統(tǒng)等等。另外Redis也可以對存入的Key-Value設(shè)置expire時(shí)間,因此也可以被當(dāng)作一 個(gè)功能加強(qiáng)版的memcached來用。
Redis的主要缺點(diǎn)是數(shù)據(jù)庫容量受到物理內(nèi)存的限制,不能用作海量數(shù)據(jù)的高性能讀寫,因此Redis適合的場景主要局限在較小數(shù)據(jù)量的高性能操作和運(yùn)算上。
21.讀寫分離模型
通過增加Slave DB的數(shù)量,讀的性能可以線性增長。為了避免Master DB的單點(diǎn)故障,集群一般都會(huì)采用兩臺(tái)Master DB做雙機(jī)熱備,所以整個(gè)集群的讀和寫的可用性都非常高。
讀寫分離架構(gòu)的缺陷在于,不管是Master還是Slave,每個(gè)節(jié)點(diǎn)都必須保存完整的數(shù)據(jù),如果在數(shù)據(jù)量很大的情況下,集群的擴(kuò)展能力還是受限于單個(gè)節(jié)點(diǎn)的存儲(chǔ)能力,而且對于Write-intensive類型的應(yīng)用,讀寫分離架構(gòu)并不適合。
22.數(shù)據(jù)分片模型
為了解決讀寫分離模型的缺陷,可以將數(shù)據(jù)分片模型應(yīng)用進(jìn)來。
可以將每個(gè)節(jié)點(diǎn)看成都是獨(dú)立的master,然后通過業(yè)務(wù)實(shí)現(xiàn)數(shù)據(jù)分片。
結(jié)合上面兩種模型,可以將每個(gè)master設(shè)計(jì)成由一個(gè)master和多個(gè)slave組成的模型。
23.使用過Redis分布式鎖么,它是什么回事?
先拿setnx來爭搶鎖,搶到之后,再用expire給鎖加一個(gè)過期時(shí)間防止鎖忘記了釋放。
24.如果在setnx之后執(zhí)行expire之前進(jìn)程意外crash或者要重啟維護(hù)了,那會(huì)怎么樣?
25.如果這個(gè)redis正在給線上的業(yè)務(wù)提供服務(wù),那使用keys指令會(huì)有什么問題?
這個(gè)時(shí)候你要回答redis關(guān)鍵的一個(gè)特性:redis的單線程的。keys指令會(huì)導(dǎo)致線程阻塞一段時(shí)間,線上服務(wù)會(huì)停頓,直到指令執(zhí)行完畢,服務(wù)才能恢復(fù)。這個(gè)時(shí)候可以使用scan指令,scan指令可以無阻塞的提取出指定模式的key列表,但是會(huì)有一定的重復(fù)概率,在客戶端做一次去重就可以了,但是整體所花費(fèi)的時(shí)間會(huì)比直接用keys指令長。
26.使用過Redis做異步隊(duì)列么,你是怎么用的?
一般使用list結(jié)構(gòu)作為隊(duì)列,rpush生產(chǎn)消息,lpop消費(fèi)消息。當(dāng)lpop沒有消息的時(shí)候,要適當(dāng)sleep一會(huì)再重試。
如果對方追問可不可以不用sleep呢?list還有個(gè)指令叫blpop,在沒有消息的時(shí)候,它會(huì)阻塞住直到消息到來。
如果對方追問能不能生產(chǎn)一次消費(fèi)多次呢?使用pub/sub主題訂閱者模式,可以實(shí)現(xiàn)1:N的消息隊(duì)列。
如果對方追問pub/sub有什么缺點(diǎn)?在消費(fèi)者下線的情況下,生產(chǎn)的消息會(huì)丟失,得使用專業(yè)的消息隊(duì)列如rabbitmq等。
如果對方追問redis如何實(shí)現(xiàn)延時(shí)隊(duì)列?我估計(jì)現(xiàn)在你很想把面試官一棒打死如果你手上有一根棒球棍的話,怎么問的這么詳細(xì)。但是你很克制,然后神態(tài)自若的回答道:使用sortedset,拿時(shí)間戳作為score,消息內(nèi)容作為key調(diào)用zadd來生產(chǎn)消息,消費(fèi)者用zrangebyscore指令獲取N秒之前的數(shù)據(jù)輪詢進(jìn)行處理。
到這里,面試官暗地里已經(jīng)對你豎起了大拇指。但是他不知道的是此刻你卻豎起了中指,在椅子背后。
27.如果有大量的key需要設(shè)置同一時(shí)間過期,一般需要注意什么?
如果大量的key過期時(shí)間設(shè)置的過于集中,到過期的那個(gè)時(shí)間點(diǎn),redis可能會(huì)出現(xiàn)短暫的卡頓現(xiàn)象。一般需要在時(shí)間上加一個(gè)隨機(jī)值,使得過期時(shí)間分散一些。
28.Redis如何做持久化的?
bgsave做鏡像全量持久化,aof做增量持久化。因?yàn)閎gsave會(huì)耗費(fèi)較長時(shí)間,不夠?qū)崟r(shí),在停機(jī)的時(shí)候會(huì)導(dǎo)致大量丟失數(shù)據(jù),所以需要aof來配合使用。在redis實(shí)例重啟時(shí),會(huì)使用bgsave持久化文件重新構(gòu)建內(nèi)存,再使用aof重放近期的操作指令來實(shí)現(xiàn)完整恢復(fù)重啟之前的狀態(tài)。
對方追問如果突然機(jī)器掉電會(huì)怎樣?取決于aof日志sync屬性的配置,如果不要求性能,在每條寫指令時(shí)都sync一下磁盤,就不會(huì)丟失數(shù)據(jù)。但是在高性能的要求下每次都sync是不現(xiàn)實(shí)的,一般都使用定時(shí)sync,比如1s1次,這個(gè)時(shí)候最多就會(huì)丟失1s的數(shù)據(jù)。
對方追問bgsave的原理是什么?你給出兩個(gè)詞匯就可以了,fork和cow。fork是指redis通過創(chuàng)建子進(jìn)程來進(jìn)行bgsave操作,cow指的是copy on write,子進(jìn)程創(chuàng)建后,父子進(jìn)程共享數(shù)據(jù)段,父進(jìn)程繼續(xù)提供讀寫服務(wù),寫臟的頁面數(shù)據(jù)會(huì)逐漸和子進(jìn)程分離開來。
29.Pipeline有什么好處,為什么要用pipeline?
可以將多次IO往返的時(shí)間縮減為一次,前提是pipeline執(zhí)行的指令之間沒有因果相關(guān)性。使用redis-benchmark進(jìn)行壓測的時(shí)候可以發(fā)現(xiàn)影響redis的QPS峰值的一個(gè)重要因素是pipeline批次指令的數(shù)目。
**附: 但是注意,如果使用`Pipeline`。當(dāng)節(jié)點(diǎn)個(gè)數(shù)擴(kuò)充后,會(huì)導(dǎo)致長連接數(shù)目成倍數(shù)上漲。**
30.Redis的同步機(jī)制了解么?
Redis可以使用主從同步,從從同步。第一次同步時(shí),主節(jié)點(diǎn)做一次bgsave,并同時(shí)將后續(xù)修改操作記錄到內(nèi)存buffer,待完成后將rdb文件全量同步到復(fù)制節(jié)點(diǎn),復(fù)制節(jié)點(diǎn)接受完成后將rdb鏡像加載到內(nèi)存。加載完成后,再通知主節(jié)點(diǎn)將期間修改的操作記錄同步到復(fù)制節(jié)點(diǎn)進(jìn)行重放就完成了同步過程。
31.是否使用過Redis集群,集群的原理是什么?
Redis Sentinal著眼于高可用,在master宕機(jī)時(shí)會(huì)自動(dòng)將slave提升為master,繼續(xù)提供服務(wù)。
Redis Cluster著眼于擴(kuò)展性,在單個(gè)redis內(nèi)存不足時(shí),使用Cluster進(jìn)行分片存儲(chǔ)。
總結(jié)
以上是生活随笔為你收集整理的redis面试问题(一)的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 【机器学习】今天想跟大家聊聊SVM
- 下一篇: scala学习笔记-Array、Arra