Redis 作為高性能鍵值存儲,是現代架構中緩存、會話、分佈式鎖、消息隊列的核心組件。本文基於 Spring Boot 2.x + Jedis 3.x,系統講解 Maven 依賴、YAML 配置、分片連接池 Bean 裝配、RedisService 統一資源管理、RedisClient 全 API 封裝 (String/Hash/List/Set/ZSet/Key/事務/管道/發布訂閱)、異常處理與資源歸還最佳實踐,附完整可運行代碼。
<dependency>
<groupId>redis.clients</groupId>
<artifactId>jedis</artifactId>
<version>3.7.0</version> <!-- Spring Boot 2.x 推薦 3.x -->
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency> <!-- 可選:Spring Data Redis 抽象層 -->
spring:
redis:
host: 192.168.56.101
port: 6379
password: lxh20160330
timeout: 2000ms
jedis:
pool:
max-active: 200 # 最大連接數
max-idle: 50 # 最大空閒連接
min-idle: 10 # 最小空閒連接
max-wait: 1000ms # 獲取連接最大等待
database: 0 # 默認 DB
# 哨兵/集群模式配置示例
# sentinel:
# master: mymaster
# nodes: 192.168.1.1:26379,192.168.1.2:26379
# cluster:
# nodes: 192.168.1.1:7001,192.168.1.2:7002
@Configuration
public class RedisConfig {
@Value("${spring.redis.host}") private String host;
@Value("${spring.redis.port}") private int port;
@Value("${spring.redis.password}") private String password;
@Value("${spring.redis.timeout}") private int timeout;
@Value("${spring.redis.jedis.pool.max-active}") private int maxActive;
@Value("${spring.redis.jedis.pool.max-idle}") private int maxIdle;
@Value("${spring.redis.jedis.pool.max-wait}") private long maxWait;
@Bean
public JedisPoolConfig jedisPoolConfig() {
JedisPoolConfig config = new JedisPoolConfig();
config.setMaxTotal(maxActive);
config.setMaxIdle(maxIdle);
config.setMinIdle(10);
config.setMaxWaitMillis(maxWait);
config.setTestOnBorrow(true);
config.setTestOnReturn(true);
return config;
}
@Bean
public ShardedJedisPool shardedJedisPool(JedisPoolConfig config) {
JedisShardInfo shardInfo = new JedisShardInfo(host, port, timeout);
shardInfo.setPassword(password);
shardInfo.setWeight(1);
return new ShardedJedisPool(config, Collections.singletonList(shardInfo));
}
}
@Repository
public class RedisService {
private static final Logger log = LoggerFactory.getLogger(RedisService.class);
@Autowired private ShardedJedisPool shardedJedisPool;
public ShardedJedis getResource() {
try {
return shardedJedisPool.getResource();
} catch (Exception e) {
log.error("獲取 Redis 連接失敗", e);
return null;
}
}
public void returnResource(ShardedJedis jedis) {
if (jedis != null) jedis.close(); // Jedis 3.x close() 歸還連接池
}
public void returnBroken(ShardedJedis jedis) {
if (jedis != null) {
try { jedis.close(); } catch (Exception ignored) {}
}
}
}
關鍵點:Jedis 3.x close() 即歸還連接池,不再區分 returnResource/returnBrokenResource。異常時必須 close() 避免洩漏。
@Component
public class RedisClient {
private static final Logger log = LoggerFactory.getLogger(RedisClient.class);
@Autowired private RedisService redisService;
// ========== 通用 Key 操作 ==========
public Boolean exists(String key) { return execute(jedis -> jedis.exists(key)); }
public Long expire(String key, long seconds) { return execute(jedis -> jedis.expire(key, seconds)); }
public String type(String key) { return execute(jedis -> jedis.type(key)); }
public Long ttl(String key) { return execute(jedis -> jedis.ttl(key)); }
public Long del(String... keys) { return execute(jedis -> jedis.del(keys)); }
// ========== String (鍵值) ==========
public String set(String key, String value) { return execute(jedis -> jedis.set(key, value)); }
public String get(String key) { return execute(jedis -> jedis.get(key)); }
public String getSet(String key, String value) { return execute(jedis -> jedis.getSet(key, value)); }
public Long setnx(String key, String value) { return execute(jedis -> jedis.setnx(key, value)); } // 分佈式鎖基石
public String setex(String key, int seconds, String value) { return execute(jedis -> jedis.setex(key, seconds, value)); }
public Long incr(String key) { return execute(jedis -> jedis.incr(key)); } // 計數器
public Long decr(String key) { return execute(jedis -> jedis.decr(key)); }
public Long append(String key, String value) { return execute(jedis -> jedis.append(key, value)); }
// ========== Hash (對象映射) ==========
public Long hset(String key, String field, String value) { return execute(jedis -> jedis.hset(key, field, value)); }
public String hget(String key, String field) { return execute(jedis -> jedis.hget(key, field)); }
public Map<String, String> hgetAll(String key) { return execute(jedis -> jedis.hgetAll(key)); }
public Long hdel(String key, String... fields) { return execute(jedis -> jedis.hdel(key, fields)); }
public Long hlen(String key) { return execute(jedis -> jedis.hlen(key)); }
public Set<String> hkeys(String key) { return execute(jedis -> jedis.hkeys(key)); }
public List<String> hvals(String key) { return execute(jedis -> jedis.hvals(key)); }
public Long hincrBy(String key, String field, long value) { return execute(jedis -> jedis.hincrBy(key, field, value)); }
// ========== List (隊列/棧/消息隊列) ==========
public Long lpush(String key, String... values) { return execute(jedis -> jedis.lpush(key, values)); }
public Long rpush(String key, String... values) { return execute(jedis -> jedis.rpush(key, values)); }
public String lpop(String key) { return execute(jedis -> jedis.lpop(key)); }
public String rpop(String key) { return execute(jedis -> jedis.rpop(key)); }
public List<String> lrange(String key, long start, long end) { return execute(jedis -> jedis.lrange(key, start, end)); }
public Long llen(String key) { return execute(jedis -> jedis.llen(key)); }
public String lindex(String key, long index) { return execute(jedis -> jedis.lindex(key, index)); }
public Long lrem(String key, long count, String value) { return execute(jedis -> jedis.lrem(key, count, value)); }
// ========== Set (去重/標籤/交並差集) ==========
public Long sadd(String key, String... members) { return execute(jedis -> jedis.sadd(key, members)); }
public Set<String> smembers(String key) { return execute(jedis -> jedis.smembers(key)); }
public Long srem(String key, String... members) { return execute(jedis -> jedis.srem(key, members)); }
public String spop(String key) { return execute(jedis -> jedis.spop(key)); }
public Long scard(String key) { return execute(jedis -> jedis.scard(key)); }
public Boolean sismember(String key, String member) { return execute(jedis -> jedis.sismember(key, member)); }
public Set<String> sinter(String... keys) { return execute(jedis -> jedis.sinter(keys)); }
public Set<String> sunion(String... keys) { return execute(jedis -> jedis.sunion(keys)); }
public Set<String> sdiff(String... keys) { return execute(jedis -> jedis.sdiff(keys)); }
// ========== ZSet (排行榜/延遲隊列/速率限流) ==========
public Long zadd(String key, double score, String member) { return execute(jedis -> jedis.zadd(key, score, member)); }
public Set<String> zrange(String key, long start, long end) { return execute(jedis -> jedis.zrange(key, start, end)); }
public Set<Tuple> zrangeWithScores(String key, long start, long end) { return execute(jedis -> jedis.zrangeWithScores(key, start, end)); }
public Long zrem(String key, String... members) { return execute(jedis -> jedis.zrem(key, members)); }
public Double zincrby(String key, double score, String member) { return execute(jedis -> jedis.zincrby(key, score, member)); }
public Long zrank(String key, String member) { return execute(jedis -> jedis.zrank(key, member)); }
public Long zcard(String key) { return execute(jedis -> jedis.zcard(key)); }
public Double zscore(String key, String member) { return execute(jedis -> jedis.zscore(key, member)); }
public Set<String> zrangeByScore(String key, double min, double max) { return execute(jedis -> jedis.zrangeByScore(key, min, max)); }
public Long zremrangeByScore(String key, double min, double max) { return execute(jedis -> jedis.zremrangeByScore(key, min, max)); }
// ========== 事務與管道 (高性能批量) ==========
public List<Object> execTransaction(Consumer<Transaction> action) {
return execute(jedis -> {
Transaction tx = jedis.multi();
action.accept(tx);
return tx.exec();
});
}
public List<Object> execPipeline(Consumer<Pipeline> action) {
return execute(jedis -> {
Pipeline pipe = jedis.pipelined();
action.accept(pipe);
return pipe.syncAndReturnAll();
});
}
// ========== 通用執行模板 (資源管理+異常處理) ==========
private T execute(Function<ShardedJedis, T> fn) {
ShardedJedis jedis = redisService.getResource();
if (jedis == null) return null;
try {
return fn.apply(jedis);
} catch (Exception e) {
log.error("Redis 操作異常: {}", e.getMessage(), e);
redisService.returnBroken(jedis);
return null;
} finally {
if (jedis != null) redisService.returnResource(jedis);
}
}
}
public boolean tryLock(String lockKey, String requestId, int expireSeconds) {
String lua = "if redis.call('setnx', KEYS[1], ARGV[1]) == 1 then redis.call('expire', KEYS[1], ARGV[2]) return 1 else return 0 end";
return execute(jedis -> (Long) jedis.eval(lua, Collections.singletonList(lockKey), Arrays.asList(requestId, String.valueOf(expireSeconds))) == 1);
}
public boolean releaseLock(String lockKey, String requestId) {
String lua = "if redis.call('get', KEYS[1]) == ARGV[1] then return redis.call('del', KEYS[1]) else return 0 end";
return execute(jedis -> (Long) jedis.eval(lua, Collections.singletonList(lockKey), Collections.singletonList(requestId)) == 1);
}
public void batchInsert(Map<String, String> data) {
execPipeline(pipe -> data.forEach(pipe::set));
}
maxTotal = 極峰 QPS × 平均耗時(秒) × 1.5testOnBorrow=true、testOnReturn=true 防止死連接connected_clients、used_memory、keyspace_hits/misses、rejected_connectionsSCAN 游標迭代Spring Boot + Jedis 整合的核心在於 分片連接池配置、統一資源管理模板、全 API 類型安全封裝。掌握 String/Hash/List/Set/ZSet 五大數據結構的業務映射 (緩存/計數器/隊列/標籤/排行榜),配合事務/管道/Lua 腳本解決原子性與批量性能問題,即可構建高可用、高性能的 Redis 應用層。下一篇我們將深入 MyEclipse/IDEA 查看 JDK/第三方 JAR 源碼的多種姿勢。