Redis 作為高性能鍵值存儲,Java 客戶端 Jedis/Lettuce 是後端開發必備技能。本文補全原文缺失內容,系統講解連接池配置、數據結構操作、序列化策略、管道優化、Lua 腳本原子性、集群模式等生產級最佳實踐。
| 客戶端 | 特點 | 適用場景 |
|---|---|---|
| Jedis | 阻塞 API、功能全、社區成熟 | 傳統同步業務、單機/哨兵模式 |
| Lettuce | 非阻塞 Netty、線程安全、支援響應式 | 高併發、集群模式、Spring WebFlux |
| Redisson | 分布式鎖、對象映射、豐富數據結構 | 分布式協調、複雜業務建模 |
<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>
JedisPoolConfig poolConfig = new JedisPoolConfig();
poolConfig.setMaxTotal(200); // 最大連接數
poolConfig.setMaxIdle(50); // 最大閒置連接
poolConfig.setMinIdle(10); // 最小閒置連接
poolConfig.setMaxWaitMillis(3000); // 獲取連接超時
poolConfig.setTestOnBorrow(true); // 借用時驗證
poolConfig.setTestOnReturn(true); // 歸還時驗證
poolConfig.setTestWhileIdle(true); // 空閒驗證
poolConfig.setTimeBetweenEvictionRunsMillis(30000);
JedisPool jedisPool = new JedisPool(poolConfig, "localhost", 6379, 2000, "password", 0);
spring:
redis:
host: localhost
port: 6379
password: your_password
database: 0
lettuce:
pool:
max-active: 200
max-idle: 50
min-idle: 10
max-wait: 3000ms
timeout: 2000ms
try (Jedis jedis = jedisPool.getResource()) {
// String
jedis.set("user:1001", "{\"name\":\"Tom\",\"age\":28}");
String json = jedis.get("user:1001");
// Hash(對象映射首選)
jedis.hset("user:1001", "name", "Tom");
jedis.hset("user:1001", "age", "28");
Map<String, String> user = jedis.hgetAll("user:1001");
// List(隊列/棧)
jedis.lpush("task:queue", "task1", "task2");
String task = jedis.rpop("task:queue");
// Set(去重/標籤)
jedis.sadd("tags:java", "backend", "jvm", "spring");
Set<String> tags = jedis.smembers("tags:java");
// Sorted Set(排行榜/延遲隊列)
jedis.zadd("rank:score", 95.5, "userA");
jedis.zadd("rank:score", 88.0, "userB");
Set<Tuple> top10 = jedis.zrevrangeWithScores("rank:score", 0, 9);
// 過期時間
jedis.expire("session:abc", 1800); // 30分鐘
long ttl = jedis.ttl("session:abc");
}
| 方案 | 優點 | 缺點 | 適用場景 |
|---|---|---|---|
| JDK 原生 | 無依賴、支援任意對象 | 體積大、不可讀、版本敏感 | 簡單臨時數據 |
| JSON (Jackson/Gson) | 可讀、跨語言、版本兼容 | 體積中等、反序列化較慢 | 通用業務數據、API 緩存 |
| Protobuf/MessagePack | 體積極小、速度快、Schema 演進 | 需定義 .proto、學習成本 | 高性能、大對象、跨服務調用 |
| Kryo/FST | Java 專用、極快、無需接口 | 跨語言差、版本遷移複雜 | 高頻緩存、會話存儲 |
ObjectMapper mapper = new ObjectMapper();
mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
String json = mapper.writeValueAsString(user);
User u = mapper.readValue(json, User.class);
管道將多條命令打包發送,減少網絡 RTT,非事務性(不保證原子性)。
// 批量寫入 10萬條用戶緩存
try (Jedis jedis = jedisPool.getResource()) {
Pipeline pipeline = jedis.pipelined();
for (int i = 0; i < 100000; i++) {
pipeline.set("user:" + i, "data" + i);
if (i % 1000 == 0) pipeline.sync(); // 定期刷新防止客戶端緩衝區 OOM
}
pipeline.sync();
}
// 批量讀取
List<Object> results = jedis.pipelined(pipe -> {
for (String key : keys) pipe.get(key);
}).syncAndReturnAll();
Redis 單線程執行 Lua 腳本,天然原子,替代分布式鎖實現「檢查-設置」模式。
-- limit.lua
local key = KEYS[1]
local limit = tonumber(ARGV[1])
local window = tonumber(ARGV[2])
local current = redis.call('GET', key)
if current and tonumber(current) >= limit then
return redis.call('TTL', key)
end
local incr = redis.call('INCR', key)
if incr == 1 then
redis.call('EXPIRE', key, window)
end
return 0
String script = new String(Files.readAllBytes(Paths.get("limit.lua")));
DefaultRedisScript<Long> redisScript = new DefaultRedisScript<>(script, Long.class);
try (Jedis jedis = jedisPool.getResource()) {
Long ttl = (Long) jedis.eval(redisScript.getScriptAsString(),
Collections.singletonList("rate:limit:user:1001"), "100", "60");
if (ttl > 0) throw new RateLimitException("請求過於頻繁," + ttl + "秒後重試");
}
// JedisCluster 最少 3 主 3 從
Set<HostAndPort> nodes = new HashSet<>();
nodes.add(new HostAndPort("192.168.1.10", 7001));
nodes.add(new HostAndPort("192.168.1.11", 7001));
nodes.add(new HostAndPort("192.168.1.12", 7001));
JedisCluster cluster = new JedisCluster(nodes, 3000, 3000, 3, "password", poolConfig);
// 只能操作單鍵或同槽位鍵
cluster.set("user:1001", "data");
// cluster.mget("a", "b") // 報錯:CROSSSLOT Keys don't hash to same slot
// 強制同槽位:Hash Tags
cluster.set("{user:1001}:profile", "...");
cluster.set("{user:1001}:settings", "...");
SCAN 游標迭代redis-cli --hotkeys 或 Proxy 層統計