Redis,全稱:Remote Dictionary Server,是一個(gè)基于內(nèi)存的高性能key-value數(shù)據(jù)庫,是應(yīng)用服務(wù)提高效率和性能必不可少的一部分,因?yàn)楫?dāng)前大部分的應(yīng)用都離不開Redis,所以學(xué)習(xí)并熟練Redis操作已經(jīng)成為一個(gè)必不可少的技能。當(dāng)然,面試中,Redis也深受面試官喜愛,下面就為大家整理匯總Redis的高頻面試題,希望能給鄉(xiāng)親們一點(diǎn)幫助。
1、什么是 Redis?簡述它的優(yōu)缺點(diǎn)?
Redis 的全稱是:Remote Dictionary.Server,本質(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)算上。
2、使用redis有哪些好處?
速度快,因?yàn)閿?shù)據(jù)存在內(nèi)存中,類似于HashMap,HashMap的優(yōu)勢就是查找和操作的時(shí)間復(fù)雜度都是O(1)。
支持豐富數(shù)據(jù)類型,支持string,list,set,sorted set,hash
支持事務(wù),操作都是原子性,所謂的原子性就是對數(shù)據(jù)的更改要么全部執(zhí)行,要么全部不執(zhí)行。
豐富的特性:可用于緩存,消息,按key設(shè)置過期時(shí)間,過期后將會自動刪除。
3、Redis支持哪幾種數(shù)據(jù)類型?
答:Redis支持五種數(shù)據(jù)類型:string(字符串),hash(哈希),list(列表),set(集合)及zset(sorted set:有序集合)。
還有一些數(shù)據(jù)結(jié)構(gòu)如HyperLogLog、Geo、Pub/Sub等,我們也最好知道,另外像Redis Module,像BloomFilter,RedisSearch,Redis-ML等,能有個(gè)印象,哪怕知其然不知其所以然也比聽都沒聽過好點(diǎn)。
4、Redis有哪幾種淘汰策略?
Redis的內(nèi)存淘汰策略是指在Redis的用于緩存的內(nèi)存不足時(shí),怎么處理需要新寫入且需要申請額外空間的數(shù)據(jù)。
no-eviction:當(dāng)內(nèi)存不足以容納新寫入數(shù)據(jù)時(shí),新寫入操作會報(bào)錯(cuò)。
allkeys-lru:當(dāng)內(nèi)存不足以容納新寫入數(shù)據(jù)時(shí),在鍵空間中,移除最近最少使用的key。
allkeys-random:當(dāng)內(nèi)存不足以容納新寫入數(shù)據(jù)時(shí),在鍵空間中,隨機(jī)移除某個(gè)key。
volatile-lru:當(dāng)內(nèi)存不足以容納新寫入數(shù)據(jù)時(shí),在設(shè)置了過期時(shí)間的鍵空間中,移除最近最少使用的key。
volatile-random:當(dāng)內(nèi)存不足以容納新寫入數(shù)據(jù)時(shí),在設(shè)置了過期時(shí)間的鍵空間中,隨機(jī)移除某個(gè)key。
volatile-ttl:當(dāng)內(nèi)存不足以容納新寫入數(shù)據(jù)時(shí),在設(shè)置了過期時(shí)間的鍵空間中,有更早過期時(shí)間的key優(yōu)先移除。
注意這里的6種機(jī)制,volatile和allkeys規(guī)定了是對已設(shè)置過期時(shí)間的數(shù)據(jù)集淘汰數(shù)據(jù)還是從全部數(shù)據(jù)集淘汰數(shù)據(jù),后面的lru、ttl以及random是三種不同的淘汰策略,再加上一種no-enviction永不回收的策略?! ?br> 使用策略規(guī)則:
如果數(shù)據(jù)呈現(xiàn)冪律分布,也就是一部分?jǐn)?shù)據(jù)訪問頻率高,一部分?jǐn)?shù)據(jù)訪問頻率低,則使用allkeys-lru
如果數(shù)據(jù)呈現(xiàn)平等分布,也就是所有的數(shù)據(jù)訪問頻率都相同,則使用allkeys-random
5、為什么 Redis 需要把所有數(shù)據(jù)放到內(nèi)存中?
答:Redis 為了達(dá)到最快的讀寫速度將數(shù)據(jù)都讀到內(nèi)存中,并通過異步的方式將數(shù)據(jù)寫入磁盤。所以 redis 具有快速和數(shù)據(jù)持久化的特征,如果不將數(shù)據(jù)放在內(nèi)存中,磁盤 I/O 速度為嚴(yán)重影響 redis 的 性能。
在內(nèi)存越來越便宜的今天,redis 將會越來越受歡迎, 如果設(shè)置了最大使用的內(nèi)存,則數(shù)據(jù)已有記錄數(shù)達(dá) 到內(nèi)存限值后不能繼續(xù)插入新值。
6、Redis 有哪些適合的場景?
(1)會話緩存(Session Cache)
最常用的一種使用 Redis 的情景是會話緩存(sessioncache),用 Redis 緩存會話比其他存儲(如Memcached)的優(yōu)勢在于:Redis 提供持久化。當(dāng)維護(hù)一個(gè)不是嚴(yán)格要求一致性的緩存時(shí),如果用戶的 購物車信息全部丟失,大部分人都會不高興的,現(xiàn)在,他們還會這樣嗎?
幸運(yùn)的是,隨著 Redis 這些年的改進(jìn),很容易找到怎么恰當(dāng)?shù)氖褂?Redis 來緩存會話的文檔。甚至廣為 人知的商業(yè)平臺 Magento 也提供 Redis 的插件。
(2)全頁緩存(FPC)
除基本的會話 token 之外,Redis 還提供很簡便的 FPC 平臺?;氐揭恢滦詥栴},即使重啟了 Redis 實(shí) 例,因?yàn)橛写疟P的持久化,用戶也不會看到頁面加載速度的下降,這是一個(gè)極大改進(jìn),類似 PHP 本地FPC。
再次以 Magento 為例,Magento 提供一個(gè)插件來使用 Redis 作為全頁緩存后端。此外,對 WordPress 的用戶來說,Pantheon 有一個(gè)非常好的插件 wp-redis,這個(gè)插件能幫助你以最快 速度加載你曾瀏覽過的頁面。
(3)隊(duì)列
Reids 在內(nèi)存存儲引擎領(lǐng)域的一大優(yōu)點(diǎn)是提供 list 和 set 操作,這使得 Redis 能作為一個(gè)很好的消息隊(duì)列 平臺來使用。Redis 作為隊(duì)列使用的操作,就類似于本地程序語言(如 Python)對 list 的 push/pop操作。
如果你快速的在 Google 中搜索“Redis queues”,你馬上就能找到大量的開源項(xiàng)目,這些項(xiàng)目的目的 就是利用 Redis 創(chuàng)建非常好的后端工具,以滿足各種隊(duì)列需求。例如,Celery 有一個(gè)后臺就是使用Redis 作為 broker,你可以從這里去查看。
(4)排行榜/計(jì)數(shù)器
Redis 在內(nèi)存中對數(shù)字進(jìn)行遞增或遞減的操作實(shí)現(xiàn)的非常好。集合(Set)和有序集合(SortedSet)也使 得我們在執(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 來存儲數(shù)據(jù)的,你可 以在這里看到。
(5)發(fā)布/訂閱
最后(但肯定不是最不重要的)是 Redis 的發(fā)布/訂閱功能。發(fā)布/訂閱的使用場景確實(shí)非常多。我已看見 人們在社交網(wǎng)絡(luò)連接中使用,還可作為基于發(fā)布/訂閱的腳本觸發(fā)器,甚至用 Redis 的發(fā)布/訂閱功能來建 立聊天系統(tǒng)!
7、說說 Redis 哈希槽的概念?
Redis 集群沒有使用一致性 hash,而是引入了哈希槽的概念,Redis 集群有 16384 個(gè)哈希槽,每個(gè) key 通 過 CRC16 校驗(yàn)后對 16384 取模來決定放置哪個(gè)槽,集群的每個(gè)節(jié)點(diǎn)負(fù)責(zé)一部分 hash 槽。
8、Redis 集群的主從復(fù)制模型是怎樣的?
為了使在部分節(jié)點(diǎn)失敗或者大部分節(jié)點(diǎn)無法通信的情況下集群仍然可用,所以集群使用了主從復(fù)制模型,每個(gè)節(jié)點(diǎn)都會有 N-1 個(gè)復(fù)制品。
9、Redis 集群會有寫操作丟失嗎?為什么?
Redis 并不能保證數(shù)據(jù)的強(qiáng)一致性,這意味這在實(shí)際中集群在特定的條件下可能會丟失寫操作。
10、Redis 集群之間是如何復(fù)制的?
答:異步復(fù)制
11、Redis 中的管道有什么用?
一次請求/響應(yīng)服務(wù)器能實(shí)現(xiàn)處理新的請求即使舊的請求還未被響應(yīng),這樣就可以將多個(gè)命令發(fā)送到服務(wù) 器,而不用等待回復(fù),最后在一個(gè)步驟中讀取該答復(fù)。這就是管道(pipelining),是一種幾十年來廣泛使用的技術(shù)。例如許多 POP3 協(xié)議已經(jīng)實(shí)現(xiàn)支持這個(gè)功 能,大大加快了從服務(wù)器下載新郵件的過程。
12、怎么理解 Redis 事務(wù)?
事務(wù)是一個(gè)單獨(dú)的隔離操作:事務(wù)中的所有命令都會序列化、按順序地執(zhí)行,事務(wù)在執(zhí)行的過程中,不會 被其他客戶端發(fā)送來的命令請求所打斷。
事務(wù)是一個(gè)原子操作:事務(wù)中的命令要么全部被執(zhí)行,要么全部都不執(zhí)行。
13、Redis 事務(wù)相關(guān)的命令有哪幾個(gè)?
MULTI、EXEC、DISCARD、WATCH
14、Redis key 的過期時(shí)間和永久有效分別怎么設(shè)置?
EXPIRE 和 PERSIST 命令
15、Redis 如何做內(nèi)存優(yōu)化?
盡可能使用散列表(hashes),散列表(是說散列表里面存儲的數(shù)少)使用的內(nèi)存非常小,所以你應(yīng)該 盡可能的將你的數(shù)據(jù)模型抽象到一個(gè)散列表里面。
比如你的 web 系統(tǒng)中有一個(gè)用戶對象,不要為這個(gè)用戶的名稱,姓氏,郵箱,密碼設(shè)置單獨(dú)的 key,而是 應(yīng)該把這個(gè)用戶的所有信息存儲到一張散列表里面。
16、Redis 回收進(jìn)程如何工作的?
一個(gè)客戶端運(yùn)行了新的命令,添加了新的數(shù)據(jù)。Redi 檢查內(nèi)存使用情況,如果大于 maxmemory 的限制, 則根據(jù)設(shè)定好的策略進(jìn)行回收。一個(gè)新的命令被執(zhí)行,等等。所以我們不斷地穿越內(nèi)存限制的邊界,通過不斷達(dá)到邊界然后不斷地回收回到邊界以下。如果一個(gè)命令的結(jié)果導(dǎo)致大量內(nèi)存被使用(例如很大的集合的交集保存到一個(gè)新的鍵),不用多久內(nèi)存限 制就會被這個(gè)內(nèi)存使用量超越。
17、Redis集群最大節(jié)點(diǎn)個(gè)數(shù)是多少?
答:16384個(gè)。
18、Redis集群如何選擇數(shù)據(jù)庫?
答:Redis集群目前無法做數(shù)據(jù)庫選擇,默認(rèn)在0數(shù)據(jù)庫。
19、都有哪些辦法可以降低Redis的內(nèi)存使用情況呢?
答:如果你使用的是32位的Redis實(shí)例,可以好好利用Hash,list,sorted set,set等集合類型數(shù)據(jù),因?yàn)橥ǔG闆r下很多小的Key-Value可以用更緊湊的方式存放到一起。
20、Redis的內(nèi)存用完了會發(fā)生什么?
答:如果達(dá)到設(shè)置的上限,Redis的寫命令會返回錯(cuò)誤信息(但是讀命令還可以正常返回。)或者你可以將Redis當(dāng)緩存來使用配置淘汰機(jī)制,當(dāng)Redis達(dá)到內(nèi)存上限時(shí)會沖刷掉舊的內(nèi)容。
21、一個(gè)Redis實(shí)例最多能存放多少的keys?List、Set、Sorted Set他們最多能存放多少元素?
答:理論上Redis可以處理多達(dá)232的keys,并且在實(shí)際中進(jìn)行了測試,每個(gè)實(shí)例至少存放了2億5千萬的keys。我們正在測試一些較大的值。任何list、set、和sorted set都可以放232個(gè)元素。換句話說,Redis的存儲極限是系統(tǒng)中的可用內(nèi)存值。
22、假如Redis里面有1億個(gè)key,其中有10w個(gè)key是以某個(gè)固定的已知的前綴開頭的,如果將它們?nèi)空页鰜恚?/p>
答:使用keys指令可以掃出指定模式的key列表。
23、如果這個(gè)redis正在給線上的業(yè)務(wù)提供服務(wù),那使用keys指令會有什么問題?
答:redis的單線程的。keys指令會導(dǎo)致線程阻塞一段時(shí)間,線上服務(wù)會停頓,直到指令執(zhí)行完畢,服務(wù)才能恢復(fù)。這個(gè)時(shí)候可以使用scan指令,scan指令可以無阻塞的提取出指定模式的key列表,但是會有一定的重復(fù)概率,在客戶端做一次去重就可以了,但是整體所花費(fèi)的時(shí)間會比直接用keys指令長。
24、如果有大量的key需要設(shè)置同一時(shí)間過期,一般需要注意什么?
答:如果大量的key過期時(shí)間設(shè)置的過于集中,到過期的那個(gè)時(shí)間點(diǎn),redis可能會出現(xiàn)短暫的卡頓現(xiàn)象。一般需要在時(shí)間上加一個(gè)隨機(jī)值,使得過期時(shí)間分散一些。
25、Redis 集群方案應(yīng)該怎么做?都有哪些方案?
codis:目前用的最多的集群方案,基本和 twemproxy 一致的效果,但它支持在節(jié)點(diǎn)數(shù)量改變情況下,舊節(jié)點(diǎn)數(shù)據(jù)可恢復(fù)到新 hash 節(jié)點(diǎn)。
redis cluster3.0 自帶的集群,特點(diǎn)在于他的分布式算法不是一致性 hash,而是 hash 槽的概念,以及自身支持節(jié)點(diǎn)設(shè)置從節(jié)點(diǎn)。具體看官方文檔介紹。
在業(yè)務(wù)代碼層實(shí)現(xiàn),起幾個(gè)毫無關(guān)聯(lián)的 redis 實(shí)例,在代碼層,對 key 進(jìn)行 hash 計(jì)算,然后去對應(yīng)的redis 實(shí)例操作數(shù)據(jù)。這種方式對 hash 層代碼要求比較高,考慮部分包括,節(jié)點(diǎn)失效后的替代算法方案,數(shù)據(jù)震蕩后的自動腳本恢復(fù),實(shí)例的監(jiān)控,等等。
26、使用過 Redis 分布式鎖么,它是怎么實(shí)現(xiàn)的?
答:先拿 setnx 來爭搶鎖,搶到之后,再用 expire 給鎖加一個(gè)過期時(shí)間防止鎖忘記了釋放。
27、如果在 setnx 之后執(zhí)行 expire 之前進(jìn)程意外 crash 或者要重啟維護(hù)了,那會怎么樣?
答:set 指令有非常復(fù)雜的參數(shù),這個(gè)應(yīng)該是可以同時(shí)把 setnx 和 expire 合成一條指令來用的!
28、使用過 Redis 做異步隊(duì)列么,你是怎么用的?有什么缺點(diǎn)?
一般使用 list 結(jié)構(gòu)作為隊(duì)列,rpush 生產(chǎn)消息,lpop 消費(fèi)消息。當(dāng) lpop 沒有消息的時(shí)候,要適當(dāng) sleep 一會再重試。
缺點(diǎn):在消費(fèi)者下線的情況下,生產(chǎn)的消息會丟失,得使用專業(yè)的消息隊(duì)列如 rabbitmq 等。
能不能生產(chǎn)一次消費(fèi)多次呢?
使用 pub/sub 主題訂閱者模式,可以實(shí)現(xiàn) 1:N 的消息隊(duì)列。
29、緩存穿透、緩存擊穿、緩存雪崩解決方案?
緩存穿透:指查詢一個(gè)一定不存在的數(shù)據(jù),如果從存儲層查不到數(shù)據(jù)則不寫入緩存,這將 導(dǎo)致這個(gè)不存在的數(shù)據(jù)每次請求都要到 DB 去查詢,可能導(dǎo)致 DB 掛掉。
解決方案:
1.查詢返回的數(shù)據(jù)為空,仍把這個(gè)空結(jié)果進(jìn)行緩存,但過期時(shí)間會比較短;
2.布 隆過濾器:將所有可能存在的數(shù)據(jù)哈希到一個(gè)足夠大的 bitmap 中,一個(gè)一定不存在的數(shù)據(jù) 會被這個(gè) bitmap 攔截掉,從而避免了對 DB 的查詢。
緩存擊穿:對于設(shè)置了過期時(shí)間的 key,緩存在某個(gè)時(shí)間點(diǎn)過期的時(shí)候,恰好這時(shí)間點(diǎn)對 這個(gè) Key 有大量的并發(fā)請求過來,這些請求發(fā)現(xiàn)緩存過期一般都會從后端 DB 加載數(shù)據(jù)并 回設(shè)到緩存,這個(gè)時(shí)候大并發(fā)的請求可能會瞬間把 DB 壓垮。
解決方案:
1.使用互斥鎖:當(dāng)緩存失效時(shí),不立即去load db,先使用如Redis的setnx去設(shè) 置一個(gè)互斥鎖,當(dāng)操作成功返回時(shí)再進(jìn)行l(wèi)oad db的操作并回設(shè)緩存,否則重試get緩存的 方法。
2.永遠(yuǎn)不過期:物理不過期,但邏輯過期(后臺異步線程去刷新)。
緩存雪崩:設(shè)置緩存時(shí)采用了相同的過期時(shí)間,導(dǎo)致緩存在某一時(shí)刻同時(shí)失效,請求全部 轉(zhuǎn)發(fā)到 DB,DB 瞬時(shí)壓力過重雪崩。與緩存擊穿的區(qū)別:雪崩是很多 key,擊穿是某一個(gè)key 緩存。
解決方案:
將緩存失效時(shí)間分散開,比如可以在原有的失效時(shí)間基礎(chǔ)上增加一個(gè)隨機(jī)值, 比如 1-5 分鐘隨機(jī),這樣每一個(gè)緩存的過期時(shí)間的重復(fù)率就會降低,就很難引發(fā)集體失效 的事件。
30、為什么redis單線程還是那么快?
答:redis利用隊(duì)列技術(shù)將并發(fā)訪問變?yōu)榇性L問,消除了傳統(tǒng)數(shù)據(jù)庫串行控制的開銷。
31、使用 redis 如何設(shè)計(jì)分布式鎖?說一下實(shí)現(xiàn)思路?使用 zk 可以嗎?如何實(shí)現(xiàn)?這兩種有什 么區(qū)別?
redis:
線程 A setnx(上鎖的對象,超時(shí)時(shí)的時(shí)間戳 t1),如果返回 true,獲得鎖。
線程 B 用 get 獲取 t1,與當(dāng)前時(shí)間戳比較,判斷是是否超時(shí),沒超時(shí) false,若超時(shí)執(zhí)行第 3 步。
計(jì)算新的超時(shí)時(shí)間 t2,使用 getset 命令返回 t3(該值可能其他線程已經(jīng)修改過),如果t1==t3,獲得鎖,如果 t1!=t3 說明鎖被其他線程獲取了。4.獲取鎖后,處理完業(yè)務(wù)邏輯,再去判斷鎖是否超時(shí),如果沒超時(shí)刪除鎖,如果已超時(shí), 不用處理(防止刪除其他線程的鎖)。
zk:
客戶端對某個(gè)方法加鎖時(shí),在 zk 上的與該方法對應(yīng)的指定節(jié)點(diǎn)的目錄下,生成一個(gè)唯一 的瞬時(shí)有序節(jié)點(diǎn) node1。
客戶端獲取該路徑下所有已經(jīng)創(chuàng)建的子節(jié)點(diǎn),如果發(fā)現(xiàn)自己創(chuàng)建的 node1 的序號是最小 的,就認(rèn)為這個(gè)客戶端獲得了鎖。
如果發(fā)現(xiàn) node1 不是最小的,則監(jiān)聽比自己創(chuàng)建節(jié)點(diǎn)序號小的最大的節(jié)點(diǎn),進(jìn)入等待。
獲取鎖后,處理完邏輯,刪除自己創(chuàng)建的 node1 即可。
區(qū)別:zk 性能差一些,開銷大,實(shí)現(xiàn)簡單。
32、知道 redis 的持久化嗎?底層如何實(shí)現(xiàn)的?有什么優(yōu)點(diǎn)缺點(diǎn)?
RDB(Redis DataBase:在不同的時(shí)間點(diǎn)將 redis 的數(shù)據(jù)生成的快照同步到磁盤等介質(zhì)上):內(nèi)存 到硬盤的快照,定期更新。缺點(diǎn):耗時(shí),耗性能(fork+io 操作),易丟失數(shù)據(jù)。
AOF(Append Only File:將redis所執(zhí)行過的所有指令都記錄下來,在下次redis重啟時(shí),只 需要執(zhí)行指令就可以了):寫日志。缺點(diǎn):體積大,恢復(fù)速度慢。
bgsave 做鏡像全量持久化,aof 做增量持久化。因?yàn)?bgsave 會消耗比較長的時(shí)間,不夠?qū)?時(shí),在停機(jī)的時(shí)候會導(dǎo)致大量的數(shù)據(jù)丟失,需要 aof 來配合,在 redis 實(shí)例重啟時(shí),優(yōu)先使 用 aof 來恢復(fù)內(nèi)存的狀態(tài),如果沒有 aof 日志,就會使用 rdb 文件來恢復(fù)。Redis 會定期做aof 重寫,壓縮 aof 文件日志大小。Redis4.0 之后有了混合持久化的功能,將 bgsave 的全量 和 aof 的增量做了融合處理,這樣既保證了恢復(fù)的效率又兼顧了數(shù)據(jù)的安全性。bgsave 的 原理,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ù) 會逐漸和子進(jìn)程分離開來。
33、redis 過期策略都有哪些?LRU 算法知道嗎?寫一下 java 代碼實(shí)現(xiàn)?
過期策略:
定時(shí)過期(一 key 一定時(shí)器)。
惰性過期:只有使用 key 時(shí)才判斷 key 是否已過期,過期則清除。
定期過期:前兩者折中。
LRU:
new LinkedHashMap<K, V>(capacity, DEFAULT_LOAD_FACTORY, true);
//第三個(gè)參數(shù)置為 true,代表 linkedlist 按訪問順序排序,可作為 LRU 緩存;設(shè)為 false 代表 按插入順序排序,可作為 FIFO 緩存
LRU 算法實(shí)現(xiàn):
1.通過雙向鏈表來實(shí)現(xiàn),新數(shù)據(jù)插入到鏈表頭部;
2.每當(dāng)緩存命中(即緩存 數(shù)據(jù)被訪問),則將數(shù)據(jù)移到鏈表頭部;
3.當(dāng)鏈表滿的時(shí)候,將鏈表尾部的數(shù)據(jù)丟棄。
LinkedHashMap:HashMap 和雙向鏈表合二為一即是 LinkedHashMap。HashMap 是無序 的,LinkedHashMap 通過維護(hù)一個(gè)額外的雙向鏈表保證了迭代順序。該迭代順序可以是插 入順序(默認(rèn)),也可以是訪問順序。
34、緩存與數(shù)據(jù)庫不一致怎么辦?
答:假設(shè)采用的主存分離,讀寫分離的數(shù)據(jù)庫,
如果一個(gè)線程 A 先刪除緩存數(shù)據(jù),然后將數(shù)據(jù)寫入到主庫當(dāng)中,這個(gè)時(shí)候,主庫和從庫同 步?jīng)]有完成,線程 B 從緩存當(dāng)中讀取數(shù)據(jù)失敗,從從庫當(dāng)中讀取到舊數(shù)據(jù),然后更新至緩 存,這個(gè)時(shí)候,緩存當(dāng)中的就是舊的數(shù)據(jù)。
發(fā)生上述不一致的原因在于,主從庫數(shù)據(jù)不一致問題,加入了緩存之后,主從不一致的時(shí)間被拉長了。
處理思路:在從庫有數(shù)據(jù)更新之后,將緩存當(dāng)中的數(shù)據(jù)也同時(shí)進(jìn)行更新,即當(dāng)從庫發(fā)生了
數(shù)據(jù)更新之后,向緩存發(fā)出刪除,淘汰這段時(shí)間寫入的舊數(shù)據(jù)。
35、主從數(shù)據(jù)庫不一致如何解決?
場景描述,對于主從庫,讀寫分離,如果主從庫更新同步有時(shí)差,就會導(dǎo)致主從庫數(shù)據(jù)的
不一致,解決方法:
忽略這個(gè)數(shù)據(jù)不一致,在數(shù)據(jù)一致性要求不高的業(yè)務(wù)下,未必需要時(shí)時(shí)一致性。
強(qiáng)制讀主庫,使用一個(gè)高可用的主庫,數(shù)據(jù)庫讀寫都在主庫,添加一個(gè)緩存,提升數(shù)據(jù)讀取的性能。
選擇性讀主庫,添加一個(gè)緩存,用來記錄必須讀主庫的數(shù)據(jù),將哪個(gè)庫,哪個(gè)表,哪個(gè) 主鍵,作為緩存的 key,設(shè)置緩存失效的時(shí)間為主從庫同步的時(shí)間,如果緩存當(dāng)中有這個(gè)數(shù) 據(jù),直接讀取主庫,如果緩存當(dāng)中沒有這個(gè)主鍵,就到對應(yīng)的從庫中讀取。
36、Redis 常見的性能問題和解決方案
master 最好不要做持久化工作,如 RDB 內(nèi)存快照和 AOF 日志文件。
如果數(shù)據(jù)比較重要,某個(gè) slave 開啟 AOF 備份,策略設(shè)置成每秒同步一次。
為了主從復(fù)制的速度和連接的穩(wěn)定性,master 和 Slave 最好在一個(gè)局域網(wǎng)內(nèi)。
盡量避免在壓力大得主庫上增加從庫5、主從復(fù)制不要采用網(wǎng)狀結(jié)構(gòu),盡量是線性結(jié)構(gòu),Master<--Slave1<----Slave2 ....
37、為什么要做Redis分區(qū)?
答:分區(qū)可以讓Redis管理更大的內(nèi)存,Redis將可以使用所有機(jī)器的內(nèi)存。如果沒有分區(qū),你最多只能使用一臺機(jī)器的內(nèi)存。分區(qū)使Redis的計(jì)算能力通過簡單地增加計(jì)算機(jī)得到成倍提升,Redis的網(wǎng)絡(luò)帶寬也會隨著計(jì)算機(jī)和網(wǎng)卡的增加而成倍增長。
38、你知道有哪些Redis分區(qū)實(shí)現(xiàn)方案?
答:客戶端分區(qū)就是在客戶端就已經(jīng)決定數(shù)據(jù)會被存儲到哪個(gè)redis節(jié)點(diǎn)或者從哪個(gè)redis節(jié)點(diǎn)讀取。大多數(shù)客戶端已經(jīng)實(shí)現(xiàn)了客戶端分區(qū)。
代理分區(qū)意味著客戶端將請求發(fā)送給代理,然后代理決定去哪個(gè)節(jié)點(diǎn)寫數(shù)據(jù)或者讀數(shù)據(jù)。代理根據(jù)分區(qū)規(guī)則決定請求哪些Redis實(shí)例,然后根據(jù)Redis的響應(yīng)結(jié)果返回給客戶端。redis和memcached的一種代理實(shí)現(xiàn)就是Twemproxy
查詢路由(Query routing) 的意思是客戶端隨機(jī)地請求任意一個(gè)redis實(shí)例,然后由Redis將請求轉(zhuǎn)發(fā)給正確的Redis節(jié)點(diǎn)。Redis Cluster實(shí)現(xiàn)了一種混合形式的查詢路由,但并不是直接將請求從一個(gè)redis節(jié)點(diǎn)轉(zhuǎn)發(fā)到另一個(gè)redis節(jié)點(diǎn),而是在客戶端的幫助下直接redirected到正確的redis節(jié)點(diǎn)。
39、Redis分區(qū)有什么缺點(diǎn)?
涉及多個(gè)key的操作通常不會被支持。例如你不能對兩個(gè)集合求交集,因?yàn)樗麄兛赡鼙淮鎯Φ讲煌腞edis實(shí)例(實(shí)際上這種情況也有辦法,但是不能直接使用交集指令)。
同時(shí)操作多個(gè)key,則不能使用Redis事務(wù)。
分區(qū)使用的粒度是key,不能使用一個(gè)非常長的排序key存儲一個(gè)數(shù)據(jù)集(The partitioning granularity is the key, so it is not possible to shard a dataset with a single huge key like a very big sorted set)。
當(dāng)使用分區(qū)的時(shí)候,數(shù)據(jù)處理會非常復(fù)雜,例如為了備份你必須從不同的Redis實(shí)例和主機(jī)同時(shí)收集RDB / AOF文件。
分區(qū)時(shí)動態(tài)擴(kuò)容或縮容可能非常復(fù)雜。Redis集群在運(yùn)行時(shí)增加或者刪除Redis節(jié)點(diǎn),能做到最大程度對用戶透明地?cái)?shù)據(jù)再平衡,但其他一些客戶端分區(qū)或者代理分區(qū)方法則不支持這種特性。然而,有一種預(yù)分片的技術(shù)也可以較好的解決這個(gè)問題。
40、Redis持久化數(shù)據(jù)和緩存怎么做擴(kuò)容?
答:如果Redis被當(dāng)做緩存使用,使用一致性哈希實(shí)現(xiàn)動態(tài)擴(kuò)容縮容。
如果Redis被當(dāng)做一個(gè)持久化存儲使用,必須使用固定的keys-to-nodes映射關(guān)系,節(jié)點(diǎn)的數(shù)量一旦確定不能變化。否則的話(即Redis節(jié)點(diǎn)需要動態(tài)變化的情況),必須使用可以在運(yùn)行時(shí)進(jìn)行數(shù)據(jù)再平衡的一套系統(tǒng),而當(dāng)前只有Redis集群可以做到這樣。
41、redis的并發(fā)競爭問題如何解決?
答:Redis為單進(jìn)程單線程模式,采用隊(duì)列模式將并發(fā)訪問變?yōu)榇性L問。Redis本身沒有鎖的概念,Redis對于多個(gè)客戶端連接并不存在競爭,但是在Jedis客戶端對Redis進(jìn)行并發(fā)訪問時(shí)會發(fā)生連接超時(shí)、數(shù)據(jù)轉(zhuǎn)換錯(cuò)誤、阻塞、客戶端關(guān)閉連接等問題,這些問題均是由于客戶端連接混亂造成。
對此有2種解決方法:
1.客戶端角度,為保證每個(gè)客戶端間正常有序與Redis進(jìn)行通信,對連接進(jìn)行池化,同時(shí)對客戶端讀寫Redis操作采用內(nèi)部鎖synchronized。
2.服務(wù)器角度,利用setnx實(shí)現(xiàn)鎖。
注:對于第一種,需要應(yīng)用程序自己處理資源的同步,可以使用的方法比較通俗,可以使用synchronized也可以使用lock;第二種需要用到Redis的setnx命令,但是需要注意一些問題。
42、簡述redis的哨兵模式
答:哨兵是對redis進(jìn)行實(shí)時(shí)的監(jiān)控,主要有兩個(gè)功能。
監(jiān)測主數(shù)據(jù)庫和從數(shù)據(jù)庫是否正常運(yùn)行。
當(dāng)主數(shù)據(jù)庫出現(xiàn)故障的時(shí)候,可以自動將一個(gè)從數(shù)據(jù)庫轉(zhuǎn)換為主數(shù)據(jù)庫,實(shí)現(xiàn)自動切換。
43、redis的哨兵的監(jiān)控機(jī)制是怎樣的?
答:哨兵監(jiān)控也是有集群的,會有多個(gè)哨兵進(jìn)行監(jiān)控,當(dāng)判斷發(fā)生故障的哨兵達(dá)到一定數(shù)量的時(shí)候才進(jìn)行修復(fù)。一個(gè)健壯的部署至少需要三個(gè)哨兵實(shí)例。
1.每個(gè)Sentinel以每秒鐘一次的頻率向它所知的Master,Slave以及其他 Sentinel 實(shí)例發(fā)送一個(gè) PING 命令
2.如果一個(gè)實(shí)例(instance)距離最后一次有效回復(fù) PING 命令的時(shí)間超過 down-after-milliseconds 選項(xiàng)所指定的值, 則這個(gè)實(shí)例會被 Sentinel 標(biāo)記為主觀下線。
3.如果一個(gè)Master被標(biāo)記為主觀下線,則正在監(jiān)視這個(gè)Master的所有 Sentinel 要以每秒一次的頻率確認(rèn)Master的確進(jìn)入了主觀下線狀態(tài)。
4.當(dāng)有足夠數(shù)量的 Sentinel(大于等于配置文件指定的值)在指定的時(shí)間范圍內(nèi)確認(rèn)Master的確進(jìn)入了主觀下線狀態(tài), 則Master會被標(biāo)記為客觀下線
5.在一般情況下, 每個(gè) Sentinel 會以每 10 秒一次的頻率向它已知的所有Master,Slave發(fā)送 INFO 命令
6.當(dāng)Master被 Sentinel 標(biāo)記為客觀下線時(shí),Sentinel 向下線的 Master 的所有 Slave 發(fā)送 INFO 命令的頻率會從 10 秒一次改為每秒一次
7.若沒有足夠數(shù)量的 Sentinel 同意 Master 已經(jīng)下線, Master 的客觀下線狀態(tài)就會被移除。若 Master 重新向 Sentinel 的 PING 命令返回有效回復(fù), Master 的主觀下線狀態(tài)就會被移除。
Redis高頻面試題就整理到這兒,總感覺遺漏了點(diǎn)什么,又忽然間想不起來,希望看完的鄉(xiāng)親們?nèi)缬醒a(bǔ)充的,麻煩留言告訴一聲,讓我補(bǔ)充的更完整一些。
更多面試題可以關(guān)注我的個(gè)人公眾號【碼之初】,真心的希望可以給你的面試之路助上一臂之力,愿你被生活溫柔以待,加油!