讀完這46道Redis面試題之後,你就會覺得自己的Redis白學了

前言

今天跟大家分享下Redis 常見面試題的知識,總共46道,希望大家能夠喜歡。

 

讀完這46道Redis面試題之後,你就會覺得自己的Redis白學了

 

1 什麼是 Redis?簡述它的優缺點?

Redis 的全稱是:Remote Dictionary.Server,本質上是一個 Key-Value 類型的內存數據庫,很像memcached,整個數據庫統統加載在內存當中進行操作,定期通過異步操作把數據庫數據 flush 到硬盤上進行保存。

因爲是純內存操作,Redis 的性能非常出色,每秒可以處理超過 10 萬次讀寫操作,是已知性能最快的Key-Value DB。

Redis 的出色之處不僅僅是性能,Redis 最大的魅力是支持保存多種數據結構,此外單個 value 的最大限制是 1GB,不像 memcached 只能保存 1MB 的數據,因此 Redis 可以用來實現很多有用的功能。

比方說用他的 List 來做 FIFO 雙向鏈表,實現一個輕量級的高性 能消息隊列服務,用他的 Set 可以做高性能的 tag 系統等等。

另外 Redis 也可以對存入的 Key-Value 設置 expire 時間,因此也可以被當作一 個功能加強版的memcached 來用。Redis 的主要缺點是數據庫容量受到物理內存的限制,不能用作海量數據的高性能讀寫,因此 Redis 適合的場景主要侷限在較小數據量的高性能操作和運算上。

2 Redis 與 memcached 相比有哪些優勢?

1.memcached 所有的值均是簡單的字符串,redis 作爲其替代者,支持更爲豐富的數據類型

2.redis 的速度比 memcached 快很多 redis 的速度比 memcached 快很多

3.redis 可以持久化其數據 redis 可以持久化其數據

3 Redis 支持哪幾種數據類型?

String、List、Set、Sorted Set、hashes

4 Redis 主要消耗什麼物理資源?

內存。

5 Redis 有哪幾種數據淘汰策略?

1.noeviction:返回錯誤當內存限制達到,並且客戶端嘗試執行會讓更多內存被使用的命令。

2.allkeys-lru: 嘗試回收最少使用的鍵(LRU),使得新添加的數據有空間存放。

3.volatile-lru: 嘗試回收最少使用的鍵(LRU),但僅限於在過期集合的鍵,使得新添加的數據有空間存放。

4.allkeys-random: 回收隨機的鍵使得新添加的數據有空間存放。

5.volatile-random: 回收隨機的鍵使得新添加的數據有空間存放,但僅限於在過期集合的鍵。

6.volatile-ttl: 回收在過期集合的鍵,並且優先回收存活時間(TTL)較短的鍵,使得新添加的數據有空間存放。

6 Redis 官方爲什麼不提供 Windows 版本?

因爲目前 Linux 版本已經相當穩定,而且用戶量很大,無需開發 windows 版本,反而會帶來兼容性等問題。

7 一個字符串類型的值能存儲最大容量是多少?

512M

8 爲什麼 Redis 需要把所有數據放到內存中?

Redis 爲了達到最快的讀寫速度將數據都讀到內存中,並通過異步的方式將數據寫入磁盤。

所以 redis 具有快速和數據持久化的特徵,如果不將數據放在內存中,磁盤 I/O 速度爲嚴重影響 redis 的性能。

在內存越來越便宜的今天,redis 將會越來越受歡迎, 如果設置了最大使用的內存,則數據已有記錄數達到內存限值後不能繼續插入新值。

9 Redis 集羣方案應該怎麼做?都有哪些方案?

1.codis

2.目前用的最多的集羣方案,基本和 twemproxy 一致的效果,但它支持在節點數量改變情況下,舊節點數據可恢復到新 hash 節點。

redis cluster3.0 自帶的集羣,特點在於他的分佈式算法不是一致性 hash,而是 hash 槽的概念,以及自身支持節點設置從節點。具體看官方文檔介紹。

3.在業務代碼層實現,起幾個毫無關聯的 redis 實例,在代碼層,對 key 進行 hash 計算,然後去對應的redis 實例操作數據。這種方式對 hash 層代碼要求比較高,考慮部分包括,節點失效後的替代算法方案,數據震盪後的自動腳本恢復,實例的監控,等等。

Java 架構學習資料(裏面有高可用、高併發、高性能及分佈式、Jvm 性能調優、Spring 源碼,MyBatis,Netty,Redis,Kafka,Mysql,Zookeeper,Tomcat,Docker,Dubbo,Nginx 等多個知識點的架構資料)合理利用自己每一分每一秒的時間來學習提升自己,不要再用"沒有時間“來掩飾自己思想上的懶惰!趁年輕,使勁拼,給未來的自己一個交代!

10 Redis 集羣方案什麼情況下會導致整個集羣不可用?

有 A,B,C 三個節點的集羣,在沒有複製模型的情況下,如果節點 B 失敗了,那麼整個集羣就會以爲缺少5501-11000 這個範圍的槽而不可用。

讀完這46道Redis面試題之後,你就會覺得自己的Redis白學了

 

 

11 MySQL 裏有 2000w 數據,redis 中只存 20w 的數據,如何保證 redis 中的數據都是熱點數據?

redis 內存數據集大小上升到一定大小的時候,就會施行數據淘汰策略。

其實面試除了考察 Redis,不少公司都很重視高併發高可用的技術,特別是一線互聯網公司,分佈式、

JVM、spring 源碼分析、微服務等知識點已是面試的必考題。文末分享給大家一線互聯網公司最新的技術知識(彩蛋)

12 Redis 有哪些適合的場景?

(1)會話緩存(Session Cache)

最常用的一種使用 Redis 的情景是會話緩存(sessioncache),用 Redis 緩存會話比其他存儲(如Memcached)的優勢在於:Redis 提供持久化。當維護一個不是嚴格要求一致性的緩存時,如果用戶的購物車信息全部丟失,大部分人都會不高興的,現在,他們還會這樣嗎?

幸運的是,隨着 Redis 這些年的改進,很容易找到怎麼恰當的使用 Redis 來緩存會話的文檔。甚至廣爲人知的商業平臺 Magento 也提供 Redis 的插件。

(2)全頁緩存(FPC)

除基本的會話 token 之外,Redis 還提供很簡便的 FPC 平臺。回到一致性問題,即使重啓了 Redis 實例,因爲有磁盤的持久化,用戶也不會看到頁面加載速度的下降,這是一個極大改進,類似 PHP 本地FPC。

再次以 Magento 爲例,Magento 提供一個插件來使用 Redis 作爲全頁緩存後端。

此外,對 WordPress 的用戶來說,Pantheon 有一個非常好的插件 wp-redis,這個插件能幫助你以最快速度加載你曾瀏覽過的頁面。

(3)隊列

Reids 在內存存儲引擎領域的一大優點是提供 list 和 set 操作,這使得 Redis 能作爲一個很好的消息隊列平臺來使用。Redis 作爲隊列使用的操作,就類似於本地程序語言(如 Python)對 list 的 push/pop操作。

如果你快速的在 Google 中搜索“Redis queues”,你馬上就能找到大量的開源項目,這些項目的目的就是利用 Redis 創建非常好的後端工具,以滿足各種隊列需求。例如,Celery 有一個後臺就是使用Redis 作爲 broker,你可以從這裏去查看。

(4)排行榜/計數器

Redis 在內存中對數字進行遞增或遞減的操作實現的非常好。集合(Set)和有序集合(SortedSet)也使得我們在執行這些操作的時候變的非常簡單,Redis 只是正好提供了這兩種數據結構。

所以,我們要從排序集合中獲取到排名最靠前的 10 個用戶–我們稱之爲“user_scores”,我們只需要像下面一樣執行即可:

當然,這是假定你是根據你用戶的分數做遞增的排序。如果你想返回用戶及用戶的分數,你需要這樣執行:

ZRANGE user_scores 0 10 WITHSCORESAgora Games 就是一個很好的例子,用 Ruby 實現的,它的排行榜就是使用 Redis 來存儲數據的,你可以在這裏看到。

(5)發佈/訂閱

最後(但肯定不是最不重要的)是 Redis 的發佈/訂閱功能。發佈/訂閱的使用場景確實非常多。我已看見人們在社交網絡連接中使用,還可作爲基於發佈/訂閱的腳本觸發器,甚至用 Redis 的發佈/訂閱功能來建立聊天系統!

13 Redis 支持的 Java 客戶端都有哪些?官方推薦用哪個?

Redisson、Jedis、lettuce 等等,官方推薦使用 Redisson。

14 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)。

15 Jedis 與 Redisson 對比有什麼優缺點?

Jedis 是 Redis 的 Java 實現的客戶端,其 API 提供了比較全面的 Redis 命令的支持;

Redisson 實現了分佈式和可擴展的 Java 數據結構,和 Jedis 相比,功能較爲簡單,不支持字符串操作,不支持排序、事務、管道、分區等 Redis 特性。Redisson 的宗旨是促進使用者對 Redis 的關注分離,從而讓使用者能夠將精力更集中地放在處理業務邏輯上。

16 說說 Redis 哈希槽的概念?

Redis 集羣沒有使用一致性 hash,而是引入了哈希槽的概念,Redis 集羣有 16384 個哈希槽,每個 key 通過 CRC16 校驗後對 16384 取模來決定放置哪個槽,集羣的每個節點負責一部分 hash 槽。

17 Redis 集羣的主從複製模型是怎樣的?

爲了使在部分節點失敗或者大部分節點無法通信的情況下集羣仍然可用,所以集羣使用了主從複製模型,每個節點都會有 N-1 個複製品.

18 Redis 集羣會有寫操作丟失嗎?爲什麼?

Redis 並不能保證數據的強一致性,這意味這在實際中集羣在特定的條件下可能會丟失寫操作。

19 Redis 集羣之間是如何複製的?

異步複製

20 Redis 集羣最大節點個數是多少?

16384 個

21 Redis 集羣如何選擇數據庫?

Redis 集羣目前無法做數據庫選擇,默認在 0 數據庫。

22 Redis 中的管道有什麼用?

一次請求/響應服務器能實現處理新的請求即使舊的請求還未被響應,這樣就可以將多個命令發送到服務器,而不用等待回覆,最後在一個步驟中讀取該答覆。

這就是管道(pipelining),是一種幾十年來廣泛使用的技術。例如許多 POP3 協議已經實現支持這個功能,大大加快了從服務器下載新郵件的過程。

23 怎麼理解 Redis 事務?

事務是一個單獨的隔離操作:事務中的所有命令都會序列化、按順序地執行,事務在執行的過程中,不會被其他客戶端發送來的命令請求所打斷。事務是一個原子操作:事務中的命令要麼全部被執行,要麼全部都不執行。

24 Redis 事務相關的命令有哪幾個?

MULTI、EXEC、DISCARD、WATCH

25 Redis key 的過期時間和永久有效分別怎麼設置?

EXPIRE 和 PERSIST 命令

26 Redis 如何做內存優化?

儘可能使用散列表(hashes),散列表(是說散列表裏面存儲的數少)使用的內存非常小,所以你應該儘可能的將你的數據模型抽象到一個散列表裏面。

比如你的 web 系統中有一個用戶對象,不要爲這個用戶的名稱,姓氏,郵箱,密碼設置單獨的 key,而是應該把這個用戶的所有信息存儲到一張散列表裏面。

27 Redis 回收進程如何工作的?

一個客戶端運行了新的命令,添加了新的數據。Redi 檢查內存使用情況,如果大於 maxmemory 的限制, 則根據設定好的策略進行回收。一個新的命令被執行,等等。

所以我們不斷地穿越內存限制的邊界,通過不斷達到邊界然後不斷地回收回到邊界以下。

如果一個命令的結果導致大量內存被使用(例如很大的集合的交集保存到一個新的鍵),不用多久內存限制就會被這個內存使用量超越。

28 加鎖機制

咱們來看上面那張圖,現在某個客戶端要加鎖。如果該客戶端面對的是一個 redis cluster 集羣,他首先會根據 hash 節點選擇一臺機器。這裏注意,僅僅只是選擇一臺機器!這點很關鍵!緊接着,就會發送一段 lua 腳本到 redis 上,那段 lua 腳本如下所示:

爲啥要用 lua 腳本呢?因爲一大坨複雜的業務邏輯,可以通過封裝在 lua 腳本中發送給 redis,保證這段複雜業務邏輯執行的原子性。

