SpringBoot 集成 Redis實現(xiàn)緩存與分布式鎖的全過程(提升系統(tǒng)性能與并發(fā)能力)
在高并發(fā)、高流量后端系統(tǒng)中,數(shù)據(jù)庫往往是性能瓶頸 —— 頻繁的數(shù)據(jù)庫查詢會導致響應(yīng)延遲、數(shù)據(jù)庫壓力過大。Redis 作為高性能鍵值對緩存數(shù)據(jù)庫,支持內(nèi)存存儲、持久化、分布式部署,可通過緩存熱點數(shù)據(jù)、實現(xiàn)分布式鎖等方式,大幅提升系統(tǒng)響應(yīng)速度、降低數(shù)據(jù)庫壓力,同時解決分布式環(huán)境下的并發(fā)安全問題,是企業(yè)級系統(tǒng)性能優(yōu)化的核心工具。
本文聚焦 SpringBoot 與 Redis 的實戰(zhàn)落地,從環(huán)境搭建、緩存注解使用、手動緩存操作,到分布式鎖實現(xiàn)、緩存問題解決方案,全程嵌入 Java 代碼教學,幫你快速掌握 Redis 核心應(yīng)用,打造高性能、高并發(fā)的后端系統(tǒng)。
一、核心認知:Redis 核心價值與適用場景
1. 核心優(yōu)勢
- 極致性能:基于內(nèi)存操作,讀寫速度可達每秒百萬級,響應(yīng)延遲毫秒級,遠超傳統(tǒng)數(shù)據(jù)庫;
- 數(shù)據(jù)結(jié)構(gòu)豐富:支持 String、Hash、List、Set、Sorted Set 等多種數(shù)據(jù)結(jié)構(gòu),適配復雜業(yè)務(wù)場景;
- 分布式兼容:支持主從復制、哨兵模式、集群部署,適配分布式系統(tǒng)架構(gòu);
- 功能強大:可實現(xiàn)緩存、分布式鎖、消息隊列、計數(shù)器、限流等多種功能;
- 持久化機制:支持 RDB、AOF 兩種持久化方式,確保數(shù)據(jù)不丟失(兼顧性能與可靠性)。
2. 核心適用場景
- 熱點數(shù)據(jù)緩存:用戶信息、商品詳情、首頁數(shù)據(jù)等高頻查詢數(shù)據(jù),減輕數(shù)據(jù)庫壓力;
- 分布式鎖:解決分布式系統(tǒng)中并發(fā)修改、資源競爭問題(如庫存扣減、訂單創(chuàng)建);
- 會話存儲:分布式環(huán)境下的用戶會話共享(替代 Session 本地存儲);
- 異步通信:基于 List 實現(xiàn)簡單消息隊列,處理異步任務(wù)(如日志收集、通知推送);
- 計數(shù)器與限流:接口訪問次數(shù)統(tǒng)計、接口限流(防止惡意請求)。
3. Redis 核心概念
- 鍵(Key):唯一標識,支持字符串、哈希值等格式,建議設(shè)計統(tǒng)一命名規(guī)范(如
user:info:1001); - 值(Value):支持多種數(shù)據(jù)結(jié)構(gòu),存儲業(yè)務(wù)數(shù)據(jù),需合理設(shè)置過期時間避免內(nèi)存溢出;
- 過期策略:支持定期刪除、惰性刪除,可設(shè)置鍵的過期時間,自動釋放內(nèi)存;
- 分布式鎖:基于
SETNX(SET if Not Exists)命令,確保同一時間只有一個線程操作資源。
二、核心實戰(zhàn)一:環(huán)境搭建(Docker 快速部署)
1. Docker 部署 Redis(單節(jié)點,開發(fā)測試場景)
# 1. 拉取 Redis 鏡像(最新穩(wěn)定版) docker pull redis:latest # 2. 啟動 Redis 容器(配置密碼、持久化、端口映射) docker run -d --name redis -p 6379:6379 \ -v redis-data:/data \ # 掛載數(shù)據(jù)卷,持久化數(shù)據(jù) -e REDIS_PASSWORD=redis123 \ # 設(shè)置訪問密碼 redis:latest \ redis-server --requirepass redis123 \ # 開啟密碼驗證 --appendonly yes # 開啟 AOF 持久化(數(shù)據(jù)更可靠)
- 連接測試:使用 Redis 客戶端(如 Redis Desktop Manager)連接
localhost:6379,密碼redis123,驗證服務(wù)可用性; - 核心配置說明:AOF 持久化會記錄所有寫操作,重啟后可恢復數(shù)據(jù),適合對數(shù)據(jù)可靠性要求高的場景。
三、核心實戰(zhàn)二:SpringBoot 集成 Redis 基礎(chǔ)配置
1. 引入依賴(Maven)
<!-- Spring Data Redis 依賴(簡化 Redis 操作) -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<!-- 連接池依賴(提升 Redis 連接效率) -->
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-pool2</artifactId>
</dependency>
<!-- Web 依賴 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- Lombok -->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>2. 配置文件(application.yml)
# Redis 配置
spring:
redis:
host: localhost # Redis 服務(wù)地址
port: 6379 # 端口
password: redis123 # 訪問密碼
database: 0 # 數(shù)據(jù)庫索引(默認 0,Redis 支持 16 個數(shù)據(jù)庫)
timeout: 10000 # 連接超時時間(毫秒)
lettuce: # 連接池配置(Lettuce 是 SpringBoot 2.x 默認 Redis 客戶端)
pool:
max-active: 16 # 最大連接數(shù)
max-idle: 8 # 最大空閑連接數(shù)
min-idle: 4 # 最小空閑連接數(shù)
max-wait: -1 # 最大等待時間(-1 表示無限制)
# 服務(wù)端口
server:
port: 80853. Redis 配置類(自定義序列化方式)
默認序列化方式會導致 Redis 中存儲的數(shù)據(jù)可讀性差,需自定義序列化(JSON 格式),同時配置緩存管理器。
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.cache.RedisCacheConfiguration;
import org.springframework.data.redis.cache.RedisCacheManager;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.GenericJackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.RedisSerializationContext;
import org.springframework.data.redis.serializer.StringRedisSerializer;
import java.time.Duration;
@Configuration
public class RedisConfig {
// 自定義 RedisTemplate(JSON 序列化,支持復雜對象存儲)
@Bean
public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory connectionFactory) {
RedisTemplate<String, Object> redisTemplate = new RedisTemplate<>();
redisTemplate.setConnectionFactory(connectionFactory);
// 字符串序列化器(Key 用字符串序列化)
StringRedisSerializer stringSerializer = new StringRedisSerializer();
// JSON 序列化器(Value 用 JSON 序列化,保留對象結(jié)構(gòu))
GenericJackson2JsonRedisSerializer jsonSerializer = new GenericJackson2JsonRedisSerializer();
// 配置 Key、Value 序列化方式
redisTemplate.setKeySerializer(stringSerializer);
redisTemplate.setValueSerializer(jsonSerializer);
redisTemplate.setHashKeySerializer(stringSerializer);
redisTemplate.setHashValueSerializer(jsonSerializer);
// 初始化 RedisTemplate
redisTemplate.afterPropertiesSet();
return redisTemplate;
}
// 配置緩存管理器(支持緩存注解)
@Bean
public RedisCacheManager cacheManager(RedisConnectionFactory connectionFactory) {
// 緩存配置(設(shè)置默認過期時間 30 分鐘,JSON 序列化)
RedisCacheConfiguration cacheConfig = RedisCacheConfiguration.defaultCacheConfig()
.entryTtl(Duration.ofMinutes(30)) // 默認緩存過期時間
.serializeKeysWith(RedisSerializationContext.SerializationPair.fromSerializer(new StringRedisSerializer()))
.serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(new GenericJackson2JsonRedisSerializer()))
.disableCachingNullValues(); // 禁止緩存 null 值(避免緩存穿透)
// 創(chuàng)建緩存管理器
return RedisCacheManager.builder(connectionFactory)
.cacheDefaults(cacheConfig)
.build();
}
}四、核心實戰(zhàn)三:Redis 緩存操作(注解式 + 手動式)
1. 注解式緩存(簡化開發(fā),適用于簡單場景)
通過 @Cacheable、@CachePut、@CacheEvict 等注解,無需手動編寫緩存邏輯,自動實現(xiàn)數(shù)據(jù)緩存與更新。
(1)Service 層使用示例
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.cache.annotation.CachePut;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.stereotype.Service;
import com.example.redis.entity.User;
import com.example.redis.mapper.UserMapper;
import javax.annotation.Resource;
@Service
public class UserService {
@Resource
private UserMapper userMapper;
// ? 緩存查詢結(jié)果:key 為 "user:info:" + id,緩存過期時間 30 分鐘
@Cacheable(value = "user", key = "'info:' + #id", unless = "#result == null")
public User getUserById(Long id) {
// 緩存不存在時,查詢數(shù)據(jù)庫(僅執(zhí)行一次)
System.out.println("查詢數(shù)據(jù)庫,用戶ID:" + id);
return userMapper.selectById(id);
}
// ? 更新緩存:更新數(shù)據(jù)庫后,同步更新緩存(避免緩存與數(shù)據(jù)庫不一致)
@CachePut(value = "user", key = "'info:' + #user.id", unless = "#user == null")
public User updateUser(User user) {
userMapper.updateById(user);
return user;
}
// ? 清除緩存:刪除數(shù)據(jù)庫數(shù)據(jù)后,刪除對應(yīng)緩存
@CacheEvict(value = "user", key = "'info:' + #id")
public void deleteUser(Long id) {
userMapper.deleteById(id);
}
// ? 清除所有緩存(如批量更新時)
@CacheEvict(value = "user", allEntries = true)
public void clearUserCache() {
// 僅清除緩存,無業(yè)務(wù)邏輯
}
}(2)Controller 層接口
import org.springframework.web.bind.annotation.*;
import com.example.redis.entity.User;
import com.example.redis.result.Result;
import com.example.redis.service.UserService;
import javax.annotation.Resource;
@RestController
@RequestMapping("/user")
public class UserController {
@Resource
private UserService userService;
@GetMapping("/{id}")
public Result<User> getUserById(@PathVariable Long id) {
User user = userService.getUserById(id);
return Result.success(user);
}
@PutMapping
public Result<User> updateUser(@RequestBody User user) {
User updatedUser = userService.updateUser(user);
return Result.success(updatedUser);
}
@DeleteMapping("/{id}")
public Result<Void> deleteUser(@PathVariable Long id) {
userService.deleteUser(id);
return Result.success();
}
}2. 手動式緩存(靈活控制,適用于復雜場景)
通過 RedisTemplate 手動操作 Redis,支持多種數(shù)據(jù)結(jié)構(gòu),適配復雜業(yè)務(wù)場景(如 Hash、List 操作)。
(1)Redis 工具類封裝
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;
import javax.annotation.Resource;
import java.util.concurrent.TimeUnit;
@Component
public class RedisUtils {
@Resource
private RedisTemplate<String, Object> redisTemplate;
// ? 存儲 String 類型數(shù)據(jù)(設(shè)置過期時間)
public void setString(String key, Object value, Long expireTime, TimeUnit timeUnit) {
redisTemplate.opsForValue().set(key, value, expireTime, timeUnit);
}
// ? 獲取 String 類型數(shù)據(jù)
public Object getString(String key) {
return redisTemplate.opsForValue().get(key);
}
// ? 刪除數(shù)據(jù)
public Boolean delete(String key) {
return redisTemplate.delete(key);
}
// ? 判斷鍵是否存在
public Boolean hasKey(String key) {
return redisTemplate.hasKey(key);
}
// ? 設(shè)置鍵過期時間
public Boolean expire(String key, Long expireTime, TimeUnit timeUnit) {
return redisTemplate.expire(key, expireTime, timeUnit);
}
// ? 存儲 Hash 類型數(shù)據(jù)(示例:存儲用戶詳情,字段拆分)
public void setHash(String key, String hashKey, Object value) {
redisTemplate.opsForHash().put(key, hashKey, value);
}
// ? 獲取 Hash 類型數(shù)據(jù)
public Object getHash(String key, String hashKey) {
return redisTemplate.opsForHash().get(key, hashKey);
}
}(2)工具類使用示例(熱點商品緩存)
import org.springframework.stereotype.Service;
import com.example.redis.entity.Goods;
import com.example.redis.mapper.GoodsMapper;
import com.example.redis.utils.RedisUtils;
import javax.annotation.Resource;
import java.util.concurrent.TimeUnit;
@Service
public class GoodsService {
@Resource
private GoodsMapper goodsMapper;
@Resource
private RedisUtils redisUtils;
// 熱點商品緩存(手動控制緩存邏輯)
public Goods getHotGoodsById(Long id) {
String key = "goods:hot:" + id;
// 1. 先查緩存
Goods goods = (Goods) redisUtils.getString(key);
if (goods != null) {
return goods;
}
// 2. 緩存不存在,查數(shù)據(jù)庫
goods = goodsMapper.selectById(id);
if (goods != null) {
// 3. 存入緩存,設(shè)置過期時間 10 分鐘(熱點數(shù)據(jù)可縮短過期時間)
redisUtils.setString(key, goods, 10L, TimeUnit.MINUTES);
}
return goods;
}
}五、核心實戰(zhàn)四:分布式鎖實現(xiàn)(解決并發(fā)安全問題)
分布式環(huán)境下,多個服務(wù)實例同時操作同一資源(如庫存扣減)會導致數(shù)據(jù)不一致,通過 Redis 分布式鎖可確保同一時間只有一個線程操作資源。
1. 分布式鎖工具類
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;
import javax.annotation.Resource;
import java.util.concurrent.TimeUnit;
@Component
public class RedisDistributedLock {
@Resource
private RedisTemplate<String, Object> redisTemplate;
// 鎖前綴(避免鍵沖突)
private static final String LOCK_PREFIX = "lock:";
// 鎖過期時間(避免死鎖,默認 30 秒)
private static final Long LOCK_EXPIRE = 30L;
/**
* 獲取分布式鎖
* @param lockKey 鎖標識(如 "stock:1001")
* @param requestId 唯一標識(如 UUID,確保釋放鎖時是自己的鎖)
* @return 是否獲取成功
*/
public Boolean tryLock(String lockKey, String requestId) {
String key = LOCK_PREFIX + lockKey;
// SETNX 命令:當鍵不存在時設(shè)置值,原子操作(確保并發(fā)安全)
Boolean locked = redisTemplate.opsForValue().setIfAbsent(key, requestId, LOCK_EXPIRE, TimeUnit.SECONDS);
return locked != null && locked;
}
/**
* 釋放分布式鎖
* @param lockKey 鎖標識
* @param requestId 唯一標識
*/
public void unlock(String lockKey, String requestId) {
String key = LOCK_PREFIX + lockKey;
try {
String value = (String) redisTemplate.opsForValue().get(key);
// 僅釋放自己的鎖(避免釋放其他線程的鎖)
if (requestId.equals(value)) {
redisTemplate.delete(key);
}
} catch (Exception e) {
e.printStackTrace();
}
}
}2. 分布式鎖使用示例(庫存扣減)
import org.springframework.stereotype.Service;
import com.example.redis.mapper.StockMapper;
import com.example.redis.utils.RedisDistributedLock;
import javax.annotation.Resource;
import java.util.UUID;
@Service
public class StockService {
@Resource
private StockMapper stockMapper;
@Resource
private RedisDistributedLock distributedLock;
// 庫存扣減(分布式環(huán)境下安全操作)
public Boolean deductStock(Long goodsId, Integer num) {
String lockKey = "stock:" + goodsId;
String requestId = UUID.randomUUID().toString();
try {
// 1. 獲取分布式鎖(最多等待 5 秒,每隔 1 秒重試)
Boolean locked = false;
int count = 0;
while (!locked && count < 5) {
locked = distributedLock.tryLock(lockKey, requestId);
if (!locked) {
Thread.sleep(1000);
count++;
}
}
if (!locked) {
// 獲取鎖失?。ㄏ蘖鳎?
return false;
}
// 2. 扣減庫存(業(yè)務(wù)邏輯)
int row = stockMapper.deductStock(goodsId, num);
return row > 0;
} catch (Exception e) {
e.printStackTrace();
return false;
} finally {
// 3. 釋放鎖(確保無論是否異常,都釋放鎖)
distributedLock.unlock(lockKey, requestId);
}
}
}六、進階:緩存常見問題解決方案
1. 緩存穿透(查詢不存在的數(shù)據(jù),緩存無效,直擊數(shù)據(jù)庫)
- 問題表現(xiàn):惡意請求不存在的 ID,緩存無法命中,頻繁查詢數(shù)據(jù)庫,導致數(shù)據(jù)庫壓力過大;
- 解決方案:緩存 null 值(設(shè)置較短過期時間),或使用布隆過濾器過濾不存在的 ID。
// 緩存 null 值解決方案(修改查詢方法)
public Goods getGoodsById(Long id) {
String key = "goods:info:" + id;
Goods goods = (Goods) redisUtils.getString(key);
if (goods != null) {
return goods;
}
// 數(shù)據(jù)庫查詢也不存在,緩存 null 值(過期時間 5 分鐘)
goods = goodsMapper.selectById(id);
if (goods == null) {
redisUtils.setString(key, null, 5L, TimeUnit.MINUTES);
return null;
}
redisUtils.setString(key, goods, 30L, TimeUnit.MINUTES);
return goods;
}2. 緩存擊穿(熱點數(shù)據(jù)緩存過期,大量請求同時直擊數(shù)據(jù)庫)
- 問題表現(xiàn):熱點數(shù)據(jù)緩存過期瞬間,大量并發(fā)請求同時查詢數(shù)據(jù)庫,導致數(shù)據(jù)庫壓力驟增;
- 解決方案:互斥鎖(同一時間只有一個線程查詢數(shù)據(jù)庫并更新緩存),或熱點數(shù)據(jù)永不過期。
3. 緩存雪崩(大量緩存同時過期,或 Redis 服務(wù)宕機,所有請求直擊數(shù)據(jù)庫)
- 問題表現(xiàn):緩存服務(wù)器宕機,或大量緩存設(shè)置同一過期時間,導致所有請求直接訪問數(shù)據(jù)庫,數(shù)據(jù)庫崩潰;
- 解決方案:Redis 集群部署(高可用),緩存過期時間加隨機值(避免同時過期),服務(wù)降級(熔斷保護數(shù)據(jù)庫)。
七、避坑指南
坑點 1:緩存與數(shù)據(jù)庫數(shù)據(jù)不一致
表現(xiàn):更新數(shù)據(jù)庫后,緩存未同步更新,導致查詢到舊數(shù)據(jù);? 解決方案:采用「更新數(shù)據(jù)庫 + 同步更新緩存」或「更新數(shù)據(jù)庫 + 刪除緩存」策略,避免單獨操作數(shù)據(jù)庫或緩存。
坑點 2:Redis 序列化后數(shù)據(jù)可讀性差
表現(xiàn):Redis 中存儲的數(shù)據(jù)為亂碼,無法直接查看;? 解決方案:自定義序列化方式(如 JSON 序列化),避免使用默認的 JdkSerializationRedisSerializer。
坑點 3:分布式鎖死鎖
表現(xiàn):獲取鎖后線程異常退出,未釋放鎖,導致其他線程無法獲取鎖;? 解決方案:設(shè)置鎖過期時間,確保鎖自動釋放;使用 try-finally 確保鎖一定會被釋放。
坑點 4:Redis 連接池耗盡
表現(xiàn):系統(tǒng)報錯「Could not get a resource from the pool」,無法獲取 Redis 連接;? 解決方案:合理配置連接池參數(shù)(max-active、max-idle),避免頻繁創(chuàng)建銷毀連接,排查是否有連接未釋放的情況。
八、終極總結(jié):Redis 實戰(zhàn)的核心是「緩存高效 + 并發(fā)安全」
Redis 實戰(zhàn)的核心價值,是通過緩存熱點數(shù)據(jù)提升系統(tǒng)響應(yīng)速度,通過分布式鎖解決分布式并發(fā)問題,同時兼顧數(shù)據(jù)可靠性與系統(tǒng)穩(wěn)定性。企業(yè)級開發(fā)中,需根據(jù)業(yè)務(wù)場景選擇合適的緩存策略與數(shù)據(jù)結(jié)構(gòu),規(guī)避常見緩存問題,平衡性能與一致性。
核心原則總結(jié):
- 緩存策略適配業(yè)務(wù):簡單場景用注解式緩存,復雜場景用手動式緩存,熱點數(shù)據(jù)縮短過期時間;
- 并發(fā)安全優(yōu)先:分布式環(huán)境下操作共享資源,必須使用分布式鎖,避免數(shù)據(jù)不一致;
- 高可用不可少:生產(chǎn)環(huán)境必用 Redis 集群(主從 + 哨兵),避免單點故障導致緩存雪崩;
- 問題提前預防:針對緩存穿透、擊穿、雪崩,提前部署解決方案,而非事后補救。
到此這篇關(guān)于SpringBoot 集成 Redis實現(xiàn)緩存與分布式鎖的全過程(提升系統(tǒng)性能與并發(fā)能力)的文章就介紹到這了,更多相關(guān)SpringBoot集成Redis 內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
SpringBoot框架中Mybatis-plus的簡單使用操作匯總
這篇文章主要介紹了SpringBoot框架中Mybatis-plus的簡單使用,本文通過示例代碼給大家介紹的非常詳細,對大家的學習或工作具有一定的參考借鑒價值,需要的朋友可以參考下2022-02-02
springboot集成spring cache緩存示例代碼
本篇文章主要介紹了springboot集成spring cache示例代碼,小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧2017-05-05
java中ArrayList和LinkedList的區(qū)別詳解
這篇文章主要介紹了java中ArrayList和LinkedList的區(qū)別詳解,幫助大家更好的理解和使用Java,感興趣的朋友可以了解下2021-01-01
Java利用ElasticSearch實現(xiàn)增刪改功能
這篇文章主要為大家詳細介紹了Java如何利用ElasticSearch實現(xiàn)增刪改功能,文中的示例代碼講解詳細,感興趣的小伙伴可以跟隨小編一起學習一下2023-08-08
使用Spring?Boot+gRPC構(gòu)建微服務(wù)并部署的案例詳解
這篇文章主要介紹了使用Spring?Boot+gRPC構(gòu)建微服務(wù)并部署,Spring Cloud僅僅是一個開發(fā)框架,沒有實現(xiàn)微服務(wù)所必須的服務(wù)調(diào)度、資源分配等功能,這些需求要借助Kubernetes等平臺來完成,本文給大家介紹的非常詳細,需要的朋友參考下吧2022-06-06

