
1. 引言在當今互聯網高并發場景下數據庫的性能瓶頸往往成為系統擴展的最大障礙。Redis 作為一款高性能的內存數據庫憑借其豐富的數據結構、極快的讀寫速度和靈活的持久化機制已經成為 Java 后端開發中不可或缺的組件。本文將系統性地介紹 Redis 的核心知識體系包括五種基本數據結構、持久化機制、集群部署模式以及面試中高頻出現的緩存穿透、緩存雪崩和緩存擊穿三大經典問題。無論你是準備面試還是實際項目開發這篇文章都能為你提供完整的知識框架。2. Redis 概述2.1 什么是 RedisRedisRemote Dictionary Server是一個開源的、基于內存的鍵值對存儲系統由 Salvatore Sanfilippo 于 2009 年開發。它支持多種數據結構如字符串、哈希、列表、集合和有序集合并提供了豐富的操作命令。2.2 Redis 的核心特性高性能基于內存操作單線程模型避免了并發競爭讀寫速度可達 10 萬次/秒以上豐富的數據結構支持 String、Hash、List、Set、ZSet 五種基本類型及 Bitmap、HyperLogLog、Geo 等高級類型持久化機制支持 RDB 快照和 AOF 日志兩種持久化方式高可用支持主從復制、哨兵模式和集群模式原子性操作所有命令都是原子操作支持事務和 Lua 腳本2.3 Redis 的應用場景緩存系統減輕數據庫壓力提升系統響應速度分布式鎖基于 SETNX 命令實現分布式鎖排行榜利用 ZSet 實現實時排行榜計數器利用 INCR/DECR 實現訪問計數、庫存扣減消息隊列利用 List 的 LPUSH/BRPOP 實現簡單的消息隊列會話管理存儲用戶登錄狀態和 Session 信息3. Redis 數據結構詳解3.1 String字符串String 是 Redis 最基本的數據類型可以存儲字符串、整數或浮點數最大容量為 512MB。常用命令SET key value# 設置鍵值GET key# 獲取值INCR key# 值加1DECR key# 值減1SETEX key seconds value# 設置帶過期時間的鍵值MSET key1 value1 key2 value2# 批量設置Java 使用示例// 使用 Spring Data RedisAutowiredprivateStringRedisTemplatestringRedisTemplate;// 設置緩存stringRedisTemplate.opsForValue().set(user:1001,張三,30,TimeUnit.MINUTES);// 獲取緩存StringuserstringRedisTemplate.opsForValue().get(user:1001);// 計數器操作LongcountstringRedisTemplate.opsForValue().increment(page:view:count);應用場景緩存對象、計數器、分布式鎖、Session 管理。3.2 Hash哈希Hash 是一個鍵值對集合適合存儲對象類型的數據可以單獨修改某個字段而不影響其他字段。常用命令HSET key field value# 設置字段值HGET key field# 獲取字段值HGETALL key# 獲取所有字段和值HDEL key field# 刪除字段HINCRBY key field increment# 字段值增加Java 使用示例AutowiredprivateRedisTemplateString,ObjectredisTemplate;// 存儲用戶對象HashOperationsString,String,ObjecthashOpsredisTemplate.opsForHash();MapString,ObjectuserMapnewHashMap();userMap.put(name,李四);userMap.put(age,25);userMap.put(email,lisiexample.com);hashOps.putAll(user:1002,userMap);// 獲取單個字段ObjectnamehashOps.get(user:1002,name);// 獲取整個對象MapString,ObjectuserhashOps.entries(user:1002);應用場景存儲對象信息、購物車、用戶資料。3.3 List列表List 是一個雙向鏈表支持從兩端插入和彈出元素適合實現隊列和棧。常用命令LPUSH key value# 從左側插入RPUSH key value# 從右側插入LPOP key# 從左側彈出RPOP key# 從右側彈出LRANGE key start stop# 獲取范圍內的元素LLEN key# 獲取列表長度Java 使用示例// 實現消息隊列ListOperationsString,StringlistOpsredisTemplate.opsForList();// 生產者發送消息listOps.rightPush(message:queue,消息1);listOps.rightPush(message:queue,消息2);// 消費者接收消息阻塞式StringmessagelistOps.leftPop(message:queue,10,TimeUnit.SECONDS);應用場景消息隊列、最新消息列表、時間線。3.4 Set集合Set 是無序的字符串集合元素唯一支持集合運算交集、并集、差集。常用命令SADD key member# 添加元素SREM key member# 刪除元素SMEMBERS key# 獲取所有元素SISMEMBER key member# 判斷元素是否存在SINTER key1 key2# 求交集SUNION key1 key2# 求并集Java 使用示例SetOperationsString,StringsetOpsredisTemplate.opsForSet();// 添加標簽setOps.add(user:1001:tags,Java,Redis,Spring);// 判斷是否包含某元素BooleanhasTagsetOps.isMember(user:1001:tags,Java);// 求共同關注SetStringcommonsetOps.intersect(user:1001:follows,user:1002:follows);應用場景標簽系統、好友關系、去重操作。3.5 ZSet有序集合ZSet 在 Set 的基礎上增加了分數score屬性元素按分數排序適合實現排行榜。常用命令ZADD key score member# 添加元素及分數ZRANGE key start stop# 按分數升序獲取ZREVRANGE key start stop# 按分數降序獲取ZSCORE key member# 獲取元素分數ZINCRBY key increment member# 增加元素分數ZRANK key member# 獲取元素排名Java 使用示例ZSetOperationsString,StringzSetOpsredisTemplate.opsForZSet();// 添加排行榜數據zSetOps.add(ranking:game,玩家A,100);zSetOps.add(ranking:game,玩家B,85);zSetOps.add(ranking:game,玩家C,95);// 獲取前三名SetStringtop3zSetOps.reverseRange(ranking:game,0,2);// 增加分數zSetOps.incrementScore(ranking:game,玩家B,20);應用場景排行榜、延時隊列、優先級隊列。4. Redis 持久化機制4.1 RDBRedis DataBase持久化RDB 是 Redis 默認的持久化方式通過創建快照Snapshot將內存中的數據保存到磁盤。工作原理在指定的時間間隔內將內存中的數據集快照寫入磁盤默認文件名為dump.rdb通過 fork 子進程完成快照生成不影響主進程服務觸發條件# 配置文件 redis.conf 中的默認配置save9001# 900秒內至少1次修改save30010# 300秒內至少10次修改save6010000# 60秒內至少10000次修改優點文件緊湊適合備份和災難恢復恢復速度快對性能影響小缺點可能丟失最后一次快照后的數據fork 子進程時可能短暫阻塞4.2 AOFAppend Only File持久化AOF 通過記錄每次寫操作命令來持久化數據以追加的方式寫入日志文件。工作原理將每次寫命令追加到appendonly.aof文件支持三種同步策略always、everysec、no支持 AOF 重寫機制壓縮文件大小配置方式# 開啟 AOFappendonlyyes# 同步策略appendfsync always# 每次寫入都同步最安全但性能最差appendfsync everysec# 每秒同步一次推薦appendfsync no# 由操作系統決定同步時機優點數據安全性高最多丟失 1 秒數據日志文件可讀便于排查問題缺點文件體積較大恢復速度較慢相同數據集下 AOF 文件比 RDB 大4.3 混合持久化Redis 4.0 引入了混合持久化機制結合 RDB 和 AOF 的優點使用 RDB 作為基礎快照在快照之間使用 AOF 記錄增量操作兼顧恢復速度和數據安全性# 開啟混合持久化aof-use-rdb-preambleyes5. Redis 集群模式5.1 主從復制模式主從復制是最基礎的集群模式通過將主節點的數據同步到從節點實現讀寫分離和數據備份。架構特點一個主節點Master負責寫操作多個從節點Slave負責讀操作從節點自動同步主節點數據配置方式# 在從節點配置文件中指定主節點replicaof192.168.1.1006379# 或使用命令動態設置SLAVEOF192.168.1.1006379復制原理從節點發送 SYNC 命令主節點執行 BGSAVE 生成 RDB 快照主節點將快照發送給從節點從節點加載快照主節點將增量命令發送給從節點優點實現讀寫分離提升讀性能數據冗余備份配置簡單缺點主節點故障時無法自動切換從節點過多時同步壓力大5.2 哨兵模式Sentinel哨兵模式在主從復制基礎上增加了自動故障轉移能力實現高可用。架構特點哨兵進程監控主從節點的運行狀態主節點故障時自動選舉新的主節點客戶端通過哨兵獲取當前主節點地址哨兵配置# sentinel.confsentinel monitor mymaster192.168.1.10063792sentinel down-after-milliseconds mymaster5000sentinel failover-timeout mymaster15000故障轉移流程主節點故障哨兵檢測到主節點下線投票選舉新主節點從節點升級為主節點其他從節點重新同步客戶端連接新主節點優點自動故障轉移高可用無需人工干預支持監控和通知缺點只有一個主節點寫性能受限配置相對復雜5.3 Cluster 集群模式Cluster 模式是 Redis 3.0 引入的分布式解決方案支持數據分片和水平擴展。架構特點數據自動分片到 16384 個哈希槽每個節點負責一部分哈希槽支持多主多從無中心化架構支持在線擴容和縮容哈希槽分配# 集群節點分配哈希槽redis-cli-p7001cluster addslots0-5460 redis-cli-p7002cluster addslots5461-10922 redis-cli-p7003cluster addslots10923-16383集群配置示例# 創建集群至少3個主節點redis-cli--clustercreate192.168.1.101:7001192.168.1.102:7002\192.168.1.103:7003 --cluster-replicas1數據分片原理客戶端請求計算哈希槽CRC16(key) % 16384定位節點執行操作優點支持水平擴展突破單機內存限制高可用支持自動故障轉移無中心化避免單點瓶頸缺點不支持多鍵操作跨槽位客戶端需要支持集群協議數據遷移期間可能影響性能6. 緩存穿透、緩存雪崩與緩存擊穿6.1 緩存穿透問題描述查詢一個不存在的數據緩存和數據庫中都沒有導致每次請求都直接打到數據庫。產生原因惡意攻擊構造大量不存在的 key業務邏輯缺陷查詢了不存在的 ID解決方案方案一緩存空值publicObjectgetData(Stringkey){// 先從緩存查詢ObjectvalueredisTemplate.opsForValue().get(key);if(value!null){returnvalue;}// 緩存未命中查詢數據庫ObjectdbValuequeryFromDatabase(key);if(dbValuenull){// 緩存空值設置較短的過期時間redisTemplate.opsForValue().set(key,,60,TimeUnit.SECONDS);returnnull;}// 緩存真實數據redisTemplate.opsForValue().set(key,dbValue,30,TimeUnit.MINUTES);returndbValue;}方案二布隆過濾器// 使用 Guava 布隆過濾器BloomFilterStringbloomFilterBloomFilter.create(Funnels.stringFunnel(Charset.defaultCharset()),1000000,// 預計元素數量0.01// 誤判率);// 初始化時加載所有存在的 keybloomFilter.put(user:1001);bloomFilter.put(user:1002);publicObjectgetData(Stringkey){// 布隆過濾器判斷if(!bloomFilter.mightContain(key)){returnnull;// 一定不存在直接返回}// 可能存在繼續查詢緩存和數據庫// ...}6.2 緩存雪崩問題描述大量緩存同時過期或 Redis 服務宕機導致所有請求直接打到數據庫造成數據庫壓力驟增甚至崩潰。產生原因緩存設置了相同的過期時間Redis 服務宕機緩存服務器重啟解決方案方案一過期時間加隨機值// 設置過期時間時加入隨機值避免同時過期intbaseExpire30*60;// 基礎過期時間30分鐘intrandomExpirenewRandom().nextInt(300);// 隨機0-300秒redisTemplate.opsForValue().set(key,value,baseExpirerandomExpire,TimeUnit.SECONDS);方案二多級緩存// 一級緩存本地緩存CaffeineCacheString,ObjectlocalCacheCaffeine.newBuilder().expireAfterWrite(5,TimeUnit.MINUTES).maximumSize(10000).build();// 二級緩存RedispublicObjectgetData(Stringkey){// 先查本地緩存ObjectlocalValuelocalCache.getIfPresent(key);if(localValue!null){returnlocalValue;}// 再查 RedisObjectredisValueredisTemplate.opsForValue().get(key);if(redisValue!null){localCache.put(key,redisValue);returnredisValue;}// 最后查數據庫ObjectdbValuequeryFromDatabase(key);if(dbValue!null){redisTemplate.opsForValue().set(key,dbValue,30,TimeUnit.MINUTES);localCache.put(key,dbValue);}returndbValue;}方案三Redis 高可用使用哨兵模式或集群模式配置主從復制實現故障自動轉移使用持久化機制重啟后快速恢復6.3 緩存擊穿問題描述某個熱點 key 在緩存過期的瞬間大量并發請求同時訪問該 key導致所有請求都打到數據庫。產生原因熱點數據過期高并發訪問同一數據解決方案方案一互斥鎖publicObjectgetData(Stringkey){// 先從緩存查詢ObjectvalueredisTemplate.opsForValue().get(key);if(value!null){returnvalue;}