在Java中整合Redis的詳細步驟
一、Redis 基礎認知:為什么選擇 Redis?
1.1 Redis 的核心特性
高性能
- 內存存儲機制:Redis 將數據完全存儲在內存中,數據讀寫操作直接在內存中進行,避免了磁盤I/O的瓶頸。內存讀寫速度通常是磁盤的100倍以上(內存訪問約100ns,SSD約100μs)。
- 單線程模型:采用單線程處理命令請求,避免了多線程上下文切換和鎖競爭的開銷,同時通過I/O多路復用技術(如epoll)實現高并發(fā)處理。
- 性能基準:在標準服務器配置(如8核CPU,16GB內存)下,Redis單節(jié)點可達到:
- 讀操作(QPS):10萬-15萬次/秒
- 寫操作(QPS):8萬-12萬次/秒
- 特別優(yōu)化的場景下甚至可達20萬+ QPS
豐富的數據結構
- String:最基本類型,最大512MB,常用于緩存簡單數據或計數器
- Hash:鍵值對集合,適合存儲對象(如用戶信息:user:1001 {name:"張三",age:28})
- List:雙向鏈表,可實現消息隊列(LPUSH+BRPOP)或最新N條記錄
- Set:無序集合,支持交并差運算,適合標簽系統(tǒng)
- Sorted Set:帶權重的有序集合,可用于排行榜(ZADD game:score 100 "player1")
- Bitmap:位圖操作,適合簽到統(tǒng)計(SETBIT sign:202301 100 1)
- HyperLogLog:基數統(tǒng)計,誤差率0.81%,適合UV統(tǒng)計(PFADD uv:20230101 "user1")
持久化機制
- RDB(快照):
- 定時將內存數據生成二進制快照文件(dump.rdb)
- 配置示例:save 900 1(900秒內至少1次修改觸發(fā)保存)
- 優(yōu)點:文件緊湊,恢復速度快
- 缺點:可能丟失最后一次快照后的數據
- AOF(Append Only File):
- 記錄所有寫操作命令(類似MySQL的binlog)
- 提供三種同步策略:
- always:每次寫操作都同步
- everysec:每秒同步(默認)
- no:由操作系統(tǒng)決定
- 優(yōu)點:數據安全性高,最多丟失1秒數據
- 缺點:文件較大,恢復速度較慢
- 混合模式:Redis 4.0+支持RDB+AOF混合持久化,結合兩者優(yōu)勢
高可用與分布式
- 主從復制:
- 一個主節(jié)點(Master)可配置多個從節(jié)點(Slave)
- 從節(jié)點異步復制主節(jié)點數據
- 讀寫分離:寫操作走主節(jié)點,讀操作可分散到從節(jié)點
- 哨兵模式(Sentinel):
- 由2個以上哨兵節(jié)點監(jiān)控Redis集群狀態(tài)
- 自動故障轉移:主節(jié)點宕機時,自動將從節(jié)點提升為主節(jié)點
- 提供配置中心和通知機制
- Redis Cluster:
- 官方分布式解決方案(Redis 3.0+)
- 數據分片存儲:16384個哈希槽分配到不同節(jié)點
- 節(jié)點間使用Gossip協(xié)議通信
- 支持自動故障轉移和請求重定向
多語言支持
- Java:Jedis、Lettuce、Redisson
- Python:redis-py
- Go:go-redis
- Node.js:ioredis
- 所有客戶端庫均遵循Redis協(xié)議規(guī)范,支持連接池、管道等高級特性
1.2 Redis 在 Java 項目中的典型應用場景
緩存熱點數據
- 實現方式:
// Spring Cache示例
@Cacheable(value="products", key="#productId")
public Product getProduct(String productId) {
return productDao.findById(productId);
}
- 緩存策略:
- 緩存穿透:布隆過濾器或空值緩存
- 緩存雪崩:隨機過期時間
- 緩存擊穿:互斥鎖更新
分布式鎖
- 原生實現:
// SETNX實現
String lockKey = "order_lock_"+orderId;
String clientId = UUID.randomUUID().toString();
try {
Boolean result = redisTemplate.opsForValue().setIfAbsent(lockKey, clientId, 30, TimeUnit.SECONDS);
if(result) {
// 執(zhí)行業(yè)務邏輯
} else {
// 獲取鎖失敗
}
} finally {
// 使用Lua腳本保證原子性
String script = "if redis.call('get', KEYS[1]) == ARGV[1] then return redis.call('del', KEYS[1]) else return 0 end";
redisTemplate.execute(new DefaultRedisScript<>(script, Long.class),
Collections.singletonList(lockKey), clientId);
}
Redisson實現:
RLock lock = redissonClient.getLock("order_lock_"+orderId);
try {
// 嘗試加鎖,最多等待100秒,上鎖后30秒自動解鎖
if(lock.tryLock(100, 30, TimeUnit.SECONDS)) {
// 執(zhí)行業(yè)務邏輯
}
} finally {
lock.unlock();
}
計數器與限流
計數器示例:
// 文章閱讀量計數
redisTemplate.opsForValue().increment("article:read:count:"+articleId);
// 獲取閱讀量
Long count = redisTemplate.opsForValue().increment("article:read:count:"+articleId, 0);限流實現(滑動窗口算法):
public boolean isAllowed(String key, int maxCount, int period) {
long now = System.currentTimeMillis();
long windowStart = now - period * 1000;
// 刪除舊時間戳
redisTemplate.opsForZSet().removeRangeByScore(key, 0, windowStart);
// 獲取當前請求數
long count = redisTemplate.opsForZSet().zCard(key);
if(count < maxCount) {
// 添加當前請求
redisTemplate.opsForZSet().add(key, String.valueOf(now), now);
return true;
}
return false;
}消息隊列
List實現簡單隊列:
// 生產者
redisTemplate.opsForList().leftPush("queue:order", orderJson);
// 消費者
String orderJson = redisTemplate.opsForList().rightPop("queue:order", 30, TimeUnit.SECONDS);Redis Stream實現(Redis 5.0+):
// 創(chuàng)建消費者組
redisTemplate.opsForStream().createGroup("order_stream", "order_group");
// 生產者
Map<String, String> message = new HashMap<>();
message.put("orderId", "1001");
message.put("amount", "99.9");
redisTemplate.opsForStream().add("order_stream", message);
// 消費者
List<MapRecord<String, String, String>> records = redisTemplate.opsForStream()
.read(Consumer.from("order_group", "consumer1"),
StreamReadOptions.empty().count(1).block(Duration.ofSeconds(1)),
StreamOffset.create("order_stream", ReadOffset.lastConsumed()));分布式Session
Spring Session集成:
<!-- pom.xml -->
<dependency>
<groupId>org.springframework.session</groupId>
<artifactId>spring-session-data-redis</artifactId>
</dependency># application.properties spring.session.store-type=redis spring.session.timeout=1800 # 30分鐘
// 自動將Session存儲到Redis
@RestController
@SessionAttributes("user")
public class UserController {
@PostMapping("/login")
public String login(@RequestParam String username, Model model) {
model.addAttribute("user", username);
return "login success";
}
}- Session共享效果:
- 用戶登錄后,Session信息存儲在Redis
- 同一用戶的請求可路由到集群中任意節(jié)點
- 各服務通過統(tǒng)一Session ID獲取用戶狀態(tài)
- 默認使用Jackson序列化Session對象
二、環(huán)境搭建:Redis 服務與 Java 開發(fā)環(huán)境準備
2.1 Redis 服務端部署(以 Linux 為例)
步驟 1:下載并解壓 Redis
下載 Redis(版本可根據需求選擇,此處以 6.2.6 為例)
# 下載 Redis 源碼包 wget https://download.redis.io/releases/redis-6.2.6.tar.gz # 驗證下載完整性(可選) wget https://download.redis.io/releases/redis-6.2.6.tar.gz.asc gpg --verify redis-6.2.6.tar.gz.asc # 解壓 Redis 源碼包 tar -zxvf redis-6.2.6.tar.gz # 進入解壓后的目錄 cd redis-6.2.6
注意事項:
- 建議使用穩(wěn)定版本(stable)而非開發(fā)版本
- 下載前可訪問 https://download.redis.io/releases/ 查看最新版本
- 對于生產環(huán)境,建議使用 Redis 6.x 及以上版本以支持多線程處理
步驟 2:編譯與安裝
# 安裝編譯依賴(CentOS/RHEL) yum install -y gcc make tcl # 或 Ubuntu/Debian apt-get install -y build-essential tcl # 編譯 Redis(此過程可能需要幾分鐘) make # 運行測試(可選,但推薦) make test # 安裝 Redis 到系統(tǒng)目錄 make install
安裝結果驗證:
- 默認安裝路徑:/usr/local/bin
- 檢查是否安裝成功:
ls -l /usr/local/bin/redis-*
步驟 3:配置并啟動 Redis
基本配置管理:
# 創(chuàng)建 Redis 配置目錄 mkdir -p /etc/redis # 復制配置文件模板 cp redis.conf /etc/redis/redis.conf # 創(chuàng)建數據存儲目錄 mkdir -p /var/lib/redis
關鍵配置文件修改(/etc/redis/redis.conf):
# 使用 vim 或其他編輯器修改配置文件 vim /etc/redis/redis.conf
需要修改的重要配置項:
網絡配置:
# 允許遠程訪問(注釋掉或修改為) # bind 127.0.0.1 bind 0.0.0.0
安全配置:
# 關閉保護模式 protected-mode no # 設置密碼(生產環(huán)境必須) requirepass your_strong_password
運行模式:
# 以守護進程方式運行 daemonize yes
日志配置:
# 指定日志文件路徑 logfile "/var/log/redis/redis-server.log"
數據持久化(根據需求配置):
# RDB 持久化配置 save 900 1 save 300 10 save 60 10000 # AOF 持久化配置(可選) appendonly yes appendfsync everysec
啟動 Redis 服務:
# 創(chuàng)建日志目錄 mkdir -p /var/log/redis chown -R redis:redis /var/log/redis # 啟動 Redis 服務 redis-server /etc/redis/redis.conf # 驗證服務狀態(tài) ps aux | grep redis netstat -tulnp | grep 6379
連接測試:
# 本地連接測試 redis-cli -h 127.0.0.1 -p 6379 -a your_password ping # 遠程連接測試(從其他服務器) redis-cli -h your_server_ip -p 6379 -a your_password ping
設置開機自啟(可選):
# 創(chuàng)建 systemd 服務文件 vim /etc/systemd/system/redis.service
添加以下內容:
[Unit] Description=Redis In-Memory Data Store After=network.target [Service] User=redis Group=redis ExecStart=/usr/local/bin/redis-server /etc/redis/redis.conf ExecStop=/usr/local/bin/redis-cli shutdown Restart=always [Install] WantedBy=multi-user.target
然后執(zhí)行:
# 重新加載 systemd systemctl daemon-reload # 啟用開機自啟 systemctl enable redis # 啟動服務 systemctl start redis # 檢查狀態(tài) systemctl status redis
2.2 Java 開發(fā)環(huán)境配置
客戶端選擇說明
Java 操作 Redis 主要有兩種主流客戶端:
- Jedis:
- 官方推薦的輕量級客戶端
- 直接操作 Redis 命令
- 支持連接池
- 適合簡單場景和性能要求高的場景
- Redisson:
- 基于 Netty 實現
- 提供分布式對象和服務
- 內置分布式鎖、原子操作等高級功能
- 適合復雜分布式場景
2.2.1 Maven 依賴配置
Jedis 依賴配置(推薦使用連接池):
<!-- Jedis 核心依賴 -->
<dependency>
<groupId>redis.clients</groupId>
<artifactId>jedis</artifactId>
<version>4.3.1</version>
</dependency>
<!-- 連接池依賴(必須) -->
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-pool2</artifactId>
<version>2.11.1</version>
</dependency>
<!-- 可選:JSON 序列化支持 -->
<dependency>
<groupId>com.google.code.gson</groupId>
<artifactId>gson</artifactId>
<version>2.9.0</version>
</dependency>Redisson 依賴配置:
<!-- Redisson 核心依賴 -->
<dependency>
<groupId>org.redisson</groupId>
<artifactId>redisson</artifactId>
<version>3.17.6</version>
</dependency>
<!-- 可選:配置支持(如 JSON/YAML 配置文件) -->
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.13.3</version>
</dependency>版本選擇建議:
- 生產環(huán)境建議使用穩(wěn)定版本
- 可訪問 Maven 中央倉庫(https://mvnrepository.com/)查詢最新版本
- 注意版本兼容性:
- Jedis 4.x 支持 Redis 6.x
- Redisson 3.x 支持 Redis 5.x+
開發(fā)環(huán)境驗證
創(chuàng)建簡單的測試類驗證環(huán)境是否配置成功:
import redis.clients.jedis.Jedis;
public class RedisTest {
public static void main(String[] args) {
// 創(chuàng)建連接
Jedis jedis = new Jedis("localhost", 6379);
jedis.auth("your_password"); // 如果有密碼
// 測試連接
System.out.println("連接狀態(tài):" + jedis.ping());
// 基本操作
jedis.set("test_key", "Hello Redis");
System.out.println("獲取值:" + jedis.get("test_key"));
// 關閉連接
jedis.close();
}
}IDE 配置建議
- IntelliJ IDEA:
- 確保 Maven 項目正確導入依賴
- 安裝 Redis 插件(如 "Redis" 插件)方便調試
- Eclipse:
- 使用 M2Eclipse 插件管理依賴
- 可安裝 Redis 客戶端插件(如 "RedisClipse")
生產環(huán)境注意事項
- 連接池配置:
- 必須使用連接池管理連接
- 推薦配置參數:
JedisPoolConfig poolConfig = new JedisPoolConfig(); poolConfig.setMaxTotal(128); // 最大連接數 poolConfig.setMaxIdle(32); // 最大空閑連接 poolConfig.setMinIdle(8); // 最小空閑連接 poolConfig.setTestOnBorrow(true); // 獲取連接時測試
- 安全建議:
- 不要將密碼硬編碼在代碼中
- 使用配置中心或環(huán)境變量管理敏感信息
- 啟用 SSL/TLS 加密傳輸(Redis 6+支持)
- 性能優(yōu)化:
- 批量操作使用 pipeline
- 大 value 考慮分片存儲
- 合理選擇序列化方式
三、Jedis 客戶端:Java 操作 Redis 的基礎實現
3.1 基本使用:單實例連接
步驟 1:創(chuàng)建 Jedis 實例并連接 Redis
import redis.clients.jedis.Jedis;
public class JedisBasicDemo {
public static void main(String[] args) {
// 1. 創(chuàng)建Jedis實例(參數說明:Redis地址、端口、連接超時時間(毫秒)、密碼)
// 注意:如果Redis服務器在同一臺機器,地址可以是"localhost"
Jedis jedis = new Jedis("192.168.1.100", 6379, 5000);
try {
// 2. 驗證密碼(若Redis未設置密碼,可跳過此步驟)
// 如果密碼錯誤,會拋出redis.clients.jedis.exceptions.JedisDataException
jedis.auth("your_password");
// 3. 測試連接
// ping()返回"PONG"表示連接正常
System.out.println("Redis連接狀態(tài):" + jedis.ping()); // 期望輸出:PONG
// 4. 操作String類型數據
jedis.set("username", "zhangsan"); // 存入數據,默認永不過期
// 帶過期時間的設置(單位秒)
jedis.setex("temp_token", 3600, "abcd1234");
String username = jedis.get("username"); // 獲取數據
System.out.println("獲取到的用戶名:" + username); // 輸出:zhangsan
// 5. 操作Hash類型數據
// 存儲用戶信息
jedis.hset("user:1001", "name", "lisi");
jedis.hset("user:1001", "age", "25");
jedis.hset("user:1001", "gender", "male");
// 獲取Hash中所有字段和值(返回Map<String,String>)
System.out.println("用戶1001信息:" + jedis.hgetAll("user:1001"));
// 輸出:{name=lisi, age=25, gender=male}
// 獲取單個字段
System.out.println("用戶年齡:" + jedis.hget("user:1001", "age")); // 輸出:25
} catch (Exception e) {
e.printStackTrace();
} finally {
// 6. 關閉連接(重要!避免連接泄漏)
if (jedis != null) {
jedis.close();
}
}
}
}注意事項
- 線程安全性:
- Jedis實例是非線程安全的,每個線程應該使用自己的Jedis實例
- 在高并發(fā)場景中直接使用單例連接會導致線程安全問題
- 性能考量:
- 每次操作都需要創(chuàng)建和關閉TCP連接,頻繁操作時會產生較大性能開銷
- 實測表明,在100次連續(xù)操作中,單實例連接方式耗時是連接池方式的10倍以上
- 實際應用建議:
- 開發(fā)環(huán)境或簡單腳本可以使用單實例連接
- 生產環(huán)境必須使用連接池(JedisPool)管理連接
- 對于長時間運行的任務,建議使用try-with-resources確保連接關閉
- 異常處理:
- 網絡中斷時可能拋出JedisConnectionException
- 認證失敗拋出JedisDataException
- 建議對關鍵操作添加重試機制
3.2 進階使用:Jedis 連接池
步驟 1:配置 Jedis 連接池
import org.apache.commons.pool2.impl.GenericObjectPoolConfig;
import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPool;
public class JedisPoolDemo {
// 初始化Jedis連接池(使用靜態(tài)代碼塊確保只初始化一次)
private static JedisPool jedisPool;
static {
// 1. 配置連接池參數
GenericObjectPoolConfig<Jedis> poolConfig = new GenericObjectPoolConfig<>();
// 關鍵參數配置(根據實際業(yè)務需求調整)
poolConfig.setMaxTotal(100); // 最大連接數(根據Redis服務器配置調整)
poolConfig.setMaxIdle(20); // 最大空閑連接數(建議設為MaxTotal的20%-50%)
poolConfig.setMinIdle(5); // 最小空閑連接數(保持一定數量的熱連接)
poolConfig.setBlockWhenExhausted(true); // 連接耗盡時是否阻塞等待(建議true)
poolConfig.setMaxWaitMillis(3000); // 獲取連接最大等待時間(毫秒)
poolConfig.setTestOnBorrow(true); // 獲取連接時是否測試有效性(建議true,但會有性能損耗)
poolConfig.setTestOnReturn(false); // 歸還連接時是否測試(建議false)
poolConfig.setTestWhileIdle(true); // 空閑時是否測試(建議true)
// 2. 初始化連接池
String redisHost = "192.168.1.100";
int redisPort = 6379;
int timeout = 5000; // 連接超時時間(毫秒)
String password = "your_password";
jedisPool = new JedisPool(
poolConfig,
redisHost,
redisPort,
timeout,
password
);
}
// 從連接池獲取Jedis實例
public static Jedis getJedis() {
return jedisPool.getResource();
}
// 測試連接池使用
public static void main(String[] args) {
// 使用try-with-resources語法自動管理連接
try (Jedis jedis = getJedis()) {
// 1. 操作String類型
jedis.set("product:1001", "iPhone 13");
System.out.println("商品1001名稱:" + jedis.get("product:1001"));
// 2. 操作List類型(模擬消息隊列)
jedis.lpush("message:queue", "msg1", "msg2", "msg3"); // 左推(入隊)
// 右拉(出隊),0表示無限期阻塞等待
String msg = jedis.brpop(0, "message:queue").get(1);
System.out.println("消費的消息:" + msg);
// 3. 批量操作(使用pipeline提升性能)
long start = System.currentTimeMillis();
for (int i = 0; i < 1000; i++) {
jedis.set("key_" + i, "value_" + i);
}
long end = System.currentTimeMillis();
System.out.println("普通操作耗時:" + (end - start) + "ms");
} catch (Exception e) {
e.printStackTrace();
}
}
}核心優(yōu)勢
- 性能優(yōu)化:
- 連接復用減少TCP三次握手開銷
- 實測在1000次操作中,連接池方式比單實例快8-10倍
- 支持Pipeline批量操作,進一步提升吞吐量
- 資源管理:
- 自動維護連接池中的活躍連接數
- 可配置連接最大存活時間(maxAge)避免長時間使用的連接出現問題
- 通過evictor線程定期檢測和清理無效連接
- 生產環(huán)境配置建議:
// 典型生產環(huán)境配置 poolConfig.setMaxTotal(200); // 根據QPS估算,一般(平均QPS*平均耗時(ms))/1000 poolConfig.setMaxIdle(50); // 根據業(yè)務波動情況調整 poolConfig.setMinIdle(10); // 保持一定熱連接 poolConfig.setMaxWaitMillis(1000); // 根據系統(tǒng)容忍度設置 poolConfig.setTestOnBorrow(true); // 確保獲取的連接可用 poolConfig.setTimeBetweenEvictionRunsMillis(30000); // 30秒檢測一次
- 監(jiān)控指標:
- 可通過JMX監(jiān)控連接池狀態(tài)
- 關鍵指標:活動連接數、空閑連接數、等待獲取連接的線程數等
- 最佳實踐:
- 每個應用使用獨立的連接池
- 根據業(yè)務類型(讀寫比例、命令復雜度)配置不同連接池
- 定期監(jiān)控連接池狀態(tài),及時調整參數
3.3 Jedis 操作核心數據結構
(1)Set 類型(無序、唯一)
try (Jedis jedis = JedisPoolDemo.getJedis()) {
// 1. 添加元素(自動去重)
jedis.sadd("tags:java", "spring", "mybatis", "redis");
jedis.sadd("tags:java", "spring"); // 重復元素不會被添加
// 2. 獲取所有元素(無序)
System.out.println("Java相關標簽:" + jedis.smembers("tags:java"));
// 輸出可能是:[redis, spring, mybatis]
// 3. 判斷元素是否存在
System.out.println("是否包含spring:" + jedis.sismember("tags:java", "spring"));
// 輸出:true
// 4. 移除元素
jedis.srem("tags:java", "mybatis");
// 5. 計算集合大小
System.out.println("標簽數量:" + jedis.scard("tags:java")); // 輸出:2
// 6. 集合運算
jedis.sadd("tags:backend", "spring", "django", "redis");
// 交集
System.out.println("交集:" + jedis.sinter("tags:java", "tags:backend"));
// 輸出:[spring, redis]
// 并集
System.out.println("并集:" + jedis.sunion("tags:java", "tags:backend"));
// 輸出:[spring, redis, django]
// 差集(tags:java有而tags:backend沒有的)
System.out.println("差集:" + jedis.sdiff("tags:java", "tags:backend"));
// 輸出:[]
}(2)Sorted Set 類型(有序、唯一,基于分數排序)
try (Jedis jedis = JedisPoolDemo.getJedis()) {
// 1. 添加元素(如果成員已存在,則更新分數)
jedis.zadd("rank:java", 95, "zhangsan");
jedis.zadd("rank:java", 88, "lisi");
jedis.zadd("rank:java", 92, "wangwu");
jedis.zadd("rank:java", 90, "lisi"); // 更新lisi的分數
// 2. 按分數升序獲?。?到-1表示全部)
System.out.println("升序排名:" + jedis.zrange("rank:java", 0, -1));
// 輸出:[lisi, wangwu, zhangsan]
// 3. 按分數降序獲取(帶分數)
Set<Tuple> results = jedis.zrevrangeWithScores("rank:java", 0, -1);
results.forEach(tuple ->
System.out.println(tuple.getElement() + ":" + tuple.getScore())
);
// 輸出:
// zhangsan:95.0
// wangwu:92.0
// lisi:90.0
// 4. 獲取分數
System.out.println("lisi的分數:" + jedis.zscore("rank:java", "lisi"));
// 輸出:90.0
// 5. 獲取排名(從0開始)
System.out.println("zhangsan的降序排名:" + jedis.zrevrank("rank:java", "zhangsan"));
// 輸出:0
// 6. 范圍查詢(80<=score<=90)
System.out.println("80-90分的學生:" +
jedis.zrangeByScoreWithScores("rank:java", 80, 90));
// 輸出:[lisi:90.0, wangwu:92.0](注意包含邊界)
// 7. 原子操作:增加分數
jedis.zincrby("rank:java", 5, "lisi"); // lisi分數+5
System.out.println("新分數:" + jedis.zscore("rank:java", "lisi"));
// 輸出:95.0
}(3)其他數據結構操作
// Geo地理位置操作
try (Jedis jedis = JedisPoolDemo.getJedis()) {
// 添加地理位置(經度、緯度、名稱)
jedis.geoadd("cities", 116.404, 39.915, "beijing");
jedis.geoadd("cities", 121.474, 31.230, "shanghai");
// 計算兩地距離(單位:km)
Double dist = jedis.geodist("cities", "beijing", "shanghai", GeoUnit.KM);
System.out.println("北京到上海距離:" + dist + "km");
// 查找附近位置
List<GeoRadiusResponse> nearby = jedis.georadiusByMember(
"cities",
"beijing",
1000,
GeoUnit.KM
);
nearby.forEach(r -> System.out.println(r.getMemberByString()));
}
// HyperLogLog基數統(tǒng)計
try (Jedis jedis = JedisPoolDemo.getJedis()) {
// 添加元素(用于統(tǒng)計不重復元素數量)
for (int i = 0; i < 10000; i++) {
jedis.pfadd("visitors", "user_" + i);
jedis.pfadd("visitors", "user_" + (i % 5000)); // 50%重復
}
// 獲取基數估計值
System.out.println("獨立訪客數:" + jedis.pfcount("visitors"));
// 輸出約7500(誤差率約0.81%)
}四、Redisson 客戶端:Java 操作 Redis 的高級實現
Redisson是基于Jedis的封裝,不僅提供了更簡潔易用的API,還實現了大量Redis官方推薦的分布式特性。相比原生Jedis,Redisson具有以下核心優(yōu)勢:
- 線程安全:內置連接池管理,徹底解決Jedis線程安全問題
- 豐富特性:支持分布式鎖(可重入鎖、公平鎖、聯(lián)鎖等)、分布式集合(Set、Map、List)、延遲隊列、布隆過濾器等20+種分布式對象
- 協(xié)議支持:完整支持Redis 5.0+的所有命令和數據類型
- 容錯機制:自動重連、故障轉移、讀寫分離等企業(yè)級特性
4.1 Redisson客戶端初始化
Redisson支持多種Redis部署模式,每種模式的配置方式如下:
單點模式配置示例
import org.redisson.Redisson;
import org.redisson.api.RedissonClient;
import org.redisson.config.Config;
/**
* Redisson客戶端工廠類(推薦單例模式)
* 典型應用場景:Spring Bean管理、靜態(tài)工具類等
*/
public class RedissonClientFactory {
// 使用volatile保證多線程可見性
private static volatile RedissonClient redissonClient;
// 雙重檢查鎖實現線程安全初始化
public static RedissonClient getRedissonClient() {
if (redissonClient == null) {
synchronized (RedissonClientFactory.class) {
if (redissonClient == null) {
// 1. 創(chuàng)建配置構建器
Config config = new Config();
// 2. 配置單點模式(生產環(huán)境建議使用YAML/JSON配置文件)
config.useSingleServer()
.setAddress("redis://192.168.1.100:6379") // 支持rediss://協(xié)議(SSL加密)
.setPassword("your_password") // 密碼特殊字符需URL編碼
.setDatabase(0) // 默認DB索引
.setConnectionPoolSize(100) // 最大連接數(默認64)
.setConnectionMinimumIdleSize(10) // 最小空閑連接(默認10)
.setConnectTimeout(5000) // 連接超時(毫秒)
.setIdleConnectionTimeout(30000) // 空閑連接超時
.setRetryAttempts(3) // 命令重試次數
.setRetryInterval(1000); // 重試間隔
// 3. 創(chuàng)建客戶端實例(實際會啟動連接池和定時任務)
redissonClient = Redisson.create(config);
}
}
}
return redissonClient;
}
// 優(yōu)雅關閉方法
public static void shutdown() {
if (redissonClient != null) {
redissonClient.shutdown();
}
}
}其他部署模式配置示例
哨兵模式
config.useSentinelServers()
.addSentinelAddress("redis://sentinel1:26379")
.addSentinelAddress("redis://sentinel2:26379")
.setMasterName("mymaster");
集群模式
config.useClusterServers()
.addNodeAddress("redis://node1:6379")
.addNodeAddress("redis://node2:6379")
.setScanInterval(2000); // 集群狀態(tài)掃描間隔
最佳實踐建議
- 連接池配置:根據QPS調整連接數(建議公式:最大連接數 = QPS × 平均響應時間(秒))
- 超時設置:網絡不穩(wěn)定的環(huán)境適當增大超時時間
- 資源釋放:應用關閉時調用
shutdown()釋放資源 - 配置分離:生產環(huán)境建議使用外部配置文件(JSON/YAML格式)
4.2 Redisson 操作核心數據結構
Redisson 是一個基于 Redis 的 Java 客戶端,它通過 RedissonClient 提供了對應 Redis 數據結構的封裝類,使得操作 Redis 更加面向對象和簡潔。Redisson 支持 Redis 的所有核心數據結構,包括 String、Hash、List、Set 和 Sorted Set 等,每種數據結構都有對應的 Java 接口實現。
(1)String 類型(RBucket)
RBucket 是 Redisson 對 Redis String 類型的封裝,提供了一系列操作字符串的方法。RBucket 支持設置過期時間,適用于緩存場景。
import org.redisson.api.RBucket;
import org.redisson.api.RedissonClient;
public class RedissonStringDemo {
public static void main(String[] args) {
// 獲取 RedissonClient 實例
RedissonClient redissonClient = RedissonClientFactory.getRedissonClient();
try {
// 獲取 RBucket 對象(對應 Redis 的 String 類型)
RBucket<String> usernameBucket = redissonClient.getBucket("username");
// 存入數據,并設置過期時間為 3600 秒(1小時)
usernameBucket.set("zhangsan", 3600);
// 獲取數據
String username = usernameBucket.get();
System.out.println("用戶名:" + username); // 輸出:zhangsan
// 判斷 key 是否存在
System.out.println("是否存在:" + usernameBucket.isExists()); // 輸出:true
// 刪除數據
usernameBucket.delete();
System.out.println("刪除后是否存在:" + usernameBucket.isExists()); // 輸出:false
} finally {
// 關閉客戶端(若為長期運行的服務,無需頻繁關閉)
redissonClient.shutdown();
}
}
}(2)Hash 類型(RMap)
RMap 是 Redisson 對 Redis Hash 類型的封裝,提供類似 Java Map 的操作方法,適用于存儲對象或結構化數據。
import org.redisson.api.RMap;
import org.redisson.api.RedissonClient;
public class RedissonHashDemo {
public static void main(String[] args) {
RedissonClient redissonClient = RedissonClientFactory.getRedissonClient();
try {
// 獲取 RMap 對象(對應 Redis 的 Hash 類型)
RMap<String, String> userMap = redissonClient.getMap("user:1001");
// 存入數據
userMap.put("name", "lisi");
userMap.put("age", "25");
userMap.put("gender", "male");
// 獲取數據
System.out.println("用戶姓名:" + userMap.get("name")); // 輸出:lisi
System.out.println("用戶所有信息:" + userMap); // 輸出:{name=lisi, age=25, gender=male}
// 刪除字段
userMap.remove("gender");
System.out.println("刪除 gender 后的信息:" + userMap); // 輸出:{name=lisi, age=25}
// 判斷字段是否存在
System.out.println("是否包含 age:" + userMap.containsKey("age")); // 輸出:true
} finally {
// 關閉客戶端
redissonClient.shutdown();
}
}
}(3)List 類型(RList)
RList 是 Redisson 對 Redis List 類型的封裝,支持雙向添加、刪除和獲取元素,適用于隊列或棧的場景。
import org.redisson.api.RList;
import org.redisson.api.RedissonClient;
public class RedissonListDemo {
public static void main(String[] args) {
RedissonClient redissonClient = RedissonClientFactory.getRedissonClient();
try {
// 獲取 RList 對象(對應 Redis 的 List 類型)
RList<String> messageList = redissonClient.getList("message:queue");
// 添加元素(右添加,等同于 RPUSH)
messageList.add("msg1");
// 左添加(等同于 LPUSH)
messageList.add(0, "msg0");
// 獲取指定索引元素
System.out.println("索引 0 的元素:" + messageList.get(0)); // 輸出:msg0
// 獲取所有元素
System.out.println("所有消息:" + messageList); // 輸出:[msg0, msg1]
// 移除并返回最后一個元素(等同于 RPOP)
String lastMsg = messageList.remove(messageList.size() - 1);
System.out.println("移除的最后一條消息:" + lastMsg); // 輸出:msg1
// 獲取列表長度
System.out.println("列表長度:" + messageList.size()); // 輸出:1
} finally {
redissonClient.shutdown();
}
}
}(4)Set 類型(RSet)
RSet 是 Redisson 對 Redis Set 類型的封裝,支持元素的添加、刪除和集合運算,適用于標簽或唯一值存儲場景。
import org.redisson.api.RSet;
import org.redisson.api.RedissonClient;
public class RedissonSetDemo {
public static void main(String[] args) {
RedissonClient redissonClient = RedissonClientFactory.getRedissonClient();
try {
// 獲取 RSet 對象(對應 Redis 的 Set 類型)
RSet<String> tagSet = redissonClient.getSet("tags:java");
// 添加元素
tagSet.add("spring");
tagSet.add("mybatis");
tagSet.add("redis");
// 判斷元素是否存在
System.out.println("是否包含 spring:" + tagSet.contains("spring")); // 輸出:true
// 獲取所有元素
System.out.println("Java 標簽集合:" + tagSet); // 輸出:[spring, mybatis, redis]
// 創(chuàng)建另一個 Set
RSet<String> backendTagSet = redissonClient.getSet("tags:backend");
backendTagSet.add("spring");
backendTagSet.add("django");
backendTagSet.add("redis");
// 計算并返回交集
System.out.println("Java 與 Backend 標簽交集:" + tagSet.retainAll(backendTagSet));
// 輸出:[spring, redis](retainAll 方法會保留交集元素并返回)
// 獲取集合大小
System.out.println("交集大小:" + tagSet.size()); // 輸出:2
} finally {
redissonClient.shutdown();
}
}
}(5)Sorted Set 類型(RSortedSet)
RSortedSet 是 Redisson 對 Redis Sorted Set 類型的封裝,支持按分數排序和范圍查詢,適用于排行榜或優(yōu)先級隊列場景。
import org.redisson.api.RSortedSet;
import org.redisson.api.RedissonClient;
import org.redisson.api.ScoredEntry;
import java.util.Collection;
public class RedissonSortedSetDemo {
public static void main(String[] args) {
RedissonClient redissonClient = RedissonClientFactory.getRedissonClient();
try {
// 獲取 RSortedSet 對象(對應 Redis 的 Sorted Set 類型)
RSortedSet<String> rankSet = redissonClient.getSortedSet("rank:java");
// 添加元素(指定分數)
rankSet.add(95, "zhangsan");
rankSet.add(88, "lisi");
rankSet.add(92, "wangwu");
// 按分數升序獲取元素
System.out.println("成績升序排名:" + rankSet); // 輸出:[lisi, wangwu, zhangsan]
// 按分數降序獲取元素(含分數)
Collection<ScoredEntry<String>> scoredEntries = rankSet.entryRangeReversed(0, -1);
for (ScoredEntry<String> entry : scoredEntries) {
System.out.println(entry.getValue() + ":" + entry.getScore());
// 輸出:
// zhangsan:95.0
// wangwu:92.0
// lisi:88.0
}
// 獲取指定元素的排名(降序)
System.out.println("zhangsan 的排名:" + rankSet.revRank("zhangsan")); // 輸出:0
// 獲取分數范圍內的元素
Collection<String> range = rankSet.valueRange(90, true, 95, true);
System.out.println("分數在 90~95 之間的學生:" + range); // 輸出:[wangwu, zhangsan]
} finally {
redissonClient.shutdown();
}
}
}4.3 Redisson 高級特性:分布式鎖與延遲隊列
4.3.1 分布式鎖(RLock)
在分布式系統(tǒng)中,多個服務實例可能同時操作同一資源(如訂單創(chuàng)建、庫存扣減等場景),分布式鎖可避免并發(fā)問題,保證數據一致性。Redisson 的RLock實現了與Java的ReentrantLock類似的可重入鎖特性,且支持自動過期釋放機制,無需手動處理死鎖問題。
核心特性
- 可重入性:同一線程可以多次獲取同一把鎖
- 自動續(xù)期:通過watchdog機制自動延長鎖持有時間
- 公平鎖支持:可配置為公平鎖模式
- 鎖等待超時:避免線程無限期等待
- 鎖釋放保障:通過finally塊確保鎖釋放
典型應用場景
- 秒殺系統(tǒng)中的庫存扣減
- 支付系統(tǒng)中的訂單狀態(tài)變更
- 分布式定時任務調度
- 重要數據的并發(fā)修改
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import java.util.concurrent.TimeUnit;
public class RedissonDistributedLockDemo {
// 鎖的key命名規(guī)范:業(yè)務類型:業(yè)務ID(如訂單創(chuàng)建鎖)
private static final String LOCK_KEY = "lock:order:create";
public static void createOrder(RedissonClient redissonClient) {
// 1. 獲取分布式鎖實例(非阻塞)
RLock lock = redissonClient.getLock(LOCK_KEY);
try {
// 2. 嘗試獲取鎖(參數說明)
// - waitTime: 獲取鎖的最大等待時間(建議設置短于業(yè)務超時時間)
// - leaseTime: 鎖自動釋放時間(業(yè)務完成前應足夠長)
// - TimeUnit: 時間單位
boolean isLocked = lock.tryLock(3, 30, TimeUnit.SECONDS);
if (isLocked) {
try {
// 3. 加鎖成功,執(zhí)行業(yè)務邏輯
System.out.println(Thread.currentThread().getName() + "獲取分布式鎖成功,開始創(chuàng)建訂單...");
// 模擬業(yè)務處理耗時(如數據庫操作、遠程調用等)
Thread.sleep(5000);
System.out.println("訂單創(chuàng)建完成!");
} finally {
// 4. 確保業(yè)務完成后再釋放鎖
if (lock.isHeldByCurrentThread()) {
lock.unlock();
System.out.println("分布式鎖已釋放!");
}
}
} else {
// 5. 獲取鎖失敗處理(可記錄日志、返回友好提示或重試)
System.out.println("獲取分布式鎖失敗,請稍后再試!");
// 可拋出特定異?;蚍祷劐e誤碼
}
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
System.err.println("線程被中斷:" + e.getMessage());
} catch (Exception e) {
System.err.println("業(yè)務處理異常:" + e.getMessage());
}
}
public static void main(String[] args) {
RedissonClient redissonClient = RedissonClientFactory.getRedissonClient();
// 模擬3個并發(fā)線程嘗試獲取鎖
for (int i = 0; i < 3; i++) {
new Thread(() -> createOrder(redissonClient), "Thread-"+i).start();
}
// 注意:實際應用中RedissonClient應該是單例長期存在
// redissonClient.shutdown();
}
}最佳實踐
- 鎖命名規(guī)范:使用業(yè)務相關的有意義的名稱,如
lock:order:{orderId} - 鎖超時設置:leaseTime應大于預估的業(yè)務處理時間
- 異常處理:確保在finally塊中釋放鎖
- 避免長時間持有鎖:鎖持有時間應盡量短
- 重試機制:獲取鎖失敗時應有合理的重試策略
4.3.2 延遲隊列(RDelayedQueue)
延遲隊列可實現"消息延遲一段時間后再被消費"的場景,如訂單超時未支付自動取消、定時任務觸發(fā)、預約提醒等。Redisson的RDelayedQueue基于Redis的Sorted Set實現,提供了簡單易用的API,避免了自行實現延遲隊列的復雜性。
核心特性
- 精確延時:支持毫秒級延遲精度
- 持久化存儲:消息不會因服務重啟而丟失
- 高可用:基于Redis集群保證可靠性
- 多消費者支持:多個服務實例可以同時消費
- 消息順序保障:按延遲時間順序消費消息
典型應用場景
- 電商訂單超時自動取消(30分鐘未支付)
- 會議開始前15分鐘提醒
- 延時任務調度
- 紅包24小時未領取自動退回
import org.redisson.api.RBlockingQueue;
import org.redisson.api.RDelayedQueue;
import org.redisson.api.RedissonClient;
import java.util.concurrent.TimeUnit;
public class RedissonDelayedQueueDemo {
// 延遲隊列命名規(guī)范:業(yè)務類型:業(yè)務目的
private static final String DELAY_QUEUE_KEY = "delay:queue:order";
private static final String BLOCKING_QUEUE_KEY = "blocking:queue:order";
/**
* 生產者:添加延遲消息
* @param redissonClient Redisson客戶端
* @param message 消息內容(建議包含業(yè)務ID)
* @param delayTime 延遲時間(單位:秒)
*/
public static void produceDelayMessage(RedissonClient redissonClient, String message, long delayTime) {
// 1. 獲取阻塞隊列(底層實際存儲結構)
RBlockingQueue<String> blockingQueue = redissonClient.getBlockingQueue(BLOCKING_QUEUE_KEY);
// 2. 創(chuàng)建延遲隊列(與阻塞隊列關聯(lián))
try (RDelayedQueue<String> delayedQueue = redissonClient.getDelayedQueue(blockingQueue)) {
// 3. 添加延遲消息
delayedQueue.offer(message, delayTime, TimeUnit.SECONDS);
System.out.println("[" + System.currentTimeMillis() + "] 添加延遲消息:" + message
+ ",延遲時間:" + delayTime + "秒");
} // 自動關閉延遲隊列(Java 7+ try-with-resources)
}
/**
* 消費者:持續(xù)消費延遲消息
* @param redissonClient Redisson客戶端
*/
public static void consumeDelayMessage(RedissonClient redissonClient) {
// 1. 獲取阻塞隊列(與生產者使用的相同)
RBlockingQueue<String> blockingQueue = redissonClient.getBlockingQueue(BLOCKING_QUEUE_KEY);
System.out.println("消費者啟動,等待消費延遲消息...");
while (!Thread.currentThread().isInterrupted()) {
try {
// 2. 阻塞獲取消息(當有消息到期時自動喚醒)
String message = blockingQueue.take();
// 3. 處理消息
long currentTime = System.currentTimeMillis();
System.out.println("[" + currentTime + "] 消費延遲消息:" + message);
// 根據消息內容處理業(yè)務
if (message != null && message.startsWith("order:")) {
String[] parts = message.split(":");
if (parts.length >= 2) {
String orderId = parts[1];
System.out.println("處理訂單超時:訂單" + orderId + "超時未支付,已自動取消!");
// 實際業(yè)務中此處會調用訂單服務取消訂單
}
}
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
System.err.println("消費者線程被中斷");
break;
} catch (Exception e) {
System.err.println("消費消息異常:" + e.getMessage());
// 可添加重試或錯誤處理邏輯
}
}
}
public static void main(String[] args) {
RedissonClient redissonClient = RedissonClientFactory.getRedissonClient();
// 啟動消費者線程(實際應用中通常是獨立服務)
Thread consumerThread = new Thread(() -> consumeDelayMessage(redissonClient));
consumerThread.setDaemon(true);
consumerThread.start();
// 模擬生產3個訂單延遲消息
produceDelayMessage(redissonClient, "order:10001", 5); // 5秒后超時
produceDelayMessage(redissonClient, "order:10002", 10); // 10秒后超時
produceDelayMessage(redissonClient, "order:10003", 15); // 15秒后超時
// 保持主線程運行(演示用)
try {
Thread.sleep(20000);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
// 實際應用中不需要關閉(長期運行的服務)
// redissonClient.shutdown();
}
}最佳實踐
- 消息設計:消息內容應包含足夠業(yè)務信息(如訂單ID)
- 異常處理:消費者需處理各種異常情況
- 冪等性:消息處理應保證冪等,防止重復消費
- 監(jiān)控報警:對消費延遲和堆積情況監(jiān)控
- 資源釋放:正確關閉延遲隊列實例
性能優(yōu)化建議
- 批量消費:可適當批量獲取消息減少網絡開銷
- 分區(qū)設計:大流量場景可按業(yè)務ID分多個隊列
- 消費并行度:根據業(yè)務需求調整消費者數量
- 消息壓縮:大消息可考慮壓縮后再存儲
五、Spring Boot 整合 Redis:企業(yè)級項目實戰(zhàn)
在 Spring Boot 項目中,可通過 spring-boot-starter-data-redis 快速整合 Redis,簡化配置和開發(fā)。該 starter 默認使用 Spring Data Redis 抽象層,支持 Jedis、Lettuce(Spring Boot 2.x 默認客戶端)等底層客戶端。Spring Data Redis 提供了統(tǒng)一的 API 來操作 Redis,支持多種數據結構的操作,包括字符串、哈希、列表、集合等。
5.1 環(huán)境配置
5.1.1 添加 Maven 依賴
在 pom.xml 中添加以下依賴:
<!-- Spring Boot Redis Starter -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
<!-- 排除默認的Lettuce客戶端(若需使用Jedis,可添加此配置) -->
<!-- <exclusions>
<exclusion>
<groupId>io.lettuce</groupId>
<artifactId>lettuce-core</artifactId>
</exclusion>
</exclusions> -->
</dependency>
<!-- 若排除Lettuce后,需添加Jedis依賴 -->
<!-- <dependency>
<groupId>redis.clients</groupId>
<artifactId>jedis</artifactId>
</dependency> -->
<!-- Spring Boot Web Starter(用于演示接口) -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>說明:
spring-boot-starter-data-redis是 Spring Boot 提供的 Redis 啟動器,自動配置了 Redis 相關的 Bean。- 默認使用 Lettuce 客戶端,如需切換為 Jedis,需排除 Lettuce 并添加 Jedis 依賴。
spring-boot-starter-web用于演示 RESTful 接口,實際項目中根據需求添加。
5.1.2 配置 application.yml
在 src/main/resources/application.yml 中添加 Redis 相關配置:
spring:
redis:
host: 192.168.1.100 # Redis服務地址,生產環(huán)境建議使用域名或內網IP
port: 6379 # Redis端口,默認6379
password: your_password # Redis密碼(若未設,可省略)
database: 0 # Redis數據庫索引(0-15),默認0
timeout: 5000ms # 連接超時時間
lettuce: # Lettuce客戶端配置(若使用Jedis,需改為jedis配置)
pool: # 連接池配置
max-active: 100 # 最大連接數,默認8
max-idle: 20 # 最大空閑連接數,默認8
min-idle: 5 # 最小空閑連接數,默認0
max-wait: 3000ms # 連接池耗盡時的最大等待時間,默認-1(無限等待)配置項詳解:
host和port:Redis 服務器地址和端口,必填項。password:如果 Redis 設置了密碼,需配置此項。database:Redis 默認有 16 個數據庫(0-15),可通過此配置選擇使用的數據庫。timeout:連接 Redis 的超時時間,建議設置為 5000ms 以上。lettuce.pool:Lettuce 連接池配置,適用于高并發(fā)場景:max-active:最大連接數,根據業(yè)務量調整。max-idle和min-idle:空閑連接數,避免頻繁創(chuàng)建和銷毀連接。max-wait:獲取連接的最大等待時間,避免長時間阻塞。
Jedis 配置示例(如需切換):
spring:
redis:
host: 192.168.1.100
port: 6379
jedis:
pool:
max-active: 100
max-idle: 20
min-idle: 5
max-wait: 3000ms生產環(huán)境建議:
- 使用連接池以提高性能。
- 密碼和敏感信息建議通過環(huán)境變量或配置中心管理。
- 對于集群或哨兵模式,需額外配置。
5.2 核心 API:RedisTemplate 與 StringRedisTemplate
Spring Data Redis 提供了兩種主要的模板類用于操作 Redis,它們各自適用于不同的使用場景:
StringRedisTemplate
StringRedisTemplate 是專門用于操作 String 類型數據的模板類,具有以下特點:
- 序列化方式:默認使用 StringRedisSerializer,所有鍵和值都以 UTF-8 編碼的字符串形式存儲
- 優(yōu)勢:
- 完全避免中文亂碼問題
- 存儲的數據可直接在 Redis CLI 中查看
- 性能較高,適用于簡單字符串操作
- 限制:
- 只能操作字符串類型的數據
- 無法直接存儲 Java 對象
RedisTemplate
RedisTemplate 是通用模板類,支持所有 Redis 數據結構,但需要注意:
- 默認序列化問題:
- 默認使用 JdkSerializationRedisSerializer
- 會導致存儲的數據帶有"xac\xed\x00\x05t\x00"等序列化前綴
- 存儲的數據在 Redis CLI 中不可讀
- 適用場景:
- 需要操作復雜數據結構(List, Set, ZSet等)
- 需要直接存儲 Java 對象
- 需要自定義序列化方式
5.2.1 配置 RedisTemplate(自定義序列化)
為優(yōu)化 RedisTemplate 的默認行為,推薦配置自定義序列化:
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
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.StringRedisSerializer;
@Configuration
public class RedisConfig {
@Bean
public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory redisConnectionFactory) {
RedisTemplate<String, Object> redisTemplate = new RedisTemplate<>();
// 設置連接工廠(必需)
redisTemplate.setConnectionFactory(redisConnectionFactory);
// Key序列化配置(String類型)
StringRedisSerializer stringRedisSerializer = new StringRedisSerializer();
redisTemplate.setKeySerializer(stringRedisSerializer);
redisTemplate.setHashKeySerializer(stringRedisSerializer);
// Value序列化配置(JSON格式)
GenericJackson2JsonRedisSerializer jackson2JsonRedisSerializer =
new GenericJackson2JsonRedisSerializer();
redisTemplate.setValueSerializer(jackson2JsonRedisSerializer);
redisTemplate.setHashValueSerializer(jackson2JsonRedisSerializer);
// 初始化模板(必需)
redisTemplate.afterPropertiesSet();
return redisTemplate;
}
}配置說明:
- 鍵序列化使用 StringRedisSerializer,保證鍵的可讀性
- 值序列化使用 Jackson,支持復雜對象存儲
- 必須調用 afterPropertiesSet() 完成初始化
5.2.2 StringRedisTemplate 使用示例
StringRedisTemplate 的常見操作示例:
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.web.bind.annotation.*;
import java.util.Map;
import java.util.concurrent.TimeUnit;
@RestController
@RequestMapping("/redis/string")
public class StringRedisController {
@Autowired
private StringRedisTemplate stringRedisTemplate;
// 存儲String數據(帶過期時間)
@PostMapping("/set")
public String setString(@RequestParam String key,
@RequestParam String value,
@RequestParam(required = false, defaultValue = "3600") long timeout) {
stringRedisTemplate.opsForValue().set(key, value, timeout, TimeUnit.SECONDS);
return "存儲成功!Key:" + key + ",Value:" + value;
}
// 獲取String數據
@GetMapping("/get/{key}")
public String getString(@PathVariable String key) {
String value = stringRedisTemplate.opsForValue().get(key);
return "獲取到的數據:Key:" + key + ",Value:" + (value == null ? "無" : value);
}
// 存儲Hash數據
@PostMapping("/hash/set")
public String setHash(@RequestParam String key,
@RequestParam Map<String, String> hashMap) {
stringRedisTemplate.opsForHash().putAll(key, hashMap);
return "Hash數據存儲成功!Key:" + key;
}
// 獲取Hash數據
@GetMapping("/hash/get/{key}")
public Map<Object, Object> getHash(@PathVariable String key) {
return stringRedisTemplate.opsForHash().entries(key);
}
// 設置過期時間
@PostMapping("/expire")
public Boolean expireKey(@RequestParam String key,
@RequestParam long timeout) {
return stringRedisTemplate.expire(key, timeout, TimeUnit.SECONDS);
}
}5.2.3 RedisTemplate 使用示例(操作對象)
定義實體類:
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
@Data
@NoArgsConstructor
@AllArgsConstructor
public class User {
private Long id;
private String name;
private Integer age;
private String gender;
}對象操作示例:
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.web.bind.annotation.*;
import java.util.concurrent.TimeUnit;
@RestController
@RequestMapping("/redis/object")
public class ObjectRedisController {
@Autowired
private RedisTemplate<String, Object> redisTemplate;
// 存儲User對象
@PostMapping("/user/set")
public String setUser(@RequestBody User user,
@RequestParam(required = false, defaultValue = "86400") long timeout) {
String key = "user:" + user.getId();
redisTemplate.opsForValue().set(key, user, timeout, TimeUnit.SECONDS);
return "User對象存儲成功!Key:" + key + ",User:" + user;
}
// 獲取User對象
@GetMapping("/user/get")
public User getUser(@RequestParam Long userId) {
String key = "user:" + userId;
return (User) redisTemplate.opsForValue().get(key);
}
// 刪除User對象
@PostMapping("/user/delete")
public String deleteUser(@RequestParam Long userId) {
String key = "user:" + userId;
Boolean isDeleted = redisTemplate.delete(key);
return isDeleted ? "User對象刪除成功!Key:" + key : "刪除失敗,Key不存在";
}
}操作說明:
- 對象存儲會自動使用配置的 Jackson 序列化器
- 獲取時會自動反序列化為 Java 對象
- 建議使用"類型:id"的鍵命名規(guī)范(如"user:123")
5.3 Spring Boot Redis緩存注解:簡化緩存開發(fā)
Spring Boot提供了@Cacheable、@CachePut、@CacheEvict等緩存注解,通過聲明式的方式快速實現緩存功能,無需手動調用RedisTemplate。這些注解底層基于Spring Cache抽象,可以與多種緩存實現(如Redis、EhCache等)無縫集成,大大簡化了緩存開發(fā)流程。
5.3.1 開啟緩存注解支持
在Spring Boot啟動類上添加@EnableCaching注解:
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cache.annotation.EnableCaching;
@SpringBootApplication
@EnableCaching // 開啟緩存注解支持,自動配置CacheManager
public class RedisSpringBootApplication {
public static void main(String[] args) {
SpringApplication.run(RedisSpringBootApplication.class, args);
}
}注意事項:
- 確保項目中已引入
spring-boot-starter-cache和spring-boot-starter-data-redis依賴 - 在application.properties/yml中配置Redis連接信息
- 默認情況下會自動配置RedisCacheManager
5.3.2 常用緩存注解詳解
(1)@Cacheable:查詢緩存
當方法被調用時,先從緩存中查詢數據。若緩存存在,則直接返回緩存數據;若緩存不存在,則執(zhí)行方法邏輯,并將結果存入緩存。
import org.springframework.cache.annotation.Cacheable;
import org.springframework.stereotype.Service;
@Service
public class UserService {
/**
* 根據用戶ID查詢用戶信息
* @param userId 用戶ID
* @return 用戶對象
*
* 緩存配置說明:
* - value="userCache": 指定緩存名稱(相當于Redis中的key前綴)
* - key="#userId": 動態(tài)生成緩存key(使用SpEL表達式獲取方法參數)
* - unless="#result == null": 當方法返回null時不緩存結果
*/
@Cacheable(value = "userCache", key = "#userId", unless = "#result == null")
public User getUserById(Long userId) {
// 模擬從數據庫查詢數據(實際開發(fā)中替換為DAO層調用)
System.out.println("從數據庫查詢用戶,userId:" + userId);
return new User(userId, "張三", 25, "男");
}
}典型應用場景:
- 高頻訪問但數據變化不頻繁的查詢,如商品詳情、用戶信息等
- 復雜計算結果的緩存,如報表數據、統(tǒng)計結果等
(2)@CachePut:更新緩存
執(zhí)行方法邏輯后,將結果存入緩存(無論緩存是否存在,都會覆蓋原有緩存),常用于數據新增或更新場景。
import org.springframework.cache.annotation.CachePut;
import org.springframework.stereotype.Service;
@Service
public class UserService {
/**
* 保存或更新用戶信息
* @param user 用戶對象
* @return 更新后的用戶對象
*
* 緩存配置說明:
* - value="userCache": 緩存名稱
* - key="#user.id": 使用用戶ID作為緩存key
* - unless="#result == null": 更新失敗不緩存
*/
@CachePut(value = "userCache", key = "#user.id", unless = "#result == null")
public User saveOrUpdateUser(User user) {
// 模擬數據庫新增/更新操作
System.out.println("數據庫新增/更新用戶,user:" + user);
return user; // 將更新后的用戶對象存入緩存
}
}使用建議:
- 方法返回值必須是需要緩存的對象
- 確保key與查詢方法@Cacheable的key一致
- 適用于insert和update操作
(3)@CacheEvict:刪除緩存
執(zhí)行方法邏輯后,刪除指定緩存,常用于數據刪除場景。
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.stereotype.Service;
@Service
public class UserService {
/**
* 刪除用戶
* @param userId 用戶ID
*
* 緩存配置說明:
* - value="userCache": 緩存名稱
* - key="#userId": 指定要刪除的緩存key
* - allEntries=false: 默認只刪除指定key的緩存
*/
@CacheEvict(value = "userCache", key = "#userId", allEntries = false)
public void deleteUser(Long userId) {
// 模擬數據庫刪除操作
System.out.println("數據庫刪除用戶,userId:" + userId);
}
/**
* 清空用戶緩存
*
* 緩存配置說明:
* - value="userCache": 緩存名稱
* - allEntries=true: 刪除該緩存名稱下的所有緩存
*/
@CacheEvict(value = "userCache", allEntries = true)
public void clearUserCache() {
System.out.println("清空用戶緩存");
}
}刪除策略選擇:
- 單條刪除:使用key指定要刪除的緩存項
- 批量刪除:設置allEntries=true清空整個緩存區(qū)域
- 可在方法執(zhí)行前刪除(beforeInvocation=true)
(4)@Caching:組合緩存注解
當一個方法需要同時使用多個緩存注解(如同時更新和刪除緩存)時,可使用@Caching組合注解。
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.cache.annotation.CachePut;
import org.springframework.cache.annotation.Caching;
import org.springframework.stereotype.Service;
@Service
public class UserService {
/**
* 更新用戶信息并清空用戶列表緩存
* @param user 用戶對象
* @return 更新后的用戶對象
*/
@Caching(
put = {
// 更新用戶緩存
@CachePut(value = "userCache", key = "#user.id")
},
evict = {
// 刪除用戶列表緩存(假設存在用戶列表緩存)
@CacheEvict(value = "userListCache", allEntries = true)
}
)
public User updateUserAndClearListCache(User user) {
// 模擬數據庫更新操作
System.out.println("更新用戶并清空用戶列表緩存,user:" + user);
return user;
}
}適用場景:
- 需要同時操作多個緩存區(qū)域
- 需要同時執(zhí)行put和evict操作
- 復雜的緩存策略組合
六、Redis 進階:持久化、高可用與性能優(yōu)化
在實際生產環(huán)境中,僅掌握基礎使用還不夠,還需深入理解 Redis 的持久化機制、高可用架構配置及性能優(yōu)化技巧,確保 Redis 服務穩(wěn)定高效運行。本章將詳細介紹這些關鍵內容。
6.1 Redis 持久化機制
Redis 提供兩種持久化方式,可根據業(yè)務需求選擇單獨使用或組合使用,確保數據安全性和系統(tǒng)可靠性。
6.1.1 RDB(Redis Database):快照持久化
原理:在指定時間間隔內,將 Redis 內存中的數據生成二進制快照文件(.rdb 文件)并保存到磁盤。這種機制類似于數據庫的全量備份。
觸發(fā)方式:
- 手動觸發(fā):
save命令:阻塞 Redis 主線程直到快照完成,期間無法處理其他請求(生產環(huán)境不推薦)bgsave命令:Redis 會 fork 一個子進程進行快照操作,主進程繼續(xù)提供服務(推薦方式)
- 自動觸發(fā):
- 通過 redis.conf 配置文件中的 save 指令設置觸發(fā)條件
- 當 Redis 正常關閉時(shutdown命令)也會自動執(zhí)行 RDB 持久化
典型配置示例:
# 900秒(15分鐘)內至少1個key變化,觸發(fā)RDB save 900 1 # 300秒(5分鐘)內至少10個key變化,觸發(fā)RDB save 300 10 # 60秒內至少10000個key變化,觸發(fā)RDB save 60 10000 # RDB文件名稱 dbfilename dump.rdb # 工作目錄(保存RDB文件和AOF文件的目錄) dir /var/lib/redis
優(yōu)缺點分析:
- 優(yōu)點:
- 快照文件體積小,占用空間少
- 恢復速度快,特別適合大規(guī)模數據恢復
- 對 Redis 性能影響小,適合定期備份
- 缺點:
- 可能丟失最后一次快照后的所有數據(如 Redis 異常崩潰時)
- 當數據量很大時,fork 子進程的過程可能耗時較長,導致短暫的服務停頓
6.1.2 AOF(Append Only File):日志持久化
原理:將 Redis 的每一條寫命令(如 SET、HSET)以協(xié)議文本格式追加到 AOF 日志文件中,Redis 重啟時通過重新執(zhí)行日志中的命令來重建數據。
AOF 工作流程:
- 命令執(zhí)行后寫入 AOF 緩沖區(qū)
- 根據配置的刷盤策略將緩沖區(qū)內容同步到磁盤
- 定期進行 AOF 重寫(rewrite)壓縮文件體積
配置詳解:
# 開啟AOF(默認關閉,需手動設置為yes) appendonly yes # AOF文件名稱 appendfilename "appendonly.aof" # AOF刷盤策略(關鍵配置) # everysec:每秒刷盤一次(推薦,平衡性能和數據安全性) # always:每執(zhí)行一條命令刷盤一次(數據零丟失,性能較差) # no:由操作系統(tǒng)決定刷盤時機(性能最好,數據丟失風險最高) appendfsync everysec # AOF重寫觸發(fā)條件 # 當前AOF文件大小比起最后一次重寫時的大小增長率達到100%時觸發(fā) auto-aof-rewrite-percentage 100 # AOF文件最小重寫大小,避免文件很小就重寫 auto-aof-rewrite-min-size 64mb # 加載AOF時發(fā)現文件末尾不完整時的處理方式 aof-load-truncated yes
AOF 重寫機制:
- 原理:創(chuàng)建一個新的 AOF 文件,包含重建當前數據集所需的最少命令集
- 觸發(fā)方式:
- 自動觸發(fā):根據 auto-aof-rewrite-percentage 和 auto-aof-rewrite-min-size 配置
- 手動觸發(fā):執(zhí)行
BGREWRITEAOF命令
優(yōu)缺點分析:
- 優(yōu)點:
- 數據安全性高,最多丟失1秒數據(everysec配置下)
- AOF 文件可讀性強,便于問題排查
- 通過重寫機制可以壓縮文件大小
- 缺點:
- AOF 文件通常比 RDB 文件大
- 恢復速度比 RDB 慢
- 在高寫入負載下,AOF 可能影響 Redis 性能
6.1.3 RDB 與 AOF 組合使用
生產環(huán)境中推薦同時開啟 RDB 和 AOF,充分發(fā)揮兩者的優(yōu)勢:
- 數據恢復策略:
- 日常恢復:優(yōu)先使用 AOF 恢復(數據更完整)
- 大規(guī)模數據恢復:若 AOF 文件過大,先使用 RDB 快速恢復基礎數據,再通過 AOF 補充后續(xù)數據
- 典型配置組合:
- RDB 配置為每天全量備份一次
- AOF 配置為每秒刷盤
- 定期將 RDB 文件和 AOF 文件備份到異地
- 注意事項:
- Redis 4.0+ 支持混合持久化(aof-use-rdb-preamble),AOF 文件前半部分是 RDB 格式,后半部分是 AOF 格式,可以結合兩者優(yōu)點
- 需要監(jiān)控磁盤空間使用情況,避免持久化文件占滿磁盤
6.2 Redis 高可用配置
為避免 Redis 單點故障,需搭建高可用架構,確保服務持續(xù)可用。Redis 提供了多種高可用方案,適用于不同場景。
6.2.1 主從復制(Master-Slave)
原理:將一臺 Redis 服務器(Master)的數據同步到多臺 Redis 服務器(Slave),實現數據冗余和讀寫分離。
核心特性:
- 異步復制:Slave 異步從 Master 同步數據
- 讀寫分離:Master 處理寫請求,Slave 處理讀請求
- 級聯(lián)復制:Slave 可以作為其他 Slave 的 Master
配置步驟(以 1 主 2 從為例):
- 準備 3 臺 Redis 服務器(假設IP為192.168.1.100-102)
- 配置 Master(192.168.1.100): 確保 redis.conf 中有以下配置:
# 如果需要密碼認證 requirepass your_password
- 配置 Slave(192.168.1.101 和 192.168.1.102):
# 指定Master的地址和端口 replicaof 192.168.1.100 6379 # 若Master設置了密碼,需配置認證密碼 masterauth your_password # 可選:設置Slave只讀 replica-read-only yes
- 驗證主從狀態(tài):
- 在 Master 上執(zhí)行
info replication查看 Slave 連接信息 - 在 Slave 上執(zhí)行
info replication查看復制狀態(tài)
- 在 Master 上執(zhí)行
主從復制過程:
- Slave 啟動后向 Master 發(fā)送 SYNC 命令
- Master 執(zhí)行 BGSAVE 生成 RDB 文件并發(fā)送給 Slave
- Master 將生成 RDB 期間的新命令緩存起來,之后發(fā)送給 Slave
- 之后 Master 將所有寫命令異步發(fā)送給 Slave
注意事項:
- 復制延遲問題:網絡延遲或 Slave 負載過高可能導致數據不一致
- 數據過期問題:Redis 的過期策略在主從模式下有特殊處理
- 復制中斷處理:可通過
replica-serve-stale-data yes配置Slave在斷開連接時是否繼續(xù)提供服務
6.2.2 哨兵模式(Sentinel)
原理:在主從復制的基礎上,增加哨兵進程監(jiān)控節(jié)點狀態(tài),實現自動故障轉移。
哨兵核心功能:
- 監(jiān)控:持續(xù)檢查 Master 和 Slave 是否正常運行
- 通知:當監(jiān)控的 Redis 實例出現問題時,可以通過API通知管理員
- 自動故障轉移:當 Master 不可用時,自動選舉新的 Master
- 配置提供者:為客戶端提供最新的 Master 地址
配置步驟(3個哨兵節(jié)點):
- 準備哨兵配置文件 sentinel.conf:
port 26379 sentinel monitor mymaster 192.168.1.100 6379 2 sentinel auth-pass mymaster your_password sentinel down-after-milliseconds mymaster 30000 sentinel parallel-syncs mymaster 1 sentinel failover-timeout mymaster 180000
- 啟動哨兵:
redis-sentinel /path/to/sentinel.conf
- 驗證哨兵狀態(tài):
redis-cli -p 26379 info sentinel
故障轉移流程:
- 哨兵檢測到 Master 不可達(超過 down-after-milliseconds 時間)
- 哨兵集群選舉領導者哨兵(需要至少 quorum 數量的哨兵同意)
- 領導者哨兵從 Slave 中選擇新的 Master(基于優(yōu)先級、復制偏移量等)
- 將其他 Slave 重新配置為復制新的 Master
- 通知客戶端配置變更
客戶端訪問哨兵模式的最佳實踐:
- 客戶端應連接哨兵獲取當前 Master 地址
- 客戶端應訂閱哨兵的
+switch-master事件處理主從切換 - 建議使用支持哨兵的 Redis 客戶端庫(如Jedis、Lettuce)
6.2.3 Redis Cluster(集群)
原理:將數據分片存儲到多個節(jié)點,每個節(jié)點負責一部分哈希槽(共16384個槽),同時每個分片可配置主從復制。
集群特性:
- 數據分片:自動將數據分布到多個節(jié)點
- 高可用:每個分片可以配置主從復制
- 線性擴展:可通過增加節(jié)點擴展集群容量和性能
- 客戶端路由:客戶端可直接連接正確的節(jié)點
配置步驟(3主3從集群):
- 準備6臺服務器(192.168.1.100-105),確保端口6379和16379開放
- 每個節(jié)點的 redis.conf 配置:
cluster-enabled yes cluster-config-file nodes-6379.conf cluster-node-timeout 15000 cluster-announce-ip 192.168.1.100 # 每臺填寫自己的IP cluster-announce-port 6379 cluster-announce-bus-port 16379
- 啟動所有節(jié)點后,創(chuàng)建集群:
redis-cli --cluster create \ 192.168.1.100:6379 192.168.1.101:6379 192.168.1.102:6379 \ 192.168.1.103:6379 192.168.1.104:6379 192.168.1.105:6379 \ --cluster-replicas 1
- 驗證集群狀態(tài):
redis-cli -c -h 192.168.1.100 -p 6379 cluster info redis-cli -c -h 192.168.1.100 -p 6379 cluster nodes
數據分片原理:
- Redis 使用 CRC16(key) mod 16384 計算 key 所屬的槽位
- 每個節(jié)點負責一部分連續(xù)的槽位
- 客戶端可以緩存槽位到節(jié)點的映射關系
集群管理命令:
cluster meet:將節(jié)點加入集群cluster addslots:分配槽位給節(jié)點cluster replicate:設置節(jié)點為從節(jié)點cluster failover:手動觸發(fā)故障轉移
集群擴展操作:
- 添加新節(jié)點:
redis-cli --cluster add-node new_host:new_port existing_host:existing_port
- 遷移槽位:
redis-cli --cluster reshard host:port
- 刪除節(jié)點:
redis-cli --cluster del-node host:port node_id
6.3 Redis 性能優(yōu)化建議
6.3.1 硬件優(yōu)化
內存優(yōu)化:
- 為 Redis 分配足夠的內存,避免頻繁交換
- 使用
maxmemory限制 Redis 最大內存使用量 - 考慮使用大頁內存(transparent huge pages)
CPU優(yōu)化:
- Redis 6.0+ 支持多線程IO(I/O threading),可配置:
io-threads 4 io-threads-do-reads yes
- 在高并發(fā)場景下,可考慮使用多個 Redis 實例分擔負載
磁盤優(yōu)化:
- 使用 SSD 存儲持久化文件
- 確保磁盤有足夠的寫入帶寬
- 對于 AOF,可以考慮使用
no-appendfsync-on-rewrite yes減少重寫時的磁盤壓力
6.3.2 配置優(yōu)化
內存管理配置:
# 內存淘汰策略(當內存達到maxmemory時) maxmemory-policy allkeys-lru # 設置最大內存(例如4GB) maxmemory 4gb # 避免內存碎片 activedefrag yes
網絡配置優(yōu)化:
# 提高TCP連接隊列大小 tcp-backlog 511 # 客戶端空閑超時時間 timeout 0 # 0表示不超時 # 最大客戶端連接數 maxclients 10000
持久化優(yōu)化:
# 禁用持久化(僅用于純緩存場景) save "" appendonly no # 如果使用RDB,適當調整save參數 save 3600 1 # 1小時內至少有1個變化則保存
6.3.3 開發(fā)優(yōu)化
鍵設計原則:
- 使用簡潔但有意義的鍵名,如
user:1000:profile - 避免使用大鍵(如包含百萬元素的hash)
- 為鍵設置合理的過期時間
命令優(yōu)化:
- 使用批量操作命令:
MSET替代多個SETHMGET替代多個HGET
- 使用管道(pipeline)減少網絡往返:
pipeline = redis.pipeline() pipeline.set('key1', 'value1') pipeline.set('key2', 'value2') pipeline.execute() - 避免阻塞命令:
- 避免在生產環(huán)境使用
KEYS,改用SCAN - 謹慎使用
FLUSHALL/FLUSHDB
- 避免在生產環(huán)境使用
客戶端優(yōu)化:
- 使用連接池管理連接
- 合理設置連接池大小
- 實現客戶端緩存(Redis 6.0+的客戶端緩存功能)
監(jiān)控與調優(yōu):
- 定期檢查
info命令輸出 - 監(jiān)控慢查詢(
slowlog get) - 使用
redis-benchmark進行性能測試 - 考慮使用Redis的Lua腳本減少網絡開銷
到此這篇關于在Java中整合Redis的詳細過程的文章就介紹到這了,更多相關java整合redis內容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!
相關文章
SpringBoot配置Actuator組件,實現系統(tǒng)監(jiān)控
在生產環(huán)境中,需要實時或定期監(jiān)控服務的可用性。Spring Boot的actuator(健康監(jiān)控)功能提供了很多監(jiān)控所需的接口,可以對應用系統(tǒng)進行配置查看、相關功能統(tǒng)計等。2021-06-06
新版IDEA使用Spring Initializr創(chuàng)建工程的兩種方法
這篇文章主要介紹了新版IDEA使用Spring Initializr創(chuàng)建工程(兩種方法,官方工具和IDEA),文中通過代碼示例和圖文結合的方式給大家講解的非常詳細,具有一定的參考價值,需要的朋友可以參考下2024-10-10
用SpringBoot框架來接收multipart/form-data文件方式
這篇文章主要介紹了用SpringBoot框架來接收multipart/form-data文件方式,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2023-02-02
Java之HashMap.values()轉List時的錯誤和正確演示
這篇文章主要介紹了Java之HashMap.values()轉List時的錯誤和正確演示,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2023-03-03
Springboot 自定義線程池的參數配置最優(yōu)小結
在SpringBoot中配置自定義線程池時,參數的設置需要根據具體的應用場景、系統(tǒng)資源和業(yè)務需求來調整,本文就來詳細的介紹一下,感興趣的可以了解一下2025-10-10

