Java Redis 客户端概述
Java 生态系统中有多个优秀的 Redis 客户端库,每个都有其特点和适用场景。选择合适的客户端对于项目的性能和开发效率至关重要。
🔧 Jedis
Redis 官方推荐的 Java 客户端,简单易用,API 直观。
✅ 优点
- API 简单直观
- 官方支持
- 文档完善
- 社区活跃
❌ 缺点
- 线程不安全
- 功能相对简单
- 需要连接池管理
🚀 Lettuce
基于 Netty 的异步 Redis 客户端,支持响应式编程。
✅ 优点
- 线程安全
- 支持异步操作
- 响应式编程
- 连接复用
❌ 缺点
- 学习曲线较陡
- 配置复杂
- 依赖较多
⚡ Redisson
功能丰富的 Redis Java 客户端,提供分布式对象和服务。
✅ 优点
- 功能丰富
- 分布式锁
- 集合操作
- 高级特性
❌ 缺点
环境准备
Maven 依赖
Jedis 使用详解
基础连接和操作
import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPool;
import redis.clients.jedis.JedisPoolConfig;
public class JedisExample {
public static void main(String[] args) {
// 创建 Jedis 连接
Jedis jedis = new Jedis("localhost", 6379);
try {
// 测试连接
String pong = jedis.ping();
System.out.println("连接测试: " + pong);
// 字符串操作
jedis.set("name", "张三");
String name = jedis.get("name");
System.out.println("姓名: " + name);
// 设置过期时间
jedis.setex("session:123", 3600, "user_data");
// 哈希操作
jedis.hset("user:1001", "name", "李四");
jedis.hset("user:1001", "age", "25");
jedis.hset("user:1001", "city", "北京");
String userName = jedis.hget("user:1001", "name");
System.out.println("用户名: " + userName);
// 列表操作
jedis.lpush("tasks", "任务1", "任务2", "任务3");
String task = jedis.rpop("tasks");
System.out.println("取出任务: " + task);
// 集合操作
jedis.sadd("tags", "Java", "Redis", "Spring");
boolean isMember = jedis.sismember("tags", "Java");
System.out.println("是否包含Java: " + isMember);
// 有序集合操作
jedis.zadd("leaderboard", 1500, "player1");
jedis.zadd("leaderboard", 2000, "player2");
jedis.zadd("leaderboard", 1800, "player3");
// 获取排行榜前3名
var topPlayers = jedis.zrevrange("leaderboard", 0, 2);
System.out.println("排行榜前3名: " + topPlayers);
} finally {
jedis.close();
}
}
}
连接池配置
import redis.clients.jedis.JedisPool;
import redis.clients.jedis.JedisPoolConfig;
import redis.clients.jedis.Jedis;
public class JedisPoolExample {
private static JedisPool jedisPool;
static {
// 连接池配置
JedisPoolConfig config = new JedisPoolConfig();
config.setMaxTotal(100); // 最大连接数
config.setMaxIdle(20); // 最大空闲连接数
config.setMinIdle(5); // 最小空闲连接数
config.setMaxWaitMillis(3000); // 最大等待时间
config.setTestOnBorrow(true); // 获取连接时测试
config.setTestOnReturn(true); // 归还连接时测试
config.setTestWhileIdle(true); // 空闲时测试
// 创建连接池
jedisPool = new JedisPool(config, "localhost", 6379, 2000);
}
public static Jedis getJedis() {
return jedisPool.getResource();
}
public static void closePool() {
if (jedisPool != null) {
jedisPool.close();
}
}
// 使用示例
public static void example() {
Jedis jedis = getJedis();
try {
jedis.set("pool:test", "连接池测试");
String value = jedis.get("pool:test");
System.out.println(value);
} finally {
jedis.close(); // 归还连接到池中
}
}
}
工具类封装
public class RedisUtil {
private static final JedisPool jedisPool;
static {
JedisPoolConfig config = new JedisPoolConfig();
config.setMaxTotal(100);
config.setMaxIdle(20);
config.setMinIdle(5);
config.setMaxWaitMillis(3000);
config.setTestOnBorrow(true);
jedisPool = new JedisPool(config, "localhost", 6379);
}
/**
* 执行 Redis 操作
*/
public static T execute(Function action) {
Jedis jedis = jedisPool.getResource();
try {
return action.apply(jedis);
} finally {
jedis.close();
}
}
/**
* 设置字符串值
*/
public static void set(String key, String value) {
execute(jedis -> {
jedis.set(key, value);
return null;
});
}
/**
* 获取字符串值
*/
public static String get(String key) {
return execute(jedis -> jedis.get(key));
}
/**
* 设置带过期时间的值
*/
public static void setex(String key, int seconds, String value) {
execute(jedis -> {
jedis.setex(key, seconds, value);
return null;
});
}
/**
* 删除键
*/
public static void del(String key) {
execute(jedis -> jedis.del(key));
}
/**
* 检查键是否存在
*/
public static boolean exists(String key) {
return execute(jedis -> jedis.exists(key));
}
}
集群模式配置
import redis.clients.jedis.JedisCluster;
import redis.clients.jedis.HostAndPort;
import redis.clients.jedis.JedisPoolConfig;
import java.util.HashSet;
import java.util.Set;
public class JedisClusterExample {
private static JedisCluster jedisCluster;
static {
// 集群节点
Set nodes = new HashSet<>();
nodes.add(new HostAndPort("192.168.1.100", 7000));
nodes.add(new HostAndPort("192.168.1.100", 7001));
nodes.add(new HostAndPort("192.168.1.101", 7000));
nodes.add(new HostAndPort("192.168.1.101", 7001));
nodes.add(new HostAndPort("192.168.1.102", 7000));
nodes.add(new HostAndPort("192.168.1.102", 7001));
// 连接池配置
JedisPoolConfig config = new JedisPoolConfig();
config.setMaxTotal(100);
config.setMaxIdle(20);
config.setMinIdle(5);
config.setMaxWaitMillis(3000);
config.setTestOnBorrow(true);
// 创建集群连接
jedisCluster = new JedisCluster(nodes, 2000, 5000, 5, config);
}
public static void example() {
try {
// 集群操作与单机操作相同
jedisCluster.set("cluster:test", "集群测试");
String value = jedisCluster.get("cluster:test");
System.out.println("集群值: " + value);
// 哈希操作
jedisCluster.hset("cluster:user:1001", "name", "集群用户");
String name = jedisCluster.hget("cluster:user:1001", "name");
System.out.println("集群用户名: " + name);
} catch (Exception e) {
e.printStackTrace();
}
}
public static void close() {
if (jedisCluster != null) {
try {
jedisCluster.close();
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
实际应用场景
1. 缓存管理
public class CacheManager {
private static final int DEFAULT_EXPIRE = 3600; // 1小时
/**
* 缓存用户信息
*/
public static void cacheUser(Long userId, User user) {
String key = "user:" + userId;
String userJson = JSON.toJSONString(user);
RedisUtil.setex(key, DEFAULT_EXPIRE, userJson);
}
/**
* 获取缓存的用户信息
*/
public static User getUser(Long userId) {
String key = "user:" + userId;
String userJson = RedisUtil.get(key);
if (userJson != null) {
return JSON.parseObject(userJson, User.class);
}
return null;
}
/**
* 缓存查询结果
*/
public static void cacheQueryResult(String queryKey, Object result, int expireSeconds) {
String resultJson = JSON.toJSONString(result);
RedisUtil.setex(queryKey, expireSeconds, resultJson);
}
/**
* 获取缓存的查询结果
*/
public static <T> T getCachedResult(String queryKey, Class<T> clazz) {
String resultJson = RedisUtil.get(queryKey);
if (resultJson != null) {
return JSON.parseObject(resultJson, clazz);
}
return null;
}
}
2. 分布式锁实现
public class DistributedLock {
private static final String LOCK_PREFIX = "lock:";
private static final String UNLOCK_SCRIPT =
"if redis.call('get', KEYS[1]) == ARGV[1] then " +
"return redis.call('del', KEYS[1]) " +
"else return 0 end";
/**
* 获取分布式锁
*/
public static boolean tryLock(String lockKey, String requestId, int expireTime) {
String key = LOCK_PREFIX + lockKey;
return RedisUtil.execute(jedis -> {
String result = jedis.set(key, requestId, "NX", "EX", expireTime);
return "OK".equals(result);
});
}
/**
* 释放分布式锁
*/
public static boolean releaseLock(String lockKey, String requestId) {
String key = LOCK_PREFIX + lockKey;
return RedisUtil.execute(jedis -> {
Object result = jedis.eval(UNLOCK_SCRIPT,
Collections.singletonList(key),
Collections.singletonList(requestId));
return Long.valueOf(1).equals(result);
});
}
/**
* 使用锁执行业务逻辑
*/
public static <T> T executeWithLock(String lockKey, int expireTime,
Supplier<T> business) {
String requestId = UUID.randomUUID().toString();
boolean locked = tryLock(lockKey, requestId, expireTime);
if (!locked) {
throw new RuntimeException("获取锁失败: " + lockKey);
}
try {
return business.get();
} finally {
releaseLock(lockKey, requestId);
}
}
}
3. 会话管理
public class SessionManager {
private static final String SESSION_PREFIX = "session:";
private static final int SESSION_TIMEOUT = 1800; // 30分钟
/**
* 创建会话
*/
public static String createSession(Long userId) {
String sessionId = UUID.randomUUID().toString();
String key = SESSION_PREFIX + sessionId;
Map<String, String> sessionData = new HashMap<>();
sessionData.put("userId", userId.toString());
sessionData.put("createTime", String.valueOf(System.currentTimeMillis()));
sessionData.put("lastAccessTime", String.valueOf(System.currentTimeMillis()));
RedisUtil.execute(jedis -> {
jedis.hmset(key, sessionData);
jedis.expire(key, SESSION_TIMEOUT);
return null;
});
return sessionId;
}
/**
* 获取会话信息
*/
public static Map<String, String> getSession(String sessionId) {
String key = SESSION_PREFIX + sessionId;
return RedisUtil.execute(jedis -> {
Map<String, String> session = jedis.hgetAll(key);
if (!session.isEmpty()) {
// 更新最后访问时间
jedis.hset(key, "lastAccessTime", String.valueOf(System.currentTimeMillis()));
jedis.expire(key, SESSION_TIMEOUT);
}
return session;
});
}
/**
* 删除会话
*/
public static void removeSession(String sessionId) {
String key = SESSION_PREFIX + sessionId;
RedisUtil.del(key);
}
/**
* 验证会话是否有效
*/
public static boolean isValidSession(String sessionId) {
String key = SESSION_PREFIX + sessionId;
return RedisUtil.exists(key);
}
}
4. 计数器和限流
public class CounterAndRateLimit {
/**
* 简单计数器
*/
public static long increment(String counterKey) {
return RedisUtil.execute(jedis -> jedis.incr(counterKey));
}
/**
* 带过期时间的计数器
*/
public static long incrementWithExpire(String counterKey, int expireSeconds) {
return RedisUtil.execute(jedis -> {
long count = jedis.incr(counterKey);
if (count == 1) {
jedis.expire(counterKey, expireSeconds);
}
return count;
});
}
/**
* 滑动窗口限流
*/
public static boolean isAllowed(String key, int limit, int windowSeconds) {
long now = System.currentTimeMillis();
long windowStart = now - windowSeconds * 1000L;
return RedisUtil.execute(jedis -> {
// 移除过期的记录
jedis.zremrangeByScore(key, 0, windowStart);
// 获取当前窗口内的请求数
long count = jedis.zcard(key);
if (count < limit) {
// 添加当前请求
jedis.zadd(key, now, String.valueOf(now));
jedis.expire(key, windowSeconds);
return true;
}
return false;
});
}
/**
* 令牌桶限流
*/
public static boolean tryAcquire(String bucketKey, int capacity, int refillRate) {
String script =
"local bucket = KEYS[1] " +
"local capacity = tonumber(ARGV[1]) " +
"local refillRate = tonumber(ARGV[2]) " +
"local now = tonumber(ARGV[3]) " +
"local bucket_data = redis.call('HMGET', bucket, 'tokens', 'lastRefill') " +
"local tokens = tonumber(bucket_data[1]) or capacity " +
"local lastRefill = tonumber(bucket_data[2]) or now " +
"local elapsed = now - lastRefill " +
"local tokensToAdd = math.floor(elapsed * refillRate / 1000) " +
"tokens = math.min(capacity, tokens + tokensToAdd) " +
"if tokens >= 1 then " +
"tokens = tokens - 1 " +
"redis.call('HMSET', bucket, 'tokens', tokens, 'lastRefill', now) " +
"redis.call('EXPIRE', bucket, 3600) " +
"return 1 " +
"else " +
"redis.call('HMSET', bucket, 'tokens', tokens, 'lastRefill', now) " +
"redis.call('EXPIRE', bucket, 3600) " +
"return 0 " +
"end";
return RedisUtil.execute(jedis -> {
Object result = jedis.eval(script,
Collections.singletonList(bucketKey),
Arrays.asList(String.valueOf(capacity),
String.valueOf(refillRate),
String.valueOf(System.currentTimeMillis())));
return Long.valueOf(1).equals(result);
});
}
}
💡 最佳实践
- 连接管理:使用连接池,避免频繁创建连接
- 异常处理:妥善处理网络异常和超时
- 键命名:使用有意义的键名,建立命名规范
- 过期时间:合理设置过期时间,避免内存泄漏
- 序列化:选择合适的序列化方式(JSON、Protobuf等)
- 监控:监控连接数、响应时间、错误率等指标
⚠️ 注意事项
- 线程安全:Jedis 实例不是线程安全的,需要使用连接池
- 资源释放:及时关闭连接,避免连接泄漏
- 大键值:避免存储过大的值,影响性能
- 批量操作:使用 Pipeline 或事务进行批量操作
- 网络延迟:考虑网络延迟对性能的影响