那麼,這段 lua 腳本是什麼意思呢?這裏 KEYS[1]代表的是你加鎖的那個 key,比如說:RLock lock = redisson.getLock("myLock");這裏你自己設置了加鎖的那個鎖 key 就是“myLock”。

ARGV[1]代表的就是鎖 key 的默認生存時間,默認 30 秒。ARGV[2]代表的是加鎖的客戶端的 ID,類似於下面這樣:8743c9c0-0795-4907-87fd-6c719a6b4586:1給大家解釋一下,第一段 if 判斷語句,就是用“exists myLock”命令判斷一下,如果你要加鎖的那個鎖 key 不存在的話,你就進行加鎖。如何加鎖呢?很簡單,用下面的命令:hset myLock8743c9c0-0795-4907-87fd-6c719a6b4586:1 1,通過這個命令設置一個 hash 數據結構,這行命令執行後,會出現一個類似下面的數據結構:

上述就代表“8743c9c0-0795-4907-87fd-6c719a6b4586:1”這個客戶端對“myLock”這個鎖 key 完成了加鎖。接着會執行“pexpire myLock 30000”命令,設置 myLock 這個鎖 key 的生存時間是 30 秒。好了,到此爲止,ok,加鎖完成了。

29 鎖互斥機制

那麼在這個時候,如果客戶端 2 來嘗試加鎖,執行了同樣的一段 lua 腳本,會咋樣呢?很簡單,第一個 if 判斷會執行“exists myLock”,發現 myLock 這個鎖 key 已經存在了。接着第二個 if 判斷,判斷一下,myLock 鎖 key 的 hash 數據結構中,是否包含客戶端 2 的 ID,但是明顯不是的,因爲那裏包含的是客戶端 1 的 ID。

所以,客戶端 2 會獲取到 pttl myLock 返回的一個數字,這個數字代表了 myLock 這個鎖 key的剩餘生存時間。比如還剩 15000 毫秒的生存時間。此時客戶端 2 會進入一個 while 循環,不停的嘗試加鎖。

30 watch dog 自動延期機制

客戶端 1 加鎖的鎖 key 默認生存時間才 30 秒,如果超過了 30 秒,客戶端 1 還想一直持有這把鎖,怎麼辦呢?

簡單!只要客戶端 1 一旦加鎖成功,就會啓動一個 watch dog 看門狗,他是一個後臺線程,會每隔 10 秒檢查一下,如果客戶端 1 還持有鎖 key,那麼就會不斷的延長鎖 key 的生存時間。

31 可重入加鎖機制

那如果客戶端 1 都已經持有了這把鎖了,結果可重入的加鎖會怎麼樣呢?比如下面這種代碼:

這時我們來分析一下上面那段 lua 腳本。第一個 if 判斷肯定不成立,“exists myLock”會顯示鎖key 已經存在了。第二個 if 判斷會成立,因爲 myLock 的 hash 數據結構中包含的那個 ID,就是客戶端 1 的那個 ID,也就是“8743c9c0-0795-4907-87fd-6c719a6b4586:1”

此時就會執行可重入加鎖的邏輯,他會用:

incrby myLock 8743c9c0-0795-4907-87fd-6c71a6b4586:1 1 ,通過這個命令,對客戶端 1的加鎖次數,累加 1。此時 myLock 數據結構變爲下面這樣:

大家看到了吧,那個 myLock 的 hash 數據結構中的那個客戶端 ID,就對應着加鎖的次數

32 釋放鎖機制

如果執行 lock.unlock(),就可以釋放分佈式鎖,此時的業務邏輯也是非常簡單的。其實說白了,就是每次都對 myLock 數據結構中的那個加鎖次數減 1。如果發現加鎖次數是 0 了,說明這個客戶端已經不再持有鎖了,此時就會用:“del myLock”命令,從 redis 裏刪除這個 key。

然後呢,另外的客戶端 2 就可以嘗試完成加鎖了。這就是所謂的分佈式鎖的開源 Redisson 框架的實現機制。

一般我們在生產系統中,可以用 Redisson 框架提供的這個類庫來基於 redis 進行分佈式鎖的加鎖與釋放鎖。

33 上述 Redis 分佈式鎖的缺點

其實上面那種方案最大的問題,就是如果你對某個 redis master 實例,寫入了 myLock 這種鎖key 的 value,此時會異步複製給對應的 master slave 實例。但是這個過程中一旦發生 redis master 宕機,主備切換,redis slave 變爲了 redis master。

接着就會導致,客戶端 2 來嘗試加鎖的時候,在新的 redis master 上完成了加鎖,而客戶端 1也以爲自己成功加了鎖。此時就會導致多個客戶端對一個分佈式鎖完成了加鎖。這時系統在業務語義上一定會出現問題,導致各種髒數據的產生。

所以這個就是 redis cluster,或者是 redis master-slave 架構的主從異步複製導致的 redis 分佈式鎖的最大缺陷:在 redis master 實例宕機的時候,可能導致多個客戶端同時完成加鎖。

34 使用過 Redis 分佈式鎖麼,它是怎麼實現的?

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

如果在 setnx 之後執行 expire 之前進程意外 crash 或者要重啓維護了,那會怎麼樣?

set 指令有非常複雜的參數,這個應該是可以同時把 setnx 和 expire 合成一條指令來用的!

35 使用過 Redis 做異步隊列麼,你是怎麼用的?有什麼缺點?

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

缺點:

在消費者下線的情況下,生產的消息會丟失,得使用專業的消息隊列如 rabbitmq 等。

能不能生產一次消費多次呢?

使用 pub/sub 主題訂閱者模式,可以實現 1:N 的消息隊列。

36 什麼是緩存穿透?如何避免?什麼是緩存雪崩?何如避免?

緩存穿透

一般的緩存系統,都是按照 key 去緩存查詢,如果不存在對應的 value,就應該去後端系統查找(比如DB)。一些惡意的請求會故意查詢不存在的 key,請求量很大,就會對後端系統造成很大的壓力。這就叫做緩存穿透。

如何避免?

1:對查詢結果爲空的情況也進行緩存,緩存時間設置短一點,或者該 key 對應的數據 insert 了之後清理緩存

2:對一定不存在的 key 進行過濾。可以把所有的可能存在的 key 放到一個大的 Bitmap 中,查詢時通過該 bitmap 過濾。

緩存雪崩

當緩存服務器重啓或者大量緩存集中在某一個時間段失效,這樣在失效的時候,會給後端系統帶來很大壓力。導致系統崩潰。

如何避免?

1:在緩存失效後,通過加鎖或者隊列來控制讀數據庫寫緩存的線程數量。比如對某個 key 只允許一個線程查詢數據和寫緩存,其他線程等待。

2:做二級緩存,A1 爲原始緩存,A2 爲拷貝緩存,A1 失效時,可以訪問 A2,A1 緩存失效時間設置爲短期,A2 設置爲長期

3:不同的 key,設置不同的過期時間,讓緩存失效的時間點儘量均勻

37 redis 和 memcached 什麼區別?爲什麼高併發下有時單線程的 redis 比多線程的memcached 效率要高?

區別:

1.mc 可緩存圖片和視頻。rd 支持除 k/v 更多的數據結構;

2.rd 可以使用虛擬內存,rd 可持久化和 aof 災難恢復,rd 通過主從支持數據備份;

3.rd 可以做消息隊列。

原因:

mc 多線程模型引入了緩存一致性和鎖,加鎖帶來了性能損耗。

redis 主從複製如何實現的?redis 的集羣模式如何實現?redis 的 key 是如何尋址的?

主從複製實現:主節點將自己內存中的數據做一份快照,將快照發給從節點,從節點將數據恢復到內存中。之後再每次增加新數據的時候,主節點以類似於 mysql 的二進制日誌方式將語句發送給從節點,從節點拿到主節點發送過來的語句進行重放。

分片方式:

-客戶端分片

-基於代理的分片

● Twemproxy

● codis-路由查詢分片

● Redis-cluster(本身提供了自動將數據分散到 Redis Cluster 不同節點的能力,整個數據集合的某個數據子集存儲在哪個節點對於用戶來說是透明的)

redis-cluster 分片原理:Cluster 中有一個 16384 長度的槽(虛擬槽),編號分別爲 0-16383。

每個 Master 節點都會負責一部分的槽,當有某個 key 被映射到某個 Master 負責的槽,那麼這個 Master 負責爲這個 key 提供服務,至於哪個 Master 節點負責哪個槽,可以由用戶指定,也可以在初始化的時候自動生成,只有 Master 才擁有槽的所有權。Master 節點維護着一個 16384/8 字節的位序列,Master 節點用 bit 來標識對於某個槽自己是否擁有。比如對於編號爲 1 的槽,Master 只要判斷序列的第二位(索引從 0 開始)是不是爲 1 即可。

這種結構很容易添加或者刪除節點。比如如果我想新添加個節點 D, 我需要從節點 A、B、C 中得部分槽到 D 上。

38 使用 redis 如何設計分佈式鎖?說一下實現思路?使用 zk 可以嗎?如何實現?這兩種有什麼區別?

redis:

1.線程 A setnx(上鎖的對象,超時時的時間戳 t1),如果返回 true,獲得鎖。

2.線程 B 用 get 獲取 t1,與當前時間戳比較,判斷是是否超時,沒超時 false,若超時執行第 3 步;

3.計算新的超時時間 t2,使用 getset 命令返回 t3(該值可能其他線程已經修改過),如果t1==t3,獲得鎖,如果 t1!=t3 說明鎖被其他線程獲取了。

4.獲取鎖後,處理完業務邏輯,再去判斷鎖是否超時,如果沒超時刪除鎖,如果已超時,不用處理(防止刪除其他線程的鎖)。

zk:

1.客戶端對某個方法加鎖時,在 zk 上的與該方法對應的指定節點的目錄下,生成一個唯一的瞬時有序節點 node1;

2.客戶端獲取該路徑下所有已經創建的子節點,如果發現自己創建的 node1 的序號是最小的,就認爲這個客戶端獲得了鎖。

3.如果發現 node1 不是最小的,則監聽比自己創建節點序號小的最大的節點,進入等待。

4.獲取鎖後,處理完邏輯,刪除自己創建的 node1 即可。

區別:zk 性能差一些,開銷大,實現簡單。

39 知道 redis 的持久化嗎?底層如何實現的?有什麼優點缺點?

RDB(Redis DataBase:在不同的時間點將 redis 的數據生成的快照同步到磁盤等介質上):內存到硬盤的快照,定期更新。缺點:耗時,耗性能(fork+io 操作),易丟失數據。

AOF(Append Only File:將 redis 所執行過的所有指令都記錄下來,在下次 redis 重啓時,只需要執行指令就可以了):

寫日誌。缺點:體積大,恢復速度慢。bgsave 做鏡像全量持久化,aof 做增量持久化。因爲 bgsave 會消耗比較長的時間,不夠實時,在停機的時候會導致大量的數據丟失,需要 aof 來配合,在 redis 實例重啓時,優先使用 aof 來恢復內存的狀態,如果沒有 aof 日誌,就會使用 rdb 文件來恢復。Redis 會定期做aof 重寫,壓縮 aof 文件日誌大小。Redis4.0 之後有了混合持久化的功能,將 bgsave 的全量和 aof 的增量做了融合處理,這樣既保證了恢復的效率又兼顧了數據的安全性。bgsave 的原理,fork 和 cow, fork 是指 redis 通過創建子進程來進行 bgsave 操作,cow 指的是 copy onwrite,子進程創建後,父子進程共享數據段,父進程繼續提供讀寫服務,寫髒的頁面數據會逐漸和子進程分離開來。

redis 過期策略都有哪些?LRU 算法知道嗎?寫一下 java 代碼實現?

過期策略:

定時過期(一 key 一定時器),惰性過期:只有使用 key 時才判斷 key 是否已過期,過期則清除。定期過期:前兩者折中。

LRU:new LinkedHashMap<K, V>(capacity, DEFAULT_LOAD_FACTORY, true);

//第三個參數置爲 true,代表 linkedlist 按訪問順序排序,可作爲 LRU 緩存;設爲 false 代表按插入順序排序,可作爲 FIFO 緩存

LRU 算法實現:

1.通過雙向鏈表來實現,新數據插入到鏈表頭部;

2.每當緩存命中(即緩存數據被訪問),則將數據移到鏈表頭部;

3.當鏈表滿的時候,將鏈表尾部的數據丟棄。

LinkedHashMap:HashMap 和雙向鏈表合二爲一即是 LinkedHashMap。HashMap 是無序的,LinkedHashMap 通過維護一個額外的雙向鏈表保證了迭代順序。該迭代順序可以是插入順序(默認),也可以是訪問順序。

40 緩存穿透、緩存擊穿、緩存雪崩解決方案?

緩存穿透:

指查詢一個一定不存在的數據,如果從存儲層查不到數據則不寫入緩存,這將導致這個不存在的數據每次請求都要到 DB 去查詢,可能導致 DB 掛掉。

解決方案:

1.查詢返回的數據爲空,仍把這個空結果進行緩存,但過期時間會比較短;

2.布隆過濾器:將所有可能存在的數據哈希到一個足夠大的 bitmap 中,一個一定不存在的數據會被這個 bitmap 攔截掉,從而避免了對 DB 的查詢。

緩存擊穿:

對於設置了過期時間的 key,緩存在某個時間點過期的時候,恰好這時間點對這個 Key 有大量的併發請求過來,這些請求發現緩存過期一般都會從後端 DB 加載數據並回設到緩存,這個時候大併發的請求可能會瞬間把 DB 壓垮。

解決方案:

1.使用互斥鎖:當緩存失效時,不立即去 load db,先使用如 Redis 的 setnx 去設置一個互斥鎖,當操作成功返回時再進行 load db 的操作並回設緩存,否則重試 get 緩存的方法。

2.永遠不過期:物理不過期,但邏輯過期(後臺異步線程去刷新)。

緩存雪崩:設置緩存時採用了相同的過期時間,導致緩存在某一時刻同時失效,請求全部轉發到 DB,DB 瞬時壓力過重雪崩。與緩存擊穿的區別:雪崩是很多 key,擊穿是某一個key 緩存。

解決方案:將緩存失效時間分散開,比如可以在原有的失效時間基礎上增加一個隨機值,比如 1-5 分鐘隨機,這樣每一個緩存的過期時間的重複率就會降低,就很難引發集體失效的事件。

41 在選擇緩存時,什麼時候選擇 redis,什麼時候選擇 memcached?

選擇 redis 的情況:

1、複雜數據結構,value 的數據是哈希,列表,集合,有序集合等這種情況下,會選擇redis, 因爲 memcache 無法滿足這些數據結構,最典型的的使用場景是,用戶訂單列表,用戶消息,帖子評論等。

2、需要進行數據的持久化功能,但是注意,不要把 redis 當成數據庫使用,如果 redis掛了,內存能夠快速恢復熱數據,不會將壓力瞬間壓在數據庫上,沒有 cache 預熱的過程。對於只讀和數據一致性要求不高的場景可以採用持久化存儲

3、高可用,redis 支持集羣,可以實現主動複製,讀寫分離,而對於 memcache 如果想要實現高可用,需要進行二次開發。

4、存儲的內容比較大,memcache 存儲的 value 最大爲 1M。

選擇 memcache 的場景:

1、純 KV,數據量非常大的業務,使用 memcache 更合適,原因是:

a)memcache 的內存分配採用的是預分配內存池的管理方式,能夠省去內存分配的時間,redis 是臨時申請空間,可能導致碎片化。

b)虛擬內存使用,memcache 將所有的數據存儲在物理內存裏,redis 有自己的 vm 機制,理論上能夠存儲比物理內存更多的數據,當數據超量時,引發 swap,把冷數據刷新到磁盤上,從這點上,數據量大時,memcache 更快

c)網絡模型,memcache 使用非阻塞的 IO 複用模型,redis 也是使用非阻塞的 IO 複用模型,但是 redis 還提供了一些非 KV 存儲之外的排序,聚合功能,複雜的 CPU 計算,會阻塞整個 IO 調度,從這點上由於 redis 提供的功能較多,memcache 更快些

d) 線程模型,memcache 使用多線程,主線程監聽,worker 子線程接受請求,執行讀寫,這個過程可能存在鎖衝突。redis 使用的單線程,雖然無鎖衝突,但是難以利用多核的特性提升吞吐量。

緩存與數據庫不一致怎麼辦

假設採用的主存分離,讀寫分離的數據庫,

如果一個線程 A 先刪除緩存數據,然後將數據寫入到主庫當中,這個時候,主庫和從庫同步沒有完成,線程 B 從緩存當中讀取數據失敗,從從庫當中讀取到舊數據,然後更新至緩存,這個時候,緩存當中的就是舊的數據。

發生上述不一致的原因在於,主從庫數據不一致問題,加入了緩存之後,主從不一致的時間被拉長了

處理思路:在從庫有數據更新之後,將緩存當中的數據也同時進行更新,即當從庫發生了數據更新之後,向緩存發出刪除,淘汰這段時間寫入的舊數據。

主從數據庫不一致如何解決場景描述,對於主從庫,讀寫分離,如果主從庫更新同步有時差,就會導致主從庫數據的不一致

1、忽略這個數據不一致,在數據一致性要求不高的業務下,未必需要時時一致性

2、強制讀主庫,使用一個高可用的主庫,數據庫讀寫都在主庫,添加一個緩存,提升數據讀取的性能。

3、選擇性讀主庫,添加一個緩存,用來記錄必須讀主庫的數據,將哪個庫,哪個表,哪個主鍵,作爲緩存的 key,設置緩存失效的時間爲主從庫同步的時間,如果緩存當中有這個數據,直接讀取主庫,如果緩存當中沒有這個主鍵,就到對應的從庫中讀取。

42 Redis 常見的性能問題和解決方案

1、master 最好不要做持久化工作,如 RDB 內存快照和 AOF 日誌文件

2、如果數據比較重要,某個 slave 開啓 AOF 備份,策略設置成每秒同步一次

3、爲了主從複製的速度和連接的穩定性,master 和 Slave 最好在一個局域網內

4、儘量避免在壓力大得主庫上增加從庫

5、主從複製不要採用網狀結構,儘量是線性結構,Master<--Slave1<----Slave2 ....

讀完這46道Redis面試題之後,你就會覺得自己的Redis白學了

 

43 Redis 的數據淘汰策略有哪些

voltile-lru 從已經設置過期時間的數據集中挑選最近最少使用的數據淘汰

voltile-ttl 從已經設置過期時間的數據庫集當中挑選將要過期的數據

voltile-random 從已經設置過期時間的數據集任意選擇淘汰數據

allkeys-lru 從數據集中挑選最近最少使用的數據淘汰

allkeys-random 從數據集中任意選擇淘汰的數據

no-eviction 禁止驅逐數據

44 Redis 當中有哪些數據結構

字符串 String、字典 Hash、列表 List、集合 Set、有序集合 SortedSet。如果是高級用戶,那麼還會有,如果你是 Redis 中高級用戶,還需要加上下面幾種數據結構 HyperLogLog、Geo、Pub/Sub。

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

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

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

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

45 使用 Redis 做過異步隊列嗎,是如何實現的

使用 list 類型保存數據信息,rpush 生產消息,lpop 消費消息,當 lpop 沒有消息時,可以 sleep 一段時間,然後再檢查有沒有信息,如果不想 sleep 的話,可以使用 blpop, 在沒有信息的時候,會一直阻塞,直到信息的到來。redis 可以通過 pub/sub 主題訂閱模式實現一個生產者,多個消費者,當然也存在一定的缺點,當消費者下線時,生產的消息會丟失。

46 Redis 如何實現延時隊列

使用 sortedset,使用時間戳做 score, 消息內容作爲 key,調用 zadd 來生產消息,消費者使用 zrangbyscore 獲取 n 秒之前的數據做輪詢處理。

讀完這46道Redis面試題之後,你就會覺得自己的Redis白學了

 

小編這裏除了redis面試題的話,還有其他的面試題包括的內容有點多,具體有:Java,dubbo、jvm、springmvc、kafka、MySQL、nginxSpring cloud、SQL優化、tomacat、spring boot、多線程、悲觀鎖等,下面就可以清楚的看到,大家如果需要的話可以轉發關注小編,私信小編“學習”來得到獲取方式吧~~~

讀完這46道Redis面試題之後,你就會覺得自己的Redis白學了

感謝大家的配合和支持,小編還會持續不斷的給大家分享,希望大家能夠喜歡~~~~

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章