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 AIGC Hackathon 黑客馬拉松全新出發!
- 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領域中的併發鎖,我們可以從中學習到什麼內容?