2022最新版 Redis大廠面試題總結(附答案)
專注於PHP、MySQL、Linux和前端開發,感興趣的感謝點個關注喲!!!文章已收錄,主要包含的技術有PHP、Redis、MySQL、JavaScript、HTML&CSS、Linux、Java、Golang、Linux和工具資源等相關理論知識、面試題和實戰內容。
文章導語
大家好,前段時間一直在忙找工作相關的事情。最近工作穩定了,於是把面試過程中遇到的Redis相關知識問題總結下來,希望能夠對大家面試、學習有所幫助。 本文重點分享資料型別,其他內容後續章節分享,也可以點選上方收錄連結。
本系列面試題會從認識Redis、Redis幾大資料型別、常見的使用場景和解決方案、Redis主從複製、Redis哨兵、Redis叢集等相關知識點進行總結。不僅僅單純的從文字方面終結,還會帶有更多的圖文方面,儘可能的讓大家深入的學習。
同時我也準備了一份彙總大綱,相對文字來說更加的清晰、明瞭。需要的小夥伴也可以回覆 Redis面試大綱
。同時該系列問題也會不斷的完善、更新。讓大家學習到更多的知識。
認識Redis
- REmote DIctionary Server(Redis) 是一個由 Salvatore Sanfilippo 寫的 key-value 儲存系統,是跨平臺的非關係型資料庫。
- Redis 是一個開源的使用 ANSI C 語言編寫、遵守 BSD 協議、支援網路、可基於記憶體、分散式、可選永續性的鍵值對(Key-Value)儲存資料庫,並提供多種語言的 API。
Redis的資料型別都有哪些
- 有五種基本資料型別,分別是string、hash、list、有序集合(zset)、集合(set)。在5.0之後增加了一種Stream型別。
- 額外的有GEO、HyperLogLog、BitMap。
Redis使用的場景有哪些
- 資料快取(使用者資訊、商品數量、文章閱讀數量)
- 訊息推送(站點的訂閱)
- 佇列(削峰、解耦、非同步)
- 排行榜(積分排行)
- 社交網路(共同好友、互踩、下拉重新整理)
- 計數器(商品庫存,站點線上人數、文章閱讀、點贊)
- 基數計算
- GEO計算
Redis功能特點都有哪些
- 持久化
- 豐富的資料型別(string、list、hash、set、zset、釋出訂閱等)
- 高可用方案(哨兵、叢集、主從)
- 事務
- 豐富的客戶端
- 提供事務
- 訊息釋出訂閱
- Geo
- HyperLogLog
- 事務
- 分散式事務鎖
Redis如何實現分散式鎖
- Redis可以使用
setnx key value
+expire key expire_time
來實現分散式鎖。 - 正常情況下,上面的命令是沒有問題的。當Redis出現異常的情況下,很容易出現非原子性操作。
- 非原子性操作指的的setnx命令執行成功,但是expire沒有執行成功,此時key就成為了一個無過期時間的key,一直保留在Redis中,導致其他的請求就無法執行。
-
要解決該問題,可以使用lua指令碼實現。通過lua實現命令的原子性操作。
在Redis中使用set命令,加引數也可以實現分散式鎖。
set key vale nx ex|px ttl
<!-- tabs:start -->
通過陣列定義
--- getLock key local key = KEYS[1] local requestId = KEYS[2] local ttl = tonumber(KEYS[3]) local result = redis.call('setnx', key, requestId) if result == 1 then --PEXPIRE:以毫秒的形式指定過期時間 redis.call('pexpire', key, ttl) else result = -1; -- 如果value相同,則認為是同一個執行緒的請求,則認為重入鎖 local value = redis.call('get', key) if (value == requestId) then result = 1; redis.call('pexpire', key, ttl) end end -- 如果獲取鎖成功,則返回 1 return result
通過陣列定義
-- releaseLock key local key = KEYS[1] local requestId = KEYS[2] local value = redis.call('get', key) if value == requestId then redis.call('del', key); return 1; end return -1
<!-- tabs:end -->
tips:如果對一個key第一次set添加了過期時間,第二次操作時沒有新增過期時間,此時key是沒有過期時間的(過期時間被覆蓋為永久不過期)。
Redis底層資料結構有哪些
Redis底層資料結構主要有六種,這六種構成了五種常用的資料型別。其他的資料型別,例如bitmap、hyperLogLog也是基於這五大資料型別實現。具體的資料結構圖如下:
說說Redis的全域性Hash表
為了實現從鍵到值的快速訪問,Redis 使用了一個雜湊表來儲存所有鍵值對。結構圖如下:
Hash表應用如此廣泛的一個重要原因,就是從理論上來說,它能以 O(1) 的複雜度快速查詢資料。Hash 表通過Hash函式的計算,就能定位資料在表中的位置,緊接著可以對資料進行操作,這就使得資料操作非常快速。
那麼我們該如何解決雜湊衝突呢?可以考慮使用以下兩種解決方案:
- 第一種方案,就是使用鏈式雜湊。但是鏈式雜湊容易導致Hash的鏈過長,查詢效率降低。
- 第二種方案,就是當鏈式雜湊的鏈長達到一定長度時,我們可以使用rehash。不過,執行rehash本身開銷比較大。
del刪除大量key有什麼問題
- 使用del命令可以刪除一個key或者多個key,其時間複雜度為O(N),這裡的N表示刪除的key數量。
- 刪除單個key時,其時間複雜度為O(1)。
- 當刪除單個列表、集合、有序集合或者雜湊列表型別的key時,時間複雜度為O(M),這裡的M表示key對應的內部元素個數。
說說Redis的全域性hash實現原理
說說Zset在skiplist和ziplist實現原理
Redis事務都有哪些命令
mutil: 開啟事務;exec: 提交事務;discard: 回滾事務。watch: 監聽key; unwatch: 取消監聽key。
Redis中的事務是否是原子性
嚴格來說,Redis中的事務並非滿足事務的原子性操作。當事務在命令組隊時沒有發生錯誤,則事務是原子性;當事務在命令組隊時發生錯誤,則事務是非原子性的。
Redis如何解決事務之間的衝突
- 使用watch監聽key變化,當key發生變化,事務中的所有操作都會被取消。
- 使用樂觀鎖,通過版本號實現。
- 使用悲觀鎖,每次開啟事務時,都新增一個鎖,事務執行結束之後釋放鎖。
悲觀鎖:悲觀鎖(Pessimistic Lock),顧名思義,就是很悲觀,每次去拿資料的時候都認為別人會修改,所以每次在拿資料的時候都會上鎖,這樣別人拿到這個資料就會block(阻塞)直到它拿到鎖。傳統的關係型資料庫裡面 就用到了很多這種鎖機制,比如行鎖、表鎖、讀鎖、寫鎖等,都是在做操作之前先上鎖。
樂觀鎖:樂觀鎖(Optimistic Lock),顧名思義,就是很樂觀,每次去那資料的時候都認為別人不會修改,所以 不會上鎖,但是在修改的時候會判斷一下在此期間別人有沒有去更新這個資料,可以使用版本號等機 制。樂觀鎖適用於多讀的應用型別,這樣可以提高吞吐量。redis就是使用這種check-and-set機制實現 事務的。
事務中的watch有什麼用
在執行multi之前,先執行watch key1 [key2 ...],可以監視一個或者多個key。若在事務的exec命令之前,這些key對應的值被其他命令所改動了,那麼事務中所有命令都將被打斷,即事務所有操作將被取消執行。
Redis事務的三大特性
- 事務中的所有命令都會序列化、按順序地執行,事務在執行過程中,不會被其他客戶端傳送來的命令請求所打斷。
- 佇列中的命令沒有提交(exec)之前,都不會實際被執行,因為事務提交前任何指令都不會被實際執行。
- 事務中如果有一條命令執行失敗,後續的命令仍然會被執行,沒有回滾。如果在組隊階段,有1個失敗了,後面都不會成功;如果在組隊階段成功了,在執行階段有那個命令失敗 就這條失敗,其他的命令則正常執行,不保證都成功或都失敗。
如何使用Redis實現佇列功能
- 可以使用list實現普通佇列,lpush新增到嘟列,lpop從佇列中讀取資料。
- 可以使用zset定期輪詢資料,實現延遲佇列。
- 可以使用釋出訂閱實現多個消費者佇列。
- 可以使用stream實現佇列。(推薦使用該方式實現)。
如何用Redis實現非同步佇列
- 一般使用list結構作為佇列,rpush生產訊息,lpop消費訊息。當lpop沒有訊息的時候,要適當sleep一會再重試。
- 如果對方追問可不可以不用sleep呢?list還有個指令叫blpop,在沒有訊息的時候,它會阻塞住直到訊息到來。
- 如果對方追問能不能生產一次消費多次呢?使用pub/sub主題訂閱者模式,可以實現1:N的訊息佇列。
- 如果對方追問pub/sub有什麼缺點?在消費者下線的情況下, 生產的訊息會丟失 ,可以使用Redis6增加的 stream資料型別 ,也可以使用專業的 訊息佇列如rabbitmq等 。
- 如果對方追問redis如何實現延時佇列? 使用sortedset,拿時間戳作為score ,訊息內容作為key呼叫zadd來生產訊息,消費者用zrangebyscore指令獲取N秒之前的資料輪詢進行處理。
Stream與list、zset和釋出訂閱區別
- list可以使用lpush向佇列中新增資料,lpop可以向佇列中讀取資料。list作為訊息佇列無法實現一個訊息多個消費者。如果出現訊息處理失敗,需要手動回滾訊息。
- zset在新增資料時,需要新增一個分值,可以根據該分值對資料進行排序,實現延遲訊息佇列的功能。訊息是否消費需要額外的處理。
- 釋出訂閱可以實現多個消費者功能,但是釋出訂閱無法實現資料持久化,容易導致資料丟失。並且開啟一個訂閱者無法獲取到之前的資料。
- stream借鑑了常用的MQ服務,新增一個訊息就會產生一個訊息ID,每一個訊息ID下可以對應多個消費組,每一個消費組下可以對應多個消費者。可以實現多個消費者功能,同時支援ack機制,減少資料的丟失情況。也是支援資料值持久化和主從複製功能。
如何設計一個網站每日、每月和每天的PV和UV
實現這樣的功能,如果只是統計一個彙總資料,推薦使用HyperLogLog資料型別。Redis HyperLogLog 是用來做基數統計的演算法,HyperLogLog 的優點是,在輸入元素的數量或者體積非常非常大時,計算基數所需的空間總是固定 的、並且是很小的。在 Redis 裡面,每個 HyperLogLog 鍵只需要花費 12 KB 記憶體,就可以計算接近 2^64 個不同元素的基 數。這和計算基數時,元素越多耗費記憶體就越多的集合形成鮮明對比。
Redis如何實現距離檢索功能
實現距離檢索,可以使用Redis中的GEO資料型別。GEO 主要用於儲存地理位置資訊,並對儲存的資訊進行操作,該功能在 Redis 3.2 版本新增。但是GEO適合精度不是很高的場景。由於GEO是在記憶體中進行計算,具備計算速度快的特點。
list和釋出訂閱實現佇列有什麼問題
- list可以使用lpush向佇列中新增資料,lpop可以向佇列中讀取資料。list作為訊息佇列無法實現一個訊息多個消費者。如果出現訊息處理失敗,需要手動回滾訊息。
- 釋出訂閱可以實現多個消費者功能,但是釋出訂閱無法實現資料持久化,容易導致資料丟失。並且開啟一個訂閱者無法獲取到之前的資料。
Redis如何實現秒殺功能
- 在秒殺場景下,超賣是一個非常嚴重的問題。常規的邏輯是先查詢庫存在減少庫存。但在秒殺場景中,無法保證減少庫存的過程中有其他的請求讀取了未減少的庫存資料。
- 由於Redis是單執行緒的執行,同一時刻只有一個執行緒進行操作。因此可以使用Redis來實現秒殺減少庫存。
- 在Redis的資料型別中,可以使用lpush,decr命令實現秒殺減少庫存。該命令屬於原子操作。
Redis如何實現使用者簽到功能
- 使用Redis實現使用者簽到可以使用bitmap實現。bitmap底層資料儲存的是1否者0,佔用記憶體小。
- Redis提供的資料型別BitMap(點陣圖),每個bit位對應0和1兩個狀態。雖然內部還是採用String型別儲存,但Redis提供了一些指令用於直接操作BitMap,可以把它看作一個bit陣列,陣列的下標就是偏移量。
- 它的優點是記憶體開銷小,效率高且操作簡單,很適合用於簽到這類場景。
- 缺點在於位計算和位表示數值的侷限。如果要用位來做業務資料記錄,就不要在意value的值。
Redis如何實現延遲佇列
- 使用Redis實現延遲佇列,可以使用zset資料型別。
- zset在新增資料時,需要新增一個分值,將時間作為分值,根據該分值對資料進行排序。
- 單獨開啟執行緒,根據分值大小定期實行資料。
Redis實現一個積分排行功能
- 使用Redis實現積分排行,可以使用zset資料型別。
- zset在新增資料時,需要新增一個分值,將積分作為分值,值作為使用者ID,根據該分值對資料進行排序。
字串型別儲存最大容量是多少
一個字串最大可儲存512M。
- SegmentFault 2022 年社群週報 Vol.9
- 社群精選 | 不容錯過的9個冷門css屬性
- 2022最新版 Redis大廠面試題總結(附答案)
- 手寫一個mini版本的React狀態管理工具
- 【vue3原始碼】十三、認識Block
- 天翼雲全場景業務無縫替換至國產原生作業系統CTyunOS!
- JavaScript 設計模式 —— 代理模式
- MobTech簡訊驗證ApiCloud端SDK
- 以羊了個羊為例,淺談小程式抓包與響應報文修改
- 這幾種常見的 JVM 調優場景,你知道嗎?
- 聊聊如何利用管道模式來進行業務編排(下篇)
- 通用ORM的設計與實現
- 如此狂妄,自稱高效能佇列的Disruptor有啥來頭?
- 為什麼要學習GoF設計模式?
- 827. 最大人工島 : 簡單「並查集 列舉」運用題
- 介紹 Preact Signals
- 手把手教你如何使用 Timestream 實現物聯網時序資料儲存和分析
- 850. 矩形面積 II : 掃描線模板題
- Java 併發程式設計解析 | 基於JDK原始碼解析Java領域中的併發鎖,我們可以從中學習到什麼內容?
- 令人困惑的 Go time.AddDate