46道史上最全Redis面試題,面試官能問的都被我找到了(含答案)

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

Redis 的全稱是:Remote Dictionary.Server,本質上是一個 Key-Value 類型的內存數據庫,很像java

memcached,整個數據庫通通加載在內存當中進行操做,按期經過異步操做把數據庫數據 flush 到硬盤上進行保存。node

由於是純內存操做,Redis 的性能很是出色,每秒能夠處理超過 10 萬次讀寫操做,是已知性能最快的Key-Value DB。mysql

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

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

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

二、Redis 與 memcached 相比有哪些優點?

1.memcached 全部的值均是簡單的字符串,redis 做爲其替代者,支持更爲豐富的數據類型算法

2.redis 的速度比 memcached 快不少 redis 的速度比 memcached 快不少spring

3.redis 能夠持久化其數據 redis 能夠持久化其數據sql

三、Redis 支持哪幾種數據類型?

String、List、Set、Sorted Set、hashes數據庫

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

內存。

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

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

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

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

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

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

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

六、Redis 官方爲何不提供 Windows 版本?

由於目前 Linux 版本已經至關穩定,並且用戶量很大,無需開發 windows 版本,反而會帶來兼容性等問題。

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

512M

八、爲何 Redis 須要把全部數據放到內存中?

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

因此 redis 具備快速和數據持久化的特徵,若是不將數據放在內存中,磁盤 I/O 速度爲嚴重影響 redis 的性能。

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

九、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 等多個知識點的架構資料)合理利用本身每一分每一秒的時間來學習提高本身,不要再用"沒有時間「來掩飾本身思想上的懶惰!趁年輕,使勁拼,給將來的本身一個交代!

十、Redis 集羣方案什麼狀況下會致使整個集羣不可用?

有 A,B,C 三個節點的集羣,在沒有複製模型的狀況下,若是節點 B 失敗了,那麼整個集羣就會覺得缺乏5501-11000 這個範圍的槽而不可用。

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

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

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

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

十二、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 的發佈/訂閱功能來創建聊天系統!

1三、Redis 支持的 Java 客戶端都有哪些?官方推薦用哪一個?

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

1四、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)。

1五、Jedis 與 Redisson 對比有什麼優缺點?

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

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

1六、說說 Redis 哈希槽的概念?

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

1七、Redis 集羣的主從複製模型是怎樣的?

爲了使在部分節點失敗或者大部分節點沒法通訊的狀況下集羣仍然可用,因此集羣使用了主從複製模型,每一個節點都會有 N-1 個複製品.

1八、Redis 集羣會有寫操做丟失嗎?爲何?

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

1九、Redis 集羣之間是如何複製的?

異步複製

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

16384 個

2一、Redis 集羣如何選擇數據庫?

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

2二、Redis 中的管道有什麼用?

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

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

2三、怎麼理解 Redis 事務?

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

2四、Redis 事務相關的命令有哪幾個?

MULTI、EXEC、DISCARD、WATCH

2五、Redis key 的過時時間和永久有效分別怎麼設置?

EXPIRE 和 PERSIST 命令

2六、Redis 如何作內存優化?

儘量使用散列表(hashes),散列表(是說散列表裏面存儲的數少)使用的內存很是小,因此你應該儘量的將你的數據模型抽象到一個散列表裏面。

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

2七、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 的狀況:

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

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

三、高可用,redis 支持集羣,能夠實現主動複製,讀寫分離,而對於 memcache 若是想要實現高可用,須要進行二次開發。

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

選擇 memcache 的場景:

一、純 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 從緩存當中讀取數據失敗,從從庫當中讀取到舊數據,而後更新至緩存,這個時候,緩存當中的就是舊的數據。

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

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

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

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

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

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

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

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

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

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

四、儘可能避免在壓力大得主庫上增長從庫

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

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 秒以前的數據作輪詢處理。

能夠加一下Java進階高級架構:416843702進羣便可獲取往期BAT資料以及視頻。(助你金三銀四能跳槽漲薪)

收集了還有你不知道的其它面試題(springboot、mybatis、併發、java中高級面試總結等)

clipboard.png

clipboard.png

相關文章
相關標籤/搜索