12.02 吐血整理的 60 個 Redis 面試題,全網最全了

1.Redis 是一個基於內存的高性能key-value數據庫。

2.Redis相比memcached有哪些優勢:

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

  • redis的速度比memcached快很多

  • redis可以持久化其數據

3.Redis是單線

redis利用隊列技術將併發訪問變為串行訪問,消除了傳統數據庫串行控制的開銷

4.Reids常用5種數據類型

  • string,list,set,sorted set,hash

6.Reids6種淘汰策略:

  • noeviction: 不

    刪除策略, 達到最大內存限制時, 如果需要更多內存, 直接返回錯誤信息。大多數寫命令都會導致佔用更多的內存(有極少數會例外。

  • **allkeys-lru:**所有key通用; 優先刪除最近最少使用(less recently used ,LRU) 的 key。

  • **volatile-lru:**只限於設置了 expire 的部分; 優先刪除最近最少使用(less recently used ,LRU) 的 key。

  • **allkeys-random:**所有key通用; 隨機刪除一部分 key。

  • volatile-random: 只限於設置了expire的部分; 隨機刪除一部分 key。

  • volatile-ttl: 只限於設置了expire的部分; 優先

    刪除剩餘時間(time to live,TTL) 短的key。

7.Redis的併發競爭問題如何解決?

單進程單線程模式,採用隊列模式將併發訪問變為串行訪問。Redis本身沒有鎖的概念,Redis對於多個客戶端連接並不存在競爭,利用setnx實現鎖。

8.Redis是使用c語言開發的。

9.Redis前端啟動命令

./redis-server

10.Reids支持的語言:

java、C、C#、C++、php、Node.js、Go等。

11.Redis 持久化方案:

Rdb 和 Aof

12.Redis 的主從複製

持久化保證了即使redis服務重啟也不會丟失數據,因為redis服務重啟後會將硬盤上持久化的數據恢復到內存中,但是當redis服務器的硬盤損壞了可能會導致數據丟失,如果通過redis的主從複製機制就可以避免這種單點故障,

13.Redis是單線程的,但Redis為什麼這麼快?

1、完全基於內存,絕大部分請求是純粹的內存操作,非常快速。數據存在內存中,類似於HashMap,HashMap的優勢就是查找和操作的時間複雜度都是O(1);

2、數據結構簡單,對數據操作也簡單,Redis中的數據結構是專門進行設計的;

3、採用單線程,避免了不必要的上下文切換和競爭條件,也不存在多進程或者多線程導致的切換而消耗 CPU,不用去考慮各種鎖的問題,不存在加鎖釋放鎖操作,沒有因為可能出現死鎖而導致的性能消耗;

4、使用多路I/O複用模型,非阻塞IO;這裡“多路”指的是多個網絡連接,“複用”指的是複用同一個線程

5、使用底層模型不同,它們之間底層實現方式以及與客戶端之間通信的應用協議不一樣,Redis直接自己構建了VM 機制 ,因為一般的系統調用系統函數的話,會浪費一定的時間去移動和請求;

14.為什麼Redis是單線程的?

Redis是基於內存的操作,CPU不是Redis的瓶頸,Redis的瓶頸最有可能是機器內存的大小或者網絡帶寬。既然單線程容易實現,而且CPU不會成為瓶頸,那就順理成章地採用單線程的方案了(畢竟採用多線程會有很多麻煩!)。

15.Redis info查看命令:info memory

16.Redis內存模型

used_memory:Redis分配器分配的內存總量(單位是字節),包括使用的虛擬內存(即swap);Redis分配器後面會介紹。used_memory_human只是顯示更友好。

used_memory_rss**:**Redis進程佔據操作系統的內存(單位是字節),與top及ps命令看到的值是一致的;除了分配器分配的內存之外,used_memory_rss還包括進程運行本身需要的內存、內存碎片等,但是不包括虛擬內存。

mem_fragmentation_ratio

**:**內存碎片比率,該值是used_memory_rss / used_memory的比值。

mem_allocator**:**Redis使用的內存分配器,在編譯時指定;可以是 libc 、jemalloc或者tcmalloc,默認是jemalloc;截圖中使用的便是默認的jemalloc。

17.Redis內存劃分

數據

作為數據庫,數據是最主要的部分;這部分佔用的內存會統計在used_memory中。

進程本身運行需要的內存

Redis主進程本身運行肯定需要佔用內存,如代碼、常量池等等;這部分內存大約幾兆,在大多數生產環境中與Redis數據佔用的內存相比可以忽略。這部分內存不是由jemalloc分配,因此不會統計在used_memory中。

緩衝內存

緩衝內存包括客戶端緩衝區、複製積壓緩衝區、AOF緩衝區等;其中,客戶端

緩衝存儲客戶端連接的輸入輸出緩衝;複製積壓緩衝用於部分複製功能;AOF緩衝區用於在進行AOF重寫時,保存最近的寫入命令。在瞭解相應功能之前,不需要知道這些緩衝的細節;這部分內存由jemalloc分配,因此會統計在used_memory中。

內存碎片

內存碎片是Redis在分配、回收物理內存過程中產生的。例如,如果對數據的更改頻繁,而且數據之間的大小相差很大,可能導致redis釋放的空間在物理內存中並沒有釋放,但redis又無法有效利用,這就形成了內存碎片。內存碎片不會統計在used_memory中。

18.Redis對象有5種類型

無論是哪種類型,Redis都不會直接存儲,而是通過redisObject對象進行存儲。

19.Redis沒有直接使用C字符串

(即以空字符’\\0’結尾的字符數組)作為默認的字符串表示,而是使用了SDS。SDS是簡單動態字符串(Simple Dynamic String)的縮寫。

20.Reidis的SDS在C字符串的基礎上加入了free和len字段

21.Reids主從複製

複製是高可用Redis的基礎,哨兵和集群都是在複製基礎上實現高可用的。複製主要實現了數據的多機備份,以及對於讀操作的負載均衡和簡單的故障恢復。缺陷:故障恢復無法自動化;寫操作無法負載均衡;存儲能力受到單機的限制。

22.Redis哨兵

在複製的基礎上,哨兵實現了自動化的故障恢復。缺陷:寫操作無法負載均衡;存儲能力受到單機的限制。

23.Reids持久化觸發條件

RDB持久化的觸發分為手動觸發和自動觸發兩種。

24.Redis 開啟AOF

Redis服務器默認開啟RDB,關閉AOF;要開啟AOF,需要在配置文件中配置:

appendonly yes

25.AOF常用配置總結

下面是AOF常用的配置項,以及默認值;前面介紹過的這裡不再詳細介紹。

  • appendonly no:是否開啟AOF

  • appendfilename "appendonly.aof":AOF文件名

  • dir ./:RDB文件和AOF文件所在目錄

  • appendfsync everysec:fsync持久化策略

  • no-appendfsync-on-rewrite no:AOF重寫期間是否禁止fsync;如果開啟該選項,可以減輕文件重寫時CPU和硬盤的負載(尤其是硬盤),但是可能會丟失AOF重寫期間的數據;需要在負載和安全性之間進行平衡

  • auto-aof-rewrite-percentage 100:文件重寫觸發條件之一

  • auto-aof-rewrite-min-size 64mb:文件重寫觸發提交之一

  • aof-load-truncated yes:如果AOF文件結尾損壞,Redis啟動時是否仍載入AOF文件

26.RDB和AOF的優缺點

RDB持久化

優點:RDB文件緊湊,體積小,網絡傳輸快,適合全量複製;恢復速度比AOF快很多。當然,與AOF相比,RDB最重要的優點之一是對性能的影響相對較小。

缺點:RDB文件的致命缺點在於其數據快照的持久化方式決定了必然做不到實時持久化,而在數據越來越重要的今天,數據的大量丟失很多時候是無法接受的,因此AOF持久化成為主流。此外,RDB文件需要滿足特定格式,兼容性差(如老版本的Redis不兼容新版本的RDB文件)。

AOF持久化

與RDB持久化相對應,AOF的優點在於支持秒級持久化、兼容性好,缺點是文件大、恢復速度慢、對性能影響大。

27.持久化策略選擇

(1)如果Redis中的數據完全丟棄也沒有關係(如Redis完全用作DB層數據的cache),那麼無論是單機,還是主從架構,都可以不進行任何持久化。

(2)在單機環境下(對於個人開發者,這種情況可能比較常見),如果可以接受十幾分鍾或更多的數據丟失,選擇RDB對Redis的性能更加有利;如果只能接受秒級別的數據丟失,應該選擇AOF。

(3)但在多數情況下,我們都會配置主從環境,slave的存在既可以實現數據的熱備,也可以進行讀寫分離分擔Redis讀請求,以及在master宕掉後繼續提供服務。

28.redis緩存被擊穿處理機制

使用mutex。簡單地來說,就是在緩存失效的時候(判斷拿出來的值為空),不是立即去load db,而是先使用緩存工具的某些帶成功操作返回值的操作(比如Redis的SETNX或者Memcache的ADD)去set一個mutex key,當操作返回成功時,再進行load db的操作並回設緩存;否則,就重試整個get緩存的方法

29.Redis還提供的高級工具

像慢查詢分析、性能測試、Pipeline、事務、Lua自定義命令、Bitmaps、HyperLogLog、發佈/訂閱、Geo等個性化功能。

30.Redis常用管理命令

<code># dbsize 返回當前數據庫 key 的數量。
# info 返回當前 redis 服務器狀態和一些統計信息。
# monitor 實時監聽並返回redis服務器接收到的所有請求信息。
# shutdown 把數據同步保存到磁盤上,並關閉redis服務。
# config get parameter 獲取一個 redis 配置參數信息。(個別參數可能無法獲取)
# config set parameter value 設置一個 redis 配置參數信息。(個別參數可能無法獲取)
# config resetstat 重置 info 命令的統計信息。(重置包括:keyspace 命中數、
# keyspace 錯誤數、 處理命令數,接收連接數、過期 key 數)
# debug object key 獲取一個 key 的調試信息。
# debug segfault 製造一次服務器當機。
# flushdb 刪除當前數據庫中所有 key,此方法不會失敗。小心慎用
# flushall 刪除全部數據庫中所有 key,此方法不會失敗。小心慎用

/<code>

31.Reids工具命令

<code>#redis-server:Redis 服務器的 daemon 啟動程序
#redis-cli:Redis 命令行操作工具。當然,你也可以用 telnet 根據其純文本協議來操作
#redis-benchmark:Redis 性能測試工具,測試 Redis 在你的系統及你的配置下的讀寫性能
$redis-benchmark -n 100000 –c 50
#模擬同時由 50 個客戶端發送 100000 個 SETs/GETs 查詢
#redis-check-aof:更新日誌檢查
#redis-check-dump:本地數據庫檢查

/<code>

32.為什麼需要持久化?

由於Redis是一種內存型數據庫,即服務器在運行時,系統為其分配了一部分內存存儲數據,一旦服務器掛了,或者突然宕機了,那麼數據庫裡面的數據將會丟失,為了使服務器即使突然關機也能保存數據,必須通過持久化的方式將數據從內存保存到磁盤中。

33.判斷key是否存在

exists key +key名字

34.刪除key

<code>del key1 key2 ...
/<code>

35.緩存和數據庫間數據一致性問題

分佈式環境下(單機就不用說了)非常容易出現緩存和數據庫間的數據一致性問題,針對這一點的話,只能說,如果你的項目對緩存的要求是強一致性的,那麼請不要使用緩存。我們只能採取合適的策略來降低緩存和數據庫間數據不一致的概率,而無法保證兩者間的強一致性。合適的策略包括 合適的緩存更新策略,更新數據庫後要及時更新緩存、緩存失敗時增加重試機制,例如MQ模式的消息隊列。

36.布隆過濾器

bloomfilter就類似於一個hash set,用於快速判某個元素是否存在於集合中,其典型的應用場景就是快速判斷一個key是否存在於某容器,不存在就直接返回。布隆過濾器的關鍵就在於hash算法和容器大小

37.緩存雪崩問題

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

解決方案:

1、也是像解決緩存穿透一樣加鎖排隊。

2、建立備份緩存,緩存A和緩存B,A設置超時時間,B不設值超時時間,先從A讀緩存,A沒有讀B,並且更新A緩存和B緩存;

38.緩存併發問題

這裡的併發指的是多個redis的client同時set key引起的併發問題。比較有效的解決方案就是把redis.set操作放在隊列中使其串行化,必須的一個一個執行,具體的代碼就不上了,當然加鎖也是可以的,至於為什麼不用redis中的事務,留給各位看官自己思考探究。

39.Redis分佈式

redis支持主從的模式。原則:Master會將數據同步到slave,而slave不會將數據同步到master。Slave啟動時會連接master來同步數據。

這是一個典型的分佈式讀寫分離模型。我們可以利用master來插入數據,slave提供檢索服務。這樣可以有效減少單個機器的併發訪問數量

40.讀寫分離模型

通過增加Slave DB的數量,讀的性能可以線性增長。為了避免Master DB的單點故障,集群一般都會採用兩臺Master DB做雙機熱備,所以整個集群的讀和寫的可用性都非常高。讀寫分離架構的缺陷在於,不管是Master還是Slave,每個節點都必須保存完整的數據,如果在數據量很大的情況下,集群的擴展能力還是受限於單個節點的存儲能力,而且對於Write-intensive類型的應用,讀寫分離架構並不適合。

41.數據分片模型

為了解決讀寫分離模型的缺陷,可以將數據分片模型應用進來。

可以將每個節點看成都是獨立的master,然後通過業務實現數據分片。

結合上面兩種模型,可以將每個master設計成由一個master和多個slave組成的模型。

42. redis常見性能問題和解決方案:

Master最好不要做任何持久化工作,如RDB內存快照和AOF日誌文件

如果數據比較重要,某個Slave開啟AOF備份數據,策略設置為每秒同步一次

為了主從複製的速度和連接的穩定性,Master和Slave最好在同一個局域網內

儘量避免在壓力很大的主庫上增加從庫

43.redis通訊協議

RESP 是redis客戶端和服務端之前使用的一種通訊協議;RESP 的特點:實現簡單、快速解析、可讀性好

44.Redis分佈式鎖實現

先拿setnx來爭搶鎖,搶到之後,再用expire給鎖加一個過期時間防止鎖忘記了釋放。**如果在setnx之後執行expire之前進程意外crash或者要重啟維護了,那會怎麼樣?**set指令有非常複雜的參數,這個應該是可以同時把setnx和expire合成一條指令來用的!

45.Redis做異步隊列

一般使用list結構作為隊列,rpush生產消息,lpop消費消息。當lpop沒有消息的時候,要適當sleep一會再重試。缺點:在消費者下線的情況下,生產的消息會丟失,得使用專業的消息隊列如rabbitmq等。**能不能生產一次消費多次呢?**使用pub/sub主題訂閱者模式,可以實現1:N的消息隊列。

46.Redis中海量數據的正確操作方式

利用SCAN系列命令(SCAN、SSCAN、HSCAN、ZSCAN)完成數據迭代。

47.SCAN系列命令注意事項

  • SCAN的參數沒有key,因為其迭代對象是DB內數據;

  • 返回值都是數組,第一個值都是下一次迭代遊標;

  • 時間複雜度:每次請求都是O(1),完成所有迭代需要O(N),N是元素數量;

  • 可用版本:version >= 2.8.0;

48.Redis 管道 Pipeline

在某些場景下我們在一次操作中可能需要執行多個命令,而如果我們只是一個命令一個命令去執行則會浪費很多網絡消耗時間,如果將命令一次性傳輸到<code>Redis/<code>中去再執行,則會減少很多開銷時間。但是需要注意的是<code>pipeline/<code>中的命令並不是原子性執行的,也就是說管道中的命令到達<code>Redis/<code>服務器的時候可能會被其他的命令穿插

49.事務不支持回滾

50.手寫一個 LRU 算法

<code>class LRUCache extends LinkedHashMap {
private final int CACHE_SIZE;

/**
* 傳遞進來最多能緩存多少數據
*
* @param cacheSize 緩存大小
*/
public LRUCache(int cacheSize) {
// true 表示讓 linkedHashMap 按照訪問順序來進行排序,最近訪問的放在頭部,最老訪問的放在尾部。
super((int) Math.ceil(cacheSize / 0.75) + 1, 0.75f, true);
CACHE_SIZE = cacheSize;
}

@Override
protected boolean removeEldestEntry(Map.Entry eldest) {
// 當 map中的數據量大於指定的緩存個數的時候,就自動刪除最老的數據。
return size > CACHE_SIZE;
}
}

/<code>

51.多節點 Redis 分佈式鎖:Redlock 算法

獲取當前時間(start)。

依次向 N 個 <code>Redis/<code>節點請求鎖。請求鎖的方式與從單節點<code>Redis/<code>獲取鎖的方式一致。為了保證在某個<code>Redis/<code>節點不可用時該算法能夠繼續運行,獲取鎖的操作都需要設置超時時間,需要保證該超時時間遠小於鎖的有效時間。這樣才能保證客戶端在向某個<code>Redis/<code>節點獲取鎖失敗之後,可以立刻嘗試下一個節點。

計算獲取鎖的過程總共消耗多長時間(consumeTime = end - start)。如果客戶端從大多數 <code>Redis/<code>節點(>= N/2 + 1) 成功獲取鎖,並且獲取鎖總時長沒有超過鎖的有效時間,這種情況下,客戶端會認為獲取鎖成功,否則,獲取鎖失敗。

如果最終獲取鎖成功,鎖的有效時間應該重新設置為鎖最初的有效時間減去 <code>consumeTime/<code>。

如果最終獲取鎖失敗,客戶端應該立刻向所有 <code>Redis/<code>節點發起釋放鎖的請求。

52.Redis 中設置過期時間主要通過以下四種方式

  • expire key seconds:設置 key 在 n 秒後過期;

  • pexpire key milliseconds:設置 key 在 n 毫秒後過期;

  • expireat key timestamp:設置 key 在某個時間戳(精確到秒)之後過期;

  • pexpireat key millisecondsTimestamp:設置 key 在某個時間戳(精確到毫秒)之後過期;

53.Reids三種不同刪除策略

定時刪除:在設置鍵的過期時間的同時,創建一個定時任務,當鍵達到過期時間時,立即執行對鍵的刪除操作

惰性刪除:放任鍵過期不管,但在每次從鍵空間獲取鍵時,都檢查取得的鍵是否過期,如果過期的話,就

刪除該鍵,如果沒有過期,就返回該鍵

定期刪除:每隔一點時間,程序就對數據庫進行一次檢查,刪除裡面的過期鍵,至於要刪除多少過期鍵,以及要檢查多少個數據庫,則由算法決定。

54.定時刪除

  • **優點:**對內存友好,定時刪除策略可以保證過期鍵會盡可能快地被刪除,並釋放國期間所佔用的內存

  • **缺點:**對cpu時間不友好,在過期鍵比較多時,刪除任務會佔用很大一部分cpu時間,在內存不緊張但cpu時間緊張的情況下,將cpu時間用在刪除

    和當前任務無關的過期鍵上,影響服務器的響應時間和吞吐量

55.定期刪除

由於定時刪除會佔用太多cpu時間,影響服務器的響應時間和吞吐量以及惰性刪除浪費太多內存,有內存洩露的危險,所以出現一種整合和折中這兩種策略的定期刪除策略。

  1. 定期刪除策略每隔一段時間執行一次刪除過期鍵操作,並通過限制刪除操作執行的時長和頻率來減少刪除操作對CPU時間的影響。

  2. 定時

    刪除策略有效地減少了因為過期鍵帶來的內存浪費。

56.惰性刪除

  • **優點:**對cpu時間友好,在每次從鍵空間獲取鍵時進行過期鍵檢查並是否刪除刪除目標也僅限當前處理的鍵,這個策略不會在其他無關的刪除任務上花費任何cpu時間。

  • **缺點:**對內存不友好,過期鍵過期也可能不會被刪除,導致所佔的內存也不會釋放。甚至可能會出現內存洩露的現象,當存在很多過期鍵,而這些過期鍵又沒有被訪問到,這會可能導致它們會一直保存在內存中,造成內存洩露

57.Reids 管理工具:Redis Manager 2.0

github地址

58.Redis常見的幾種緩存策略

  • Cache-Aside

  • Read-Through

  • Write-Through

  • Write-Behind

59.Redis Module 實現布隆過濾器

Redis module 是Redis 4.0 以後支持的新的特性,這裡很多國外牛逼的大學和機構提供了很多牛逼的Module 只要編譯引入到Redis 中就能輕鬆的實現我們某些需求的功能。在Redis 官方Module 中有一些我們常見的一些模塊,我們在這裡就做一個簡單的使用。

  • neural-redis 主要是神經網絡的機器學,集成到redis 可以做一些機器訓練感興趣的可以嘗試

  • RedisSearch 主要支持一些富文本的的搜索

  • RedisBloom 支持分佈式環境下的Bloom 過濾器

60.Redis 到底是怎麼實現“附近的人”

使用方式

<code>GEOADD key longitude latitude member [longitude latitude member ...]
/<code>

將給定的位置對象(緯度、經度、名字)添加到指定的key。其中,key為集合名稱,member為該經緯度所對應的對象。在實際運用中,當所需存儲的對象數量過多時,可通過設置多key(如一個省一個key)的方式對對象集合變相做sharding,避免單集合數量過多。

成功插入後的返回值:

<code>(integer) N
/<code>

其中N為成功插入的個數。

吐血整理的 60 个 Redis 面试题,全网最全了

END

關注


分享到:


相關文章: