1. 程式人生 > >wu版-天下無難試之Redis面試題刁難大全

wu版-天下無難試之Redis面試題刁難大全

面試中可能會被問到的Redis問題整理,希望能彙總各種奇葩問題,幫助更多的人思考學習。大多非作者原創,只是收集整理而來。

Redis在網際網路技術儲存方面使用如此廣泛,幾乎所有的後端技術面試官都要在Redis的使用和原理方面對小夥伴們進行各種刁難。作為一名在網際網路技術行業打擊過成百上千名【請允許我誇張一下】的資深技術面試官,看過了無數落寞的身影失望的離開,略感愧疚,故獻上此文,希望各位讀者以後面試勢如破竹,永無失敗!

=========================此部分為常見問題相信會大力幫到你===========================

這一部分一般是會在你的專案中提交,面試官很容易揪著一直問,所以一定要有深度。可以抓取一個點收集一系列的相關問題,我在這裡後續也會進行新增。

Redis有哪些資料結構?

字串String、字典Hash、列表List、集合Set、有序集合SortedSet。

如果你是Redis中高階使用者,還需要加上下面幾種資料結構HyperLogLog、Geo、Pub/Sub。

如果你說還玩過Redis Module,像BloomFilter,RedisSearch,Redis-ML,面試官得眼睛就開始發亮了。

使用過Redis分散式鎖麼,它是什麼回事?

先拿setnx來爭搶鎖,搶到之後,再用expire給鎖加一個過期時間防止鎖忘記了釋放。

這時候對方會告訴你說你回答得不錯,然後接著問如果在setnx之後執行expire之前程序意外crash或者要重啟維護了,那會怎麼樣?

這時候你要給予驚訝的反饋:唉,是喔,這個鎖就永遠得不到釋放了。緊接著你需要抓一抓自己得腦袋,故作思考片刻,好像接下來的結果是你主動思考出來的,然後回答:我記得set指令有非常複雜的引數,這個應該是可以同時把setnx和expire合成一條指令來用的!對方這時會顯露笑容,心裡開始默唸:摁,這小子還不錯。

假如Redis裡面有1億個key,其中有10w個key是以某個固定的已知的字首開頭的,如果將它們全部找出來?

使用keys指令可以掃出指定模式的key列表。

對方接著追問:如果這個redis正在給線上的業務提供服務,那使用keys指令會有什麼問題?

這個時候你要回答redis關鍵的一個特性:redis的單執行緒的。keys指令會導致執行緒阻塞一段時間,線上服務會停頓,直到指令執行完畢,服務才能恢復。這個時候可以使用scan指令,scan指令可以無阻塞的提取出指定模式的key列表,但是會有一定的重複概率,在客戶端做一次去重就可以了,但是整體所花費的時間會比直接用keys指令長。

使用過Redis做非同步佇列麼,你是怎麼用的?

一般使用list結構作為佇列,rpush生產訊息,lpop消費訊息。當lpop沒有訊息的時候,要適當sleep一會再重試。

如果對方追問可不可以不用sleep呢?list還有個指令叫blpop,在沒有訊息的時候,它會阻塞住直到訊息到來。

如果對方追問能不能生產一次消費多次呢?使用pub/sub主題訂閱者模式,可以實現1:N的訊息佇列。

如果對方追問pub/sub有什麼缺點?在消費者下線的情況下,生產的訊息會丟失,得使用專業的訊息佇列如rabbitmq等。

如果對方追問redis如何實現延時佇列?我估計現在你很想把面試官一棒打死如果你手上有一根棒球棍的話,怎麼問的這麼詳細。但是你很剋制,然後神態自若的回答道:使用sortedset,拿時間戳作為score,訊息內容作為key呼叫zadd來生產訊息,消費者用zrangebyscore指令獲取N秒之前的資料輪詢進行處理。

到這裡,面試官暗地裡已經對你豎起了大拇指。但是他不知道的是此刻你卻豎起了中指,在椅子背後。

如果有大量的key需要設定同一時間過期,一般需要注意什麼?

如果大量的key過期時間設定的過於集中,到過期的那個時間點,redis可能會出現短暫的卡頓現象。一般需要在時間上加一個隨機值,使得過期時間分散一些。

Redis如何做持久化的?

bgsave做映象全量持久化,aof做增量持久化。因為bgsave會耗費較長時間,不夠實時,在停機的時候會導致大量丟失資料,所以需要aof來配合使用。在redis例項重啟時,會使用bgsave持久化檔案重新構建記憶體,再使用aof重放近期的操作指令來實現完整恢復重啟之前的狀態。

對方追問那如果突然機器掉電會怎樣?取決於aof日誌sync屬性的配置,如果不要求效能,在每條寫指令時都sync一下磁碟,就不會丟失資料。但是在高效能的要求下每次都sync是不現實的,一般都使用定時sync,比如1s1次,這個時候最多就會丟失1s的資料。

對方追問bgsave的原理是什麼?你給出兩個詞彙就可以了,fork和cow。fork是指redis通過建立子程序來進行bgsave操作,cow指的是copy on write,子程序建立後,父子程序共享資料段,父程序繼續提供讀寫服務,寫髒的頁面資料會逐漸和子程序分離開來。

Pipeline有什麼好處,為什麼要用pipeline?

可以將多次IO往返的時間縮減為一次,前提是pipeline執行的指令之間沒有因果相關性。使用redis-benchmark進行壓測的時候可以發現影響redis的QPS峰值的一個重要因素是pipeline批次指令的數目。

Redis的同步機制瞭解麼?

Redis可以使用主從同步,從從同步。第一次同步時,主節點做一次bgsave,並同時將後續修改操作記錄到記憶體buffer,待完成後將rdb檔案全量同步到複製節點,複製節點接受完成後將rdb映象載入到記憶體。載入完成後,再通知主節點將期間修改的操作記錄同步到複製節點進行重放就完成了同步過程。

是否使用過Redis叢集,叢集的原理是什麼?

Redis Sentinal著眼於高可用,在master宕機時會自動將slave提升為master,繼續提供服務。

Redis Cluster著眼於擴充套件性,在單個redis記憶體不足時,使用Cluster進行分片儲存。

======================出現概率不如上面(瞭解)=====================================

mySQL裡有2000w資料,redis中只存20w的資料,如何保證redis中的資料都是熱點資料

相關知識:redis 記憶體資料集大小上升到一定大小的時候,就會施行資料淘汰策略。redis 提供 6種資料淘汰策略:

 volatile-lru:從已設定過期時間的資料集(server.db[i].expires)中挑選最近最少使用的資料淘汰

volatile-ttl:從已設定過期時間的資料集(server.db[i].expires)中挑選將要過期的資料淘汰

volatile-random:從已設定過期時間的資料集(server.db[i].expires)中任意選擇資料淘汰

allkeys-lru:從資料集(server.db[i].dict)中挑選最近最少使用的資料淘汰

allkeys-random:從資料集(server.db[i].dict)中任意選擇資料淘汰

no-enviction(驅逐):禁止驅逐資料

Memcache與Redis的區別都有哪些?

 1)、儲存方式

 Memecache把資料全部存在記憶體之中,斷電後會掛掉,資料不能超過記憶體大小。

Redis有部份存在硬碟上,這樣能保證資料的永續性。

2)、資料支援型別

 Memcache對資料型別支援相對簡單。

Redis有複雜的資料型別。

3)、使用底層模型不同

 它們之間底層實現方式 以及與客戶端之間通訊的應用協議不一樣。

Redis直接自己構建了VM 機制 ,因為一般的系統呼叫系統函式的話,會浪費一定的時間去移動和請求。

Redis 常見的效能問題都有哪些?如何解決?

 1).Master寫記憶體快照,save命令排程rdbSave函式,會阻塞主執行緒的工作,當快照比較大時對效能影響是非常大的,會間斷性暫停服務,所以Master最好不要寫記憶體快照。

2).Master AOF持久化,如果不重寫AOF檔案,這個持久化方式對效能的影響是最小的,但是AOF檔案會不斷增大,AOF檔案過大會影響Master重啟的恢復速度。Master最好不要做任何持久化工作,包括記憶體快照和AOF日誌檔案,特別是不要啟用記憶體快照做持久化,如果資料比較關鍵,某個Slave開啟AOF備份資料,策略為每秒同步一次。

3).Master呼叫BGREWRITEAOF重寫AOF檔案,AOF在重寫的時候會佔大量的CPU和記憶體資源,導致服務load過高,出現短暫服務暫停現象。

4). Redis主從複製的效能問題,為了主從複製的速度和連線的穩定性,Slave和Master最好在同一個區域網內

=========================此為別人的遇到的面試題(反應出面試時的一種常態)=================

關於 Redis 佇列的一道面試題

面試的時候講到自己做的一個專案,我說用了 Redis 佇列, lpush 進, rpop 出,面試官打斷問:一般入佇列沒有問題,如果出現對列阻塞了怎麼辦?實際生產環境中在這裡沒有出現問題,而且佇列量很大,我沒有太理解面試官問的什麼。(消費者掛掉 or 太慢。怎麼處理

Redis 作佇列需要注意什麼問題?如果是高併發情況下怎麼辦?謝謝大家指點!

還問有沒有考慮佇列的長度呢,我在實際開發中,沒有考慮過這個問題, redis 可以一直入佇列吧,不過如果佇列太大,有可能佔滿記憶體,那這種情況怎麼辦?

1 、佇列阻塞問題做好報警 
2 、從業務方面考慮能夠動態調控提高佇列處理能力


    8   2016-03-08 16:17:17 +08:00   ♥ 2最近就遇到這個問題了。 

消費者的速度沒有生產者快,增加消費者也緩解不了, Redis 會被打滿,直到掛掉(影響其它業務功能)。而且 Redis 目前的資料清理策略是根據 key 的,如果設定了清理用作佇列的那個 list key 會被清掉,如果這是業務程式碼寫的不是太好的話消費者和生產者都會掛掉。消費者最好還是非同步消費,不要阻塞佇列。 

當然還是要對佇列長度做監控。 

目前已經轉投 Kafka 了。
 額,可能說的不是太專業。看具體業務吧,你拿到資料肯定是需要做一些處理,這個過程可以非同步操作,比如非同步寫檔案,非同步入庫等。甚至繼續向下遊業務拋,壓力往後轉移。    

額,可能說的不是太專業。看具體業務吧,你拿到資料肯定是需要做一些處理,這個過程可以非同步操作,比如非同步寫檔案,非同步入庫等。甚至繼續向下遊業務拋,壓力往後轉移。
    8   2016-03-08 16:17:17 +08:00   ♥ 2最近就遇到這個問題了。 

消費者的速度沒有生產者快,增加消費者也緩解不了, Redis 會被打滿,直到掛掉(影響其它業務功能)。而且 Redis 目前的資料清理策略是根據 key 的,如果設定了清理用作佇列的那個 list key 會被清掉,如果這是業務程式碼寫的不是太好的話消費者和生產者都會掛掉。消費者最好還是非同步消費,不要阻塞佇列。 

當然還是要對佇列長度做監控。 

目前已經轉投 Kafka 了。

出佇列阻塞就是消費者的處理速度跟不上生產者產生的任務,加消費者提高處理速度。 
消費者加到極限還處理不過來,實在有爆記憶體的風險的時候,可以考慮將任務資料持久化,避免資料丟失,先把任務狀態儲存下來,根據具體業務做優化調整,等待異常問題解決之後再繼續處理。
關於你的問題我遇見過: 
1. Redis 關於記憶體滿了如何處理的策略要配置好,是使用 lru 還是 noviction 等,一定要想清楚, Redis 可使用的記憶體大小要配置適當,最好是你 Redis 記憶體使用峰值的幾倍甚至 10 倍。 

2. Redis 記憶體要做好報警,簡單的做法是每分鐘或更短的時間使用 info 指令獲取記憶體使用情況,如果超過某個值要馬上想辦法處理。 

3. 如果能多加 consumer ,就嘗試多加 consumer 解決。 

4. 如果還沒有解決,你可能需要想辦法提升 Redis 記憶體佔用,可能想辦法平行擴充套件 Redis Server 。 

5. 如果還沒有解決,就只能想辦法用 kafka 這種可以持久化的方式了,或者你自己設計一種 Redis 的持久化方式,因為記憶體總歸是有限的且稀缺的。
按我的理解, 是說 " consumer 吞吐量太弱, 不及 producer 生產速度, 此時何解? " 

這就難玩兒了. . 就好比要解決北上廣住房問題 . 


不考慮移民 (投靠其他技術選型), 

1. 監控佇列, 動態調配 consumer, 適時增加 consumer 

最簡單有效的辦法, 從根本上解決問題. 當然你得花錢加機器 

2. 做好降級方案, 達到預設閥值, 出隊後暫不處理, 直接先持久化再說 


3. consumer 投遞非同步任務, 不要阻塞出佇列 

這個比較難玩兒

============================一些基礎相關的東東================================

redis的併發競爭問題如何解決?

Redis為單程序單執行緒模式,採用佇列模式將併發訪問變為序列訪問。Redis本身沒有鎖的概念,Redis對於多個客戶端連線並不存在競爭,但是在Jedis客戶端對Redis進行併發訪問時會發生連線超時、資料轉換錯誤、阻塞、客戶端關閉連線等問題,這些問題均是由於客戶端連線混亂造成。對此有2種解決方法:

1.客戶端角度,為保證每個客戶端間正常有序與Redis進行通訊,對連線進行池化,同時對客戶端讀寫Redis操作採用內部鎖synchronized

2.伺服器角度,利用setnx實現鎖。

對於第一種,需要應用程式自己處理資源的同步,可以使用的方法比較通俗,可以使用synchronized也可以使用lock;第二種需要用到Redis的setnx命令,但是需要注意一些問題。

redis事物的瞭解CAS(check-and-set 操作實現樂觀鎖 )?

和眾多其它資料庫一樣,Redis作為NoSQL資料庫也同樣提供了事務機制。在Redis中,MULTI/EXEC/DISCARD/WATCH這四個命令是我們實現事務的基石。相信對有關係型資料庫開發經驗的開發者而言這一概念並不陌生,即便如此,我們還是會簡要的列出Redis中事務的實現特徵:

1). 在事務中的所有命令都將會被序列化的順序執行,事務執行期間,Redis不會再為其它客戶端的請求提供任何服務,從而保證了事物中的所有命令被原子的執行。

2). 和關係型資料庫中的事務相比,在Redis事務中如果有某一條命令執行失敗,其後的命令仍然會被繼續執行。

3). 我們可以通過MULTI命令開啟一個事務,有關係型資料庫開發經驗的人可以將其理解為"BEGIN TRANSACTION"語句。在該語句之後執行的命令都將被視為事務之內的操作,最後我們可以通過執行EXEC/DISCARD命令來提交/回滾該事務內的所有操作。這兩個Redis命令可被視為等同於關係型資料庫中的COMMIT/ROLLBACK語句。

4). 在事務開啟之前,如果客戶端與伺服器之間出現通訊故障並導致網路斷開,其後所有待執行的語句都將不會被伺服器執行。然而如果網路中斷事件是發生在客戶端執行EXEC命令之後,那麼該事務中的所有命令都會被伺服器執行。

5). 當使用Append-Only模式時,Redis會通過呼叫系統函式write將該事務內的所有寫操作在本次呼叫中全部寫入磁碟。然而如果在寫入的過程中出現系統崩潰,如電源故障導致的宕機,那麼此時也許只有部分資料被寫入到磁碟,而另外一部分資料卻已經丟失。Redis伺服器會在重新啟動時執行一系列必要的一致性檢測,一旦發現類似問題,就會立即退出並給出相應的錯誤提示。此時,我們就要充分利用Redis工具包中提供的redis-check-aof工具,該工具可以幫助我們定位到資料不一致的錯誤,並將已經寫入的部分資料進行回滾。修復之後我們就可以再次重新啟動Redis伺服器了。

.WATCH命令和基於CAS的樂觀鎖:

在Redis的事務中,WATCH命令可用於提供CAS(check-and-set)功能。假設我們通過WATCH命令在事務執行之前監控了多個Keys,倘若在WATCH之後有任何Key的值發生了變化,EXEC命令執行的事務都將被放棄,同時返回Null multi-bulk應答以通知呼叫者事務執行失敗。例如,我們再次假設Redis中並未提供incr命令來完成鍵值的原子性遞增,如果要實現該功能,我們只能自行編寫相應的程式碼。其偽碼如下:

val = GET mykey

val = val + 1

SET mykey $val

  以上程式碼只有在單連線的情況下才可以保證執行結果是正確的,因為如果在同一時刻有多個客戶端在同時執行該段程式碼,那麼就會出現多執行緒程式中經常出現的一種錯誤場景--競態爭用(race condition)。比如,客戶端A和B都在同一時刻讀取了mykey的原有值,假設該值為10,此後兩個客戶端又均將該值加一後set回Redis伺服器,這樣就會導致mykey的結果為11,而不是我們認為的12。為了解決類似的問題,我們需要藉助WATCH命令的幫助,見如下程式碼:

WATCH mykey

val = GET mykey

val = val + 1

MULTI

SET mykey $val

EXEC

  和此前程式碼不同的是,新程式碼在獲取mykey的值之前先通過WATCH命令監控了該鍵,此後又將set命令包圍在事務中,這樣就可以有效的保證每個連線在執行EXEC之前,如果當前連接獲取的mykey的值被其它連線的客戶端修改,那麼當前連線的EXEC命令將執行失敗。這樣呼叫者在判斷返回值後就可以獲悉val是否被重新設定成功。

redis持久化的幾種方式

1、快照(snapshots

預設情況情況下,Redis把資料快照存放在磁碟上的二進位制檔案中,檔名為dump.rdb。你可以配置Redis的持久化策略,例如資料集中每N秒鐘有超過M次更新,就將資料寫入磁碟;或者你可以手工呼叫命令SAVEBGSAVE

工作原理

  . Redis forks.

  .子程序開始將資料寫到臨時RDB檔案中。

  .當子程序完成寫RDB檔案,用新檔案替換老檔案。

  .這種方式可以使Redis使用copy-on-write技術。

2、AOF

快照模式並不十分健壯,當系統停止,或者無意中Rediskill掉,最後寫入Redis的資料就會丟失。這對某些應用也許不是大問題,但對於要求高可靠性的應用來說,

Redis就不是一個合適的選擇。

Append-only檔案模式是另一種選擇。

你可以在配置檔案中開啟AOF模式

3、虛擬記憶體方式

4、diskstore方式

redis的快取失效策略和主鍵失效機制

作為快取系統都要定期清理無效資料,就需要一個主鍵失效和淘汰策略.

  在Redis當中,有生存期的key被稱為volatile。在建立快取時,要為給定的key設定生存期,當key過期的時候(生存期為0),它可能會被刪除。

1、影響生存時間的一些操作

  生存時間可以通過使用 DEL 命令來刪除整個 key 來移除,或者被 SET 和 GETSET 命令覆蓋原來的資料,也就是說,修改key對應的value和使用另外相同的key和value來覆蓋以後,當前資料的生存時間不同。

  比如說,對一個 key 執行INCR命令,對一個列表進行LPUSH命令,或者對一個雜湊表執行HSET命令,這類操作都不會修改 key 本身的生存時間。另一方面,如果使用RENAME對一個 key 進行改名,那麼改名後的 key 的生存時間和改名前一樣。

RENAME命令的另一種可能是,嘗試將一個帶生存時間的 key 改名成另一個帶生存時間的 another_key ,這時舊的 another_key (以及它的生存時間)會被刪除,然後舊的 key 會改名為 another_key ,因此,新的 another_key 的生存時間也和原本的 key 一樣。使用PERSIST命令可以在不刪除 key 的情況下,移除 key 的生存時間,讓 key 重新成為一個persistent key 。

===================