2086 字
约 6 分钟
23
Java 使用 Redis

Java Redis 客户端概述

Java 生态系统中有多个优秀的 Redis 客户端库,每个都有其特点和适用场景。选择合适的客户端对于项目的性能和开发效率至关重要。

🔧 Jedis

Redis 官方推荐的 Java 客户端,简单易用,API 直观。

✅ 优点
  • API 简单直观
  • 官方支持
  • 文档完善
  • 社区活跃
❌ 缺点
  • 线程不安全
  • 功能相对简单
  • 需要连接池管理

🚀 Lettuce

基于 Netty 的异步 Redis 客户端,支持响应式编程。

✅ 优点
  • 线程安全
  • 支持异步操作
  • 响应式编程
  • 连接复用
❌ 缺点
  • 学习曲线较陡
  • 配置复杂
  • 依赖较多

⚡ Redisson

功能丰富的 Redis Java 客户端,提供分布式对象和服务。

✅ 优点
  • 功能丰富
  • 分布式锁
  • 集合操作
  • 高级特性
❌ 缺点

体积较大复杂度高性能开销

环境准备

Maven 依赖

redis.clientsjedis4.4.3io.lettucelettuce-core6.2.6.RELEASEorg.redissonredisson3.23.4org.apache.commonscommons-pool22.11.1

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 staticT execute(Functionaction) {
        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 {
        // 集群节点
        Setnodes = 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 或事务进行批量操作网络延迟:考虑网络延迟对性能的影响

Java 使用 Redis
https://clxhxhhr.top/posts/2816/
作者
clxstart
发布于
2026-09-19
许可协议
CC BY-NC-SA 4.0
评论
0 条
还没有评论,先写一条吧。