目錄
一,基礎
二,經典
緩存雪崩:
1. Redis事務的原子性
2. 與MySQL事務的區別
1. 主從復制原理
2. 哨兵模式故障轉移流程
3. 客戶端感知故障轉移
三,高級
-
一,基礎
-
Redis的5種基礎數據類型及使用場景?Zset底層實現原理?
-
String(字符串)
-
場景:緩存簡單鍵值對(如用戶會話信息、計數器)、分布式鎖(如
SETNX
)、二進制數據存儲(如圖片Base64)。 -
特點:支持原子操作(如
INCR
、DECR
),最大存儲512MB。
-
-
List(列表)
-
場景:消息隊列(
LPUSH/RPOP
實現生產者-消費者)、最新消息排行(如微博時間線)、阻塞式任務調度。 -
特點:雙向鏈表,支持按索引操作(但時間復雜度高)。
-
-
Hash(哈希表)
-
場景:存儲對象(如用戶信息字段:
HSET user:1 name "Alice"
)、聚合數據(如購物車商品信息)。 -
特點:支持單字段讀寫,內存優化(底層為ziplist或hashtable)。
-
-
Set(集合)
-
場景:去重數據(如用戶標簽)、共同好友(
SINTER
求交集)、隨機抽獎(SRANDMEMBER
)。 -
特點:無序、自動去重,支持集合運算(并/交/差)。
-
-
Zset(有序集合)
-
場景:排行榜(按分數排序)、延遲隊列(以時間戳為score)、帶權重的任務調度。
-
特點:元素唯一但score可重復,支持范圍查詢(
ZRANGEBYSCORE
)。
-
-
Zset的底層由?跳躍表(Skip List)?和?字典(Dict)?組成,或在小數據量時使用?listpack(替代舊版的ziplist):
-
跳躍表:
-
多層鏈表結構,高層鏈表作為“快速通道”,支持O(log N)的查詢、插入、刪除。
-
每個節點包含
score
和value
,按score排序,支持高效范圍操作(如ZRANGE
)。
-
-
字典:
-
維護
value -> score
的映射,實現O(1)的單元素查詢。
-
-
內存優化:
-
元素數≤
zset-max-listpack-entries
且元素大小≤zset-max-listpack-value
時,使用listpack存儲。
-
-
-
-
Redis持久化機制RDB和AOF的區別?如何選擇?
-
特性 RDB AOF 原理 定時生成全量數據快照(二進制文件) 記錄所有寫命令(追加日志文件) 文件體積 小(壓縮二進制) 大(文本命令,需重寫優化) 恢復速度 快(直接加載內存) 慢(重放命令) 數據安全 可能丟失最后一次快照后的數據 可配置 fsync
策略(無/秒級/每次寫)資源消耗 高( fork
子進程內存開銷)低(追加日志,重寫時才有 fork
開銷) -
選擇策略:
-
高數據安全:選擇AOF(
appendfsync everysec
),容忍秒級丟失。 -
快速恢復/備份:選擇RDB(如每日備份)。
-
混合模式:Redis 4.0+支持
RDB+AOF
,AOF記錄增量,RDB做全量備份。
-
-
-
如何用Redis實現分布式鎖?需要注意哪些問題?
-
實現步驟:
-
// 加鎖:SET key unique_value NX PX 30000 String result = jedis.set("lock_key", "client1", "NX", "PX", 30000); if ("OK".equals(result)) {// 執行業務邏輯 } // 解鎖:Lua腳本保證原子性 String script = "if redis.call('get', KEYS[1]) == ARGV[1] then " +" return redis.call('del', KEYS[1]) " +"else " +" return 0 " +"end"; Object unlockResult = jedis.eval(script, Collections.singletonList("lock_key"), Collections.singletonList("client1"));
注意事項:
-
鎖過期時間:需預估業務耗時,建議設置自動續期(如Redisson的看門狗)。
-
唯一標識:value使用唯一值(如UUID+線程ID),避免誤刪其他客戶端的鎖。
-
優化方案:
-
使用Redisson庫,內置看門狗、可重入鎖、紅鎖(RedLock)實現。
-
對鎖操作添加重試機制(如指數退避)。
-
原子性操作:加鎖(
SET NX PX
)和釋放鎖(Lua腳本)必須原子化。 -
集群問題:
-
主從異步復制:主節點宕機可能導致鎖丟失,可考慮RedLock算法(需部署多實例)。
-
RedLock爭議:依賴系統時鐘一致性,需權衡CAP。
-
-
-
-
二,經典
- 什么是緩存穿透/雪崩/擊穿?分別給出解決方案
- 緩存穿透:
-
問題:大量請求查詢數據庫中不存在的數據(如非法ID),繞過緩存直接壓垮數據庫。
解決方案:? ? ? ??-
布隆過濾器(Bloom Filter):在緩存層前加布隆過濾器,預存所有合法鍵的哈希值,攔截非法請求。
-
特點:存在一定誤判率(可能將非法請求誤判為合法),需權衡內存占用。
-
-
緩存空值:對查詢結果為
null
的鍵,緩存空值并設置短過期時間(如30秒)。-
注意:需定期清理無效空值,避免內存浪費。
-
-
-
-
緩存雪崩:
-
問題:大量緩存同時過期或緩存服務宕機,導致請求全部涌向數據庫。
解決方案:-
隨機過期時間:為緩存設置基礎過期時間 + 隨機偏移值(如?
TTL = 24h + random(0, 1h)
)。 -
永不過期 + 異步更新:
-
緩存不設過期時間,通過后臺線程定期更新。
-
結合雙緩存策略:主緩存永不過期,備份緩存設置過期時間作為兜底。
-
-
熔斷降級:使用Hystrix等工具,在數據庫壓力過大時直接返回默認值或限流。
-
-
- 緩存擊穿:
-
問題:熱點數據過期時,高并發請求瞬間擊穿緩存,直接訪問數據庫。
解決方案: -
互斥鎖(Mutex Lock):
-
當緩存失效時,通過分布式鎖(如Redis的
SETNX
)控制只有一個線程重建緩存,其他線程等待。 -
示例代碼:
-
public String getData(String key) {String value = redis.get(key);if (value == null) {if (redis.setnx("lock_" + key, "1", 10)) { // 獲取鎖value = db.query(key);redis.setex(key, 3600, value);redis.del("lock_" + key); // 釋放鎖} else {Thread.sleep(100); // 等待后重試return getData(key);}}return value; }
-
邏輯過期:緩存永不過期,但存儲的數據中增加邏輯過期時間字段,由業務代碼判斷是否需要異步更新。
-
-
- 緩存穿透:
- Redis事務的原子性如何理解?與MySQL事務的區別?
-
1. Redis事務的原子性
-
-
實現方式:通過
MULTI
(開啟事務)、EXEC
(提交事務)、DISCARD
(取消事務)命令實現。 -
原子性含義:
-
命令隊列的原子執行:事務中的命令會被序列化并按順序執行,不會被其他客戶端命令打斷。
-
不支持回滾:若事務中某條命令執行失敗(如語法錯誤),其他命令仍會繼續執行,無回滾機制。
-
示例:
-
MULTI SET key1 "A" # 入隊 INCR key2 # 入隊(若key2非數值類型,執行時報錯) EXEC # 提交事務,第二條命令執行失敗,但第一條仍生效
-
-
2. 與MySQL事務的區別
特性 Redis MySQL 原子性 僅保證命令隊列的批量執行,無回滾 支持ACID,失敗時自動回滾 隔離性 無隔離級別,事務執行期間可能被其他客戶端修改數據 支持多隔離級別(如讀已提交、可重復讀) 持久性 依賴持久化機制(RDB/AOF) 依賴Redo Log和Binlog保證持久性 使用場景 簡單批量操作(如批量SET) 復雜業務邏輯(如轉賬、訂單處理)
-
-
- Redis主從復制原理?哨兵模式如何實現故障轉移?
-
1. 主從復制原理
-
核心流程:
-
全量同步(首次連接):
-
從節點發送
PSYNC ? -1
命令請求同步。 -
主節點執行
BGSAVE
生成RDB文件并發送給從節點,同時緩存期間的寫命令到復制緩沖區。 -
從節點加載RDB后,主節點發送緩沖區中的寫命令使其追上最新狀態。
-
-
增量同步(斷線重連):
-
從節點發送
PSYNC <runid> <offset>
,主節點根據offset
從復制緩沖區發送增量數據。 -
若復制緩沖區數據丟失(如緩沖區溢出),觸發全量同步。
-
-
-
關鍵配置:
-
repl-backlog-size
:調整復制緩沖區大小,避免頻繁全量同步。
-
-
2. 哨兵模式故障轉移流程
哨兵(Sentinel)是Redis的高可用解決方案,負責監控、通知和自動故障轉移:
-
監控:
-
每個哨兵節點定期向主節點、從節點和其他哨兵發送
PING
命令檢測存活狀態。
-
-
主觀下線(SDOWN):
-
若哨兵在
down-after-milliseconds
時間內未收到主節點的有效響應,標記其為主觀下線。
-
-
客觀下線(ODOWN):
-
當超過半數哨兵認為主節點主觀下線,則標記為客觀下線。
-
-
選舉Leader哨兵:
-
通過Raft算法選舉一個Leader哨兵來執行故障轉移。
-
-
故障轉移:
-
Leader哨兵從從節點列表中選出一個新的主節點(基于優先級、復制偏移量等)。
-
向其他從節點發送
SLAVEOF
命令,使其復制新主節點。 -
更新客戶端配置,通知其連接新主節點。
-
-
3. 客戶端感知故障轉移
-
客戶端通過訂閱哨兵的
+switch-master
事件獲取新主節點地址,或通過哨兵API動態查詢。
-
-
三,高級
- Redis Cluster集群模式的數據分片原理?如何實現動態擴容?
-
數據分片原理
Redis Cluster采用哈希槽(Hash Slot)機制進行數據分片,共有16384個槽位。每個鍵通過CRC16(key)
計算哈希值,再對16384取模確定所屬槽位。集群中的每個節點負責一部分槽,數據分布由槽分配決定。客戶端請求時,若連接節點不負責該槽,會返回MOVED
重定向錯誤,引導客戶端訪問正確節點。節點間通過Gossip協議交換集群狀態,維護槽分配信息的一致性。動態擴容實現
-
添加新節點:使用
CLUSTER MEET
將新節點加入集群。 -
遷移槽數據:
-
使用
redis-cli --cluster reshard
觸發槽重新分片。 -
源節點將槽內鍵逐個遷移到目標節點,期間對正在遷移的鍵的請求,源節點返回
ASK
重定向,客戶端需重試到目標節點。
-
-
更新槽分配:遷移完成后,槽歸屬信息通過Gossip協議同步到整個集群。
-
副本擴展:新增副本節點通過
CLUSTER REPLICATE
同步主節點數據。
-
- Redis內存淘汰策略有哪些?如何設計大Key熱Key的解決方案?
-
內存淘汰策略(通過
maxmemory-policy
配置) -
noeviction:拒絕寫入新數據(默認)。
-
volatile-ttl:淘汰過期時間最近的鍵。
-
LRU/LFU策略:
-
allkeys-lru
/volatile-lru
:基于最近最少使用。 -
allkeys-lfu
/volatile-lfu
:基于訪問頻率(4.0+)。
-
-
隨機淘汰:
allkeys-random
/volatile-random
。 -
大Key解決方案
-
拆分:如將大Hash拆分為多個小Hash,通過鍵名后綴分片。
-
異步刪除:使用
UNLINK
代替DEL
,非阻塞釋放內存。 -
壓縮:對Value進行壓縮(如GZIP),但會增加CPU開銷。
-
監控:通過
redis-cli --bigkeys
定期掃描大Key。 -
熱Key解決方案
-
多級緩存:本地緩存(如Guava)減少Redis訪問。
-
讀寫分離:通過副本節點分散讀壓力。
-
分片打散:使用Hash Tag(如
{user1000}.profile
)強制熱Key分布到同一節點,并增加副本。 -
Proxy支持:如Twemproxy或Codis自動分散請求。
-
- Redis多線程模型演進(從單線程到IO多線程)?管道技術原理?
-
多線程演進
-
單線程模型(6.0前):單線程處理所有網絡I/O、命令解析和執行,避免鎖競爭,但無法利用多核CPU。
-
I/O多線程(6.0+):
-
多線程網絡I/O:主線程負責接收連接,I/O線程處理讀寫(配置
io-threads
啟用)。 -
單線程命令執行:命令執行仍為單線程,保證原子性。
-
-
性能提升:高并發場景下,網絡I/O瓶頸緩解,吞吐量顯著提升。
-
管道技術(Pipeline)原理
-
批量發送:客戶端將多個命令打包發送,減少網絡往返次數(RTT)。
-
服務端順序執行:服務器按順序依次執行命令,全部完成后一次性返回結果。
-
優勢:適用于批量操作(如批量寫入),提升吞吐量。
-
注意點:不保證原子性,且返回結果需客戶端按序解析。
-