常被問到頭大的Redis面試題,有了這些還怕面試官?(含答案)


常被問到頭大的Redis面試題,有了這些還怕面試官?(含答案)

Redis是一個非常火熱的一個“專有詞”在互聯網和大數據中佔有一席之位。Redis是一款高性能的NOSQL系列的非關係型數據庫,也是之所以為何這麼火的原因了吧!下面是小編為大家總結的一些關於Redis的面試題(個人總結)希望大家會喜歡。

Redis的基礎

  • 什麼是Redis ?簡述它的優缺點:Redis的全稱是Remote DictionarylServer ,本質上是一個Key-Value類型的內存數據庫,很像memcached ,整個數據庫統統加載在內存當中進行操作,定期通過異步操作把數據庫數據flush到硬盤上進行保存。Redis的主要缺點是數據庫容量受到物理內存的限制,不能用作海量數據的高性能讀寫,因此Redis適合的場景主要侷限在較小數據量的高性能操作和運算上。
  • 為什麼Redis需要把所有數據放到內存中:Redis為了達到最快的讀寫速度將數據都讀到內存中,並通過異步的方式將數據寫入磁盤。所以redis具有快速和數據持久化的特徵,如果不將數據放在內存中,磁盤I/O速度為嚴重影響redis的性能。在內存越來越便宜的今天, redis將會越來越受歡迎,如果設置了最大使用的內存,則數據已有記錄數達到內存限值後不能繼續插入新值。
  • Redis和Redisson有什麼關係:Redisson是-個高級的分佈式協調Redis客服端,能幫助用戶在分佈式環境中輕鬆實現一些Java的對象(Bloom filter, BitSet, Set, SetMultimap, ScoredSortedSet, SortedSet, Map, ConcurrentMap,List, ListMultimap, Queue, BlockingQueue, Deque, BlockingDeque, Semaphore, Lock,ReadWriteLock, AtomicLong, CountDownLatch, Publish / Subscribe, HyperLogLog)。
  • Jedis與Redisson對比有什麼優缺點:Jedis是Redis的Java實現的客戶端,其API提供了比較全面的Redis命令的支持;Redisson實現了分佈式和可擴展的Java數據結構,和Jedis相比,功能較為簡單,不支持字符串操作,不支持排序、事務、管道、分區等Redis特性。Redisson 的宗旨是促進使用者對Redis的關注分離,從而讓使用者能夠將精力更集中地放在處理業務邏輯上。

Redis的緩存穿透、緩存崩潰、緩存擊穿的理解?

常被問到頭大的Redis面試題,有了這些還怕面試官?(含答案)

1.緩存穿透:是指查詢一個數據庫一定不存在的數據。

正常的使用緩存流程大致是,數據查詢先進行緩存查詢,如果key不存在或 者key已經過期,再對數據庫進行查詢,並把查詢到的對象,放進緩存。如果數據庫查詢對象為空,則不放進緩存。

發生場景:

如果傳入的參數為-1,會是怎麼樣?這個-1,就是一定不存在的對象。就會每次都去查詢數據庫,而每次查詢都是空,每次又都不會進行緩存。假如有惡意攻擊,就可以利用這個漏洞,對數據庫造成壓力,甚至壓垮數據庫。即便是採用UUID,也是很容易找到一個不存在的KEY,進行攻擊。

2.緩存擊穿: 是指一個key非常熱點,在不停的扛著大併發,大併發集中對這一個點進行訪問,當這個key在失效的瞬間,持續的大併發就穿破緩存,直接請求數據庫,就像在一個屏障上鑿開了一個洞。

發生場景:某一個商品爆款的時候會導致這種情況的產生。解決方案:

  • 設置緩存永不過期(或者過期時間比較大)。
  • 設置雙重緩存備份A和B,當A緩存失效時,使用B緩存。

3.緩存雪崩:緩存在同一時間內大量鍵過期(失效),接著來的一大波請求瞬間都落在了數據庫中導致連接異常。

解決方案:

  • 也是像解決緩存穿透一樣加鎖排隊;
  • 建立備份緩存,緩存A和緩存B,A設置超時時間,B不設置超時時間,先從A讀緩存,A沒有讀B,並且更新A緩存和B緩存;
  • 設置緩存超時時間的時候加上一個隨機的時間長度,比如這個緩存key的超時時間是固定的5分鐘加上隨機的2分鐘,醬紫可從一定程度上避免雪崩問題;

Redis 和 Memecache 有什麼區別?

常被問到頭大的Redis面試題,有了這些還怕面試官?(含答案)

  • Redis和Memcache都是將數據存放在內存中,都是內存數據庫。不過memcache還可用於緩存其他東西,例如圖片、視頻等等。
  • Redis不僅僅支持簡單的k/v類型的數據,同時還提供list,set,hash等數據結構的存儲。
  • 虛擬內存–Redis當物理內存用完時,可以將一些很久沒用到的value 交換到磁盤
  • 過期策略–memcache在set時就指定,例如set key1 0 0 8,即永不過期。Redis可以通過例如expire 設定,例如expire name 10
  • 分佈式–設定memcache集群,利用magent做一主多從;redis可以做一主多從。都可以一主一從
  • 存儲數據安全–memcache掛掉後,數據沒了;redis可以定期保存到磁盤(持久化)
  • 災難恢復–memcache掛掉後,數據不可恢復; redis數據丟失後可以通過aof恢復
  • Redis支持數據的備份,即master-slave模式的數據備份。

Redis的分佈式鎖如何實現,有什麼優缺點?

1.分佈式鎖需要解決的問題

  • 互斥性:任意時刻只能有一個客戶端擁有鎖,不能同時多個客戶端獲取
  • 安全性:鎖只能被持有該鎖的用戶刪除,而不能被其他用戶刪除
  • 死鎖:獲取鎖的客戶端因為某些原因而宕機,而未能釋放鎖,其他客戶端無法獲取此鎖,需要有機制來避免該類問題的發生
  • 容錯:當部分節點宕機,客戶端仍能獲取鎖或者釋放鎖。

2.如何通過Redis實現分佈式鎖:(非完善方法)

  • SETNX key value :如果key不存在,則創建並賦值
  • 時間複雜度: 0(1)
  • 返回值:設置成功,返回1;設置失敗,返回0。
  • 但是此時我們獲取的key是長期有效的,所以我們應該如何解決長期有效的問題呢?
  • 如何解決SETNX長期有效的問題?
  • EXPIRE key seconds
  • 設置key的生存時間,當key過期時(生存時間為0) ,會被自動刪除
  • 缺點:原子性得不到滿足

//該程序存在危險,如果執行到第二行就崩潰了,則此時key會被一直佔用而無法被釋放

RedisService redisService = SpringUtils.getBean(Redi sService.class);

long status = redisService.setnx(key, "1");

if(status == 1) {

redisService.expire(key, expire);

//執行獨佔資源邏輯

doOcuppiedWork();

}

3.如何通過Redis實現分佈式鎖:(正確方式)

  • SET key value [EX seconds] [PX milliseconds] [NX|XX]
  • EX second :設置鍵的過期時間為second秒
  • PX millisecond :設置鍵的過期時間為millisecond毫秒
  • NX :只在鍵不存在時,才對鍵進行設置操作
  • XX:只在鍵已經存在時,才對鍵進行設置操作
  • SET操作成功完成時,返回OK ,否則返回nil

RedisService redisService = SpringUtils.getBean(RedisService.class); .

String result = redisService.set(lockKey, requestId, SET_IF_NOT_EXIST, SET_WITH_EXPIRE_TIME, expireTime);

if ("OK".equals(result)) {

//執行獨佔資源邏輯

doOcuppiedWork();

}

4.大量的key同時過期的注意事項

集中過期,由於清除大量的key很耗時,會出現短暫的卡頓現象
解放方案:在設置key的過期時間的時候,給每個key加上隨機值

  • 特殊場景1:超時後使用del 導致誤刪其他線程的鎖
  • 又是一個極端場景,假如某線程成功得到了鎖,並且設置的超時時間是30秒。
  • 如果某些原因導致線程B執行的很慢很慢,過了30秒都沒執行完,這時候鎖過期自動釋放,線程B得到了鎖。
  • 隨後,線程A執行完了任務,線程A接著執行del指令來釋放鎖。但這時候線程B還沒執行完,線程A實際上刪除的是線程B加的鎖。
  • 怎麼避免這種情況呢?可以在del釋放鎖之前做一個判斷,驗證當前的鎖是不是自己加的鎖。
  • 至於具體的實現,可以在加鎖的時候把當前的線程ID當做value,並在刪除之前驗證key對應的value是不是自己線程的ID。
  • if判斷和釋放鎖是兩個獨立操作,不是原子性。

我們都是追求極致的程序員,所以這一塊要用Lua腳本來實現:

String luaScript = 'if redis.call('get', KEYS[1]) == ARGV[1] then return redis.call('del', KEYS[1]) else return 0 end';

redisClient.eval(luaScript , Collections.singletonList(key), Collections.singletonList(threadId));

這樣一來,驗證和刪除過程就是原子操作了。

特殊場景2: 出現併發的可能性

  • 還是剛才的場景1,雖然我們避免了線程A誤刪掉key的情況,但是同一時間有A,B兩個線程在訪問代碼塊,仍然是不完美的。
  • 怎麼辦呢?我們可以讓獲得鎖的線程開啟一個守護線程,用來給快要過期的鎖“續航”。
  • 當過去了29秒,線程A還沒執行完,這時候守護線程會執行expire指令,為這把鎖“續命”20秒。守護線程從第29秒開始執行,每20秒執行一次。
  • 當線程A執行完任務,會顯式關掉守護線程。
  • 另一種情況,如果節點1 忽然斷電,由於線程A和守護線程在同一個進程,守護線程也會停下。這把鎖到了超時的時候,沒人給它續命,也就自動釋放了。

由於文章限制小編把剩下的面試題整理成文檔和視頻的形勢列在下方,面試哪裡還不懂?趕緊領取資料吧!

獲取方式:轉發文章並私信小編【學習】即可獲取

常被問到頭大的Redis面試題,有了這些還怕面試官?(含答案)

常被問到頭大的Redis面試題,有了這些還怕面試官?(含答案)


分享到:


相關文章: