史上最全51道redis面試題(含答案),面試再也不怕了


史上最全51道redis面試題(含答案),面試再也不怕了

1、什麼是NoSQL?列舉幾個你知道的NoSQL數據庫。

答:①許多網站在海量用戶訪問的高併發情況下出現崩潰問題,根本原因是關係型數據庫。關係型數據庫有性能瓶頸:磁盤IO性能低下、擴展瓶頸:數據關係複雜,擴展性差,不便於大規模集群。②NoSQL即Not-Only SQL,泛指非關係型數據庫,作為關係型數據庫的補充,降低了磁盤IO次數——使用內存存儲、去除數據間關係——不存儲關係,僅存儲數據。③NoSQL的特徵:特徵:可擴容,可伸縮;大數據量下高性能;靈活的數據模型;高可用。④常見的NoSQL數據庫:Redis、memcache、HBase、MongoDB。

2、簡單講講Redis的含義

答:①Redis (REmote DIctionary Server) 是用 C 語言開發的一個開源的高性能鍵值對(key-value)數據庫。②Redis數據庫中的數據間沒有必然的關聯關係,內部採用單線程機制進行工作,性能比較高,支持持久化存儲。③支持多種數據類型,包括字符串類型 string、列表類型 list、散列類型 hash、集合類型 set、有序集合類型 sorted_set。

3、Redis有哪些應用場景?

答:①為熱點數據加速查詢(主要場景),如熱點商品、熱點新聞、熱點資訊、推廣類等高訪問量信息等。②應用於任務隊列,如秒殺、搶購、購票排隊等。③即時信息查詢,如排行榜、各類網站訪問統計、公交到站信息、在線人數信息(聊天室、網站)、設備信號等。④時效性信息控制,如驗證碼控制、投票控制等。⑤分佈式數據共享,如分佈式集群架構中的 session 分離以及消息隊列、分佈式鎖等。

4、簡述string類型的基本操作和注意事項

答:①存儲的數據:單個數據,最簡單常用的數據存儲類型。存儲數據的格式:一個存儲空間保存一個數據。存儲內容:通常使用字符串,如果字符串以整數的形式展示,可以作為數字操作使用。②添加/修改數據:set key value、獲取數據:get key、刪除數據:del key、添加/修改多個數據:mset key1 value1 key2 value2 ...、獲取多個數據:mget key1 key2 …、獲取數據字符個數(字符串長度):strlen key、追加信息到原始信息後部(如果原始信息存在就追加,否則新建):append key value。③string在redis內部存儲默認就是一個字符串,當遇到增減類操作incr,decr時會轉成數值型進行計算。redis所有的操作都是原子性的,採用單線程處理所有業務,命令是一個一個執行的,因此無需考慮併發 帶來的數據影響。注意:按數值進行操作的數據,如果原始數據不能轉成數值,或超越了redis 數值上限範圍(java中long型數據最大值,Long.MAX_VALUE)將報錯。

5、簡述hash類型的基本操作和注意事項

答:①存儲需求:對一系列存儲的數據進行編組,方便管理,一般存儲對象信息。存儲結構:一個存儲空間保存多個鍵值對數據。底層使用哈希表結構實現數據存儲。②如果field數量較少,存儲結構優化為類數組結構;如果field數量較多,存儲結構使用HashMap結構。③添加/修改數據:hset key field value、獲取數據:hget key field,hgetall key、 刪除數據:hdel key field1 [field2]、添加/修改多個數據:hmset key field1 value1 field2 value2 …、 獲取多個數據:hmget key field1 field2 …、獲取哈希表中字段的數量:hlen key、獲取哈希表中是否存在指定的字段:hexists key field。③hash類型下的value只能存儲字符串,不允許存儲其他數據類型,不存在嵌套現象。如果數據未獲取到, 對應的值為(nil)。每個 hash 可以存儲 2^32 - 1 個鍵值對。hash類型十分貼近對象的數據存儲形式,並且可以靈活添加刪除對象屬性。但hash設計初衷不是為了存儲大量對象而設計的,不可濫用,更不可以將hash作為對象列表使用。hgetall 操作可以獲取全部屬性,如果內部field過多,遍歷整體數據效率就很會低,有可能成為數據訪問瓶頸。

6、簡述list類型的基本操作和注意事項

答:①存儲需求:存儲多個數據,並對數據進入存儲空間的順序進行區分。存儲結構:一個存儲空間保存多個數據,且通過數據可以體現進入順序。保存多個數據,底層使用雙向鏈表存儲結構實現。②添加/修改數據:lpush key value1 [value2] …,rpush key value1 [value2] …、獲取數據:lrange key start stop,lindex key index,llen key、獲取並移除數據:lpop key,rpop key。獲取數據時可以設置等待時間,list為空時等待獲取。移除指定數據:lrem key count value。③list中保存的數據都是string類型的,數據總容量是有限的,最多2^32- 1 個元素(4294967295)。list具有索引的概念,但是操作數據時通常以隊列的形式進行入隊出隊操作,或以棧的形式進行入棧出棧操作。獲取全部數據操作結束索引設置為-1。list可以對數據進行分頁操作,通常第一頁的信息來自於list,第2頁及更多的信息通過數據庫的形式加載。

7、簡述set類型的基本操作和注意事項

答:①存儲需求:存儲大量的數據,在查詢方面提供更高的效率。存儲結構:能夠保存大量的數據,高效的內部存儲機制,便於查詢。與hash存儲結構完全相同,僅存儲鍵,不存儲值(nil),並且值是不允許重複的。②添加數據:sadd key member1 [member2]、獲取全部數據:smembers key、刪除數據:srem key member1 [member2]、獲取集合數據總量:scard key、判斷集合中是否包含指定數據:sismember key member。③set 類型不允許數據重複,如果添加的數據在 set 中已經存在,將只保留一份。set 雖然與hash的存儲結構相同,但是無法啟用hash中存儲值的空間。

8、簡述sorted-set類型的相關操作和注意事項

答:①存儲需求:數據排序有利於數據的有效展示,需要提供一種可以根據自身特徵進行排序的方式。存儲結構:新的存儲模型,可以保存可排序的數據,在set的存儲結構基礎上添加可排序字段。②添加數據:zadd key score1 member1 [score2 member2]、獲取全部數據:zrange key start stop [WITHSCORES],zrevrange key start stop [WITHSCORES]、刪除數據:zrem key member [member ...]。③score保存的數據存儲空間是64位,超過該範圍的話score保存的數據也可以是一個雙精度的double值,但可能會丟失精度,使用時候要慎重。sorted_set 底層存儲還是基於set結構的,因此數據不能重複,如果重複添加相同的數據,score值將被反覆覆蓋,保留最後一次修改的結果。

9、Key有哪些通用指令?

答:①刪除指定key:del key、獲取key是否存在:exists key、獲取key的類型:type key。②為指定key設置有效期:expire key seconds 單位秒,pexpire key milliseconds 單位毫秒、獲取key的有效時間:ttl key 如果key不存在或key失效顯示-2,沒設置有效期或永久性顯示-1,單位秒、pttl key以毫秒為單位、切換key從時效性轉換為永久性:persist key。③查詢key:keys pattern。查詢模式規則:* 匹配任意數量的任意符號,? 配合一個任意符號,[] 匹配一個指定符號。④為key改名:rename key newkey、renamenx key newkey新名不存在時才可使用。

10、Redis如何解決key的重複問題?數據庫有哪些基本操作?

答:①redis為每個服務提供有16個數據庫,編號從0到15。每個數據庫之間的數據相互獨立。②切換數據庫:select index、退出:quit、測試連通:ping、輸出信息:echo message。③移動到其他數據庫:move key db 、數據個數:dbsize 、清除該數據庫:flushdb 、清除所有數據庫:flushall 。

11、Jedis是什麼?

答:①Jedis是一種利用Java語言連接redis的服務,需要依賴redis.clients下的jedis包。②通過new Jedis(String address,int port)創建一個操作redis數據庫的對象,第一個參數是字符串類的ip地址,第二個參數是int類型的端口號。③之後通過Jedis類實例對象調用相關API實現對redis數據庫的操作。

12、新聞網站會出現熱點新聞,熱點新聞最大的特徵是時效性,如何自動控制熱點新聞的時效性?

答:redis 可以控制數據的生命週期,通過數據是否失效控制業務行為,適用於所有具有時效性限定控制的操作,使用String數據結構,通過setex key seconds value 可以設置數據有效的生命週期,有效時間以秒為單位,也可以通過psetex key milliseconds value設置數據的有效時間,有效時間以毫秒為單位。

13、你會如何設計與實現電商網站購物車?

答:①可以使用redis數據庫,以客戶id作為key,每位客戶創建一個hash存儲結構存儲對應的購物車信息。②將商品編號作為field,購買數量作為value進行存儲。③添加商品:追加全新的field與value。④瀏覽:遍歷hash。⑤更改數量:自增/自減,設置value值。⑥刪除商品:直接刪除field。⑦清空購物車:直接刪除key。⑧當前僅僅是將數據存儲到了redis中,並沒有起到加速的作用,商品信息還需要二次查詢數據庫,將每條購物車中的商品記錄保存成兩條field,field1專用於保存購買數量,命名格式:商品id:nums 數值;field2專用於保存購物車中顯示的信息,包含文字描述,圖片地址,所屬商家信息等,命名格式:商品id:info json數據 。

14、雙11活動日,銷售手機充值卡的商家對移動、聯通、電信的30元、50元、100元商品推出搶購活動,每種商品搶購上限1000張,你會怎麼解決?

答:①使用redis的hash數據結構,以商家id作為key、將參與搶購的商品id作為field、將參與搶購的商品數量作為對應的value。②搶購時使用降值的方式控制產品數量,通過hincrby key field increment實現對指定key的field值實現值的更新操作,例如hincrby CMCC card30:nums -10實現對移動的30元商品數量實現-10操作。

15、微信朋友圈點贊,要求按照點贊順序顯示點贊好友信息,如果取消點贊,移除對應好友信息,你會怎麼實現?

答:redis 可以應用於具有操作先後順序的數據控制,可以使用list數據結構實現,點贊時使用rpush key value從右添加實現順序顯示功能,取消點贊通過lrem key count value從list左邊開始移除指定數據。

16、每位用戶首次使用今日頭條時會設置3項愛好的內容,但是後期為了增加用戶的活躍度、興趣點,必須讓用戶對其他信息類別逐漸產生興趣,增加客戶留存度,如何實現?

答:①可以利用redis數據庫的set數據結構完成,系統分析出各個分類的最新或最熱點信息條目並組織成set集合,隨機挑選其中部分信息,配合用戶關注信息分類中的熱點信息組織成展示的全信息集合。②通過srandmember key [count]隨機獲取集合中指定數量的數據,通過spop key [count]隨機獲取集合中的某個數據並將該數據移出集合。

17、 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 適合的場景主要侷限在較小數據量的高性能操作和運算上。

18、Redis 與 memcached 相比有哪些優勢?

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

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

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

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

String、List、Set、Sorted Set、hashes

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

內存。

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

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

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

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

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

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

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

存放。

22、Redis 官方為什麼不提供 Windows 版本?

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

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

512M

24、為什麼 Redis 需要把所有數據放到內存中?

Redis 為了達到最快的讀寫速度將數據都讀到內存中,並通過異步的方式將數據寫入磁盤。 所以 redis 具有快速和數據持久化的特徵,如果不將數據放在內存中,磁盤 I/O 速度為嚴重影響 redis 的性能。

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

25、Redis 集群方案應該怎麼做?都有哪些方案?

1.codis

2.目前用的最多的集群方案,基本和 twemproxy 一致的效果,但它支持在節點數量改變情況下,舊節點 數據可恢復到新 hash 節點。redis cluster3.0 自帶的集群,特點在於他的分佈式算法不是一致性 hash,而是 hash 槽的概念,以及自 身支持節點設置從節點。具體看官方文檔介紹。

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

26、Redis 集群方案什麼情況下會導致整個集群不可用?

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

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

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

28、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 WITHSCORES

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

(5)發佈/訂閱

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

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

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

30、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)。

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

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

32、說說 Redis 哈希槽的概念?

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

33、Redis 集群的主從複製模型是怎樣的?

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

34、Redis 集群會有寫操作丟失嗎?為什麼?

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

35、Redis 集群之間是如何複製的?

異步複製

36、Redis 集群最大節點個數是多少?

16384 個

37、Redis 集群如何選擇數據庫?

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

38、Redis 中的管道有什麼用?

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

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

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

事務是一個原子操作:事務中的命令要麼全部被執行,要麼全部都不執行。

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

MULTI、EXEC、DISCARD、WATCH

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

EXPIRE 和 PERSIST 命令

41、Redis 如何做內存優化?

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

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

42、Redis 回收進程如何工作的?

一個客戶端運行了新的命令,添加了新的數據。

Redi 檢查內存使用情況,如果大於 maxmemory 的限制, 則根據設定好的策略進行回收。 一個新的命令被執行,等等。 所以我們不斷地穿越內存限制的邊界,通過不斷達到邊界然後不斷地回收回到邊界以下。 如果一個命令的結果導致大量內存被使用(例如很大的集合的交集保存到一個新的鍵),不用多久內存限 制就會被這個內存使用量超越。

43、加鎖機制

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

為啥要用 lua 腳本呢?因為一大坨複雜的業務邏輯,可以通過封裝在 lua 腳本中發送給 redis, 保證這段複雜業務邏輯執行的原子性。 那麼,這段 lua 腳本是什麼意思呢?這裡 KEYS[1]代表的是你加鎖的那個 key,比如說:RLoc k lock = redisson.getLock("myLock");這裡你自己設置了加鎖的那個鎖 key 就是“myLock”。

ARGV[1]代表的就是鎖 key 的默認生存時間,默認 30 秒。ARGV[2]代表的是加鎖的客戶端的 I D,類似於下面這樣:8743c9c0-0795-4907-87fd-6c719a6b4586:1

給大家解釋一下,第一段 if 判斷語句,就是用“exists myLock”命令判斷一下,如果你要加鎖 的那個鎖 key 不存在的話,你就進行加鎖。如何加鎖呢?很簡單,用下面的命令:hset myLoc k8743c9c0-0795-4907-87fd-6c719a6b4586:1 1,通過這個命令設置一個 hash 數據結構,這行命令執行後,會出現一個類似下面的數據結構:

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

44、鎖互斥機制

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

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

45、watch dog 自動延期機制

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

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

46、可重入加鎖機制

那如果客戶端 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,就對應著加鎖的次數 。

47、釋放鎖機制

如果執行 lock.unlock(),就可以釋放分佈式鎖,此時的業務邏輯也是非常簡單的。其實說白 了,就是每次都對 myLock 數據結構中的那個加鎖次數減 1。如果發現加鎖次數是 0 了,說明 這個客戶端已經不再持有鎖了,此時就會用:“del myLock”命令,從 redis 裡刪除這個 key。 然後呢,另外的客戶端 2 就可以嘗試完成加鎖了。這就是所謂的分佈式鎖的開源 Redisson 框 架的實現機制。

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

48、上述 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 實例宕機的時候,可能導致多個客戶端同時完成加鎖。

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

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

50、使用過 Redis 做異步隊列麼,你是怎麼用的?有什麼缺點?一般使用 list 結構作為隊列,rpush 生產消息,lpop 消費消息。當 lpop 沒有消息的時候,要適當 sleep 一會再重試。

缺點:

在消費者下線的情況下,生產的消息會丟失,得使用專業的消息隊列如 rabbitmq 等。 能不能生產一次消費多次呢? 使用 pub/sub 主題訂閱者模式,可以實現 1:N 的消息隊列。

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

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

如何避免?

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

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

緩存雪崩

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

如何避免?

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

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

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


分享到:


相關文章: