首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> Java 程式設計 >> Java 綜合教學

Java 操作 Redis 實戰:Spring Boot + Jedis 整合、分片連接池與全 API 封裝最佳實踐

     編輯:Java 綜合教學

Spring Boot 整合 Jedis 實戰:從配置到生產級 API 封裝全流程

Redis 作為高性能鍵值存儲,是現代架構中緩存、會話、分佈式鎖、消息隊列的核心組件。本文基於 Spring Boot 2.x + Jedis 3.x,系統講解 Maven 依賴、YAML 配置、分片連接池 Bean 裝配、RedisService 統一資源管理、RedisClient 全 API 封裝 (String/Hash/List/Set/ZSet/Key/事務/管道/發布訂閱)、異常處理與資源歸還最佳實踐,附完整可運行代碼。

1. Maven 依賴 (Spring Boot 2.x / Jedis 3.x)

<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 抽象層 -->

2. YAML 配置文件 (application.yml)

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

3. 分片連接池配置類 (手動裝配 ShardedJedisPool)

@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));
    }
}

4. RedisService:統一資源獲取與歸還 (核心防洩漏)

@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() 避免洩漏。

5. RedisClient:全 API 封裝類 (節選核心方法)

@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);
        }
    }
}

6. 實戰場景示例

分佈式鎖 (SETNX + Lua 腳本保證原子性)

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));
}

7. 生產環境關鍵優化清單

  1. 連接池參數壓測調優:maxTotal = 極峰 QPS × 平均耗時(秒) × 1.5
  2. 啟用測試借還:testOnBorrow=true、testOnReturn=true 防止死連接
  3. 大 Key 拆分:Hash/List/ZSet 超過 1 萬元素拆分,避免阻塞主線程
  4. 熱 Key 本地緩存:Caffeine + Redis 兩級緩存,降低 Redis 壓力
  5. 監控告警:Prometheus + Grafana 監控 connected_clients、used_memory、keyspace_hits/misses、rejected_connections
  6. 避免 Keys *:生產嚴禁,用 SCAN 游標迭代
  7. 序列化選擇:對象存儲優先 JSON (Jackson) 或 Protobuf,拒絕 JDK 原生序列化

總結

Spring Boot + Jedis 整合的核心在於 分片連接池配置、統一資源管理模板、全 API 類型安全封裝。掌握 String/Hash/List/Set/ZSet 五大數據結構的業務映射 (緩存/計數器/隊列/標籤/排行榜),配合事務/管道/Lua 腳本解決原子性與批量性能問題,即可構建高可用、高性能的 Redis 應用層。下一篇我們將深入 MyEclipse/IDEA 查看 JDK/第三方 JAR 源碼的多種姿勢。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved