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

Redis Cluster 客戶端 Jedis 實戰:Spring 整合配置與五大資料結構完整操作範例

     編輯:Java 綜合教學

Redis Cluster 客戶端 Jedis 實戰:Spring 整合配置與五大資料結構完整操作範例

Redis Cluster 提供高可用、自動分片的分散式快取方案。雖然 Spring Data Redis 1.7+ 已原生支援 Cluster,但許多專案仍處於舊版本(如 1.6.x),需直接使用原生 Jedis 客戶端操作 Cluster。本文提供完整的Spring XML 整合配置、JedisCluster 封裝工具類,並演示String、Set、List、Hash、ZSet五大資料結構的寫入、讀取、遍歷操作,附帶Key 管理策略避免 `KEYS *` 性能災難。

一、Spring XML 整合 JedisCluster

核心思路:配置 ``,構造器傳入 `Set` 種子節點。

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="https://www.springframework.org/schema/beans"
       xmlns:xsi="https://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="https://www.springframework.org/schema/beans
           https://www.springframework.org/schema/beans/spring-beans.xsd">

    <bean id="jedisCluster" class="redis.clients.jedis.JedisCluster">
        <constructor-arg index="0">
            <set>
                <bean class="redis.clients.jedis.HostAndPort">
                    <constructor-arg name="host" value="127.0.0.1"/>
                    <constructor-arg name="port" value="7000"/>
                </bean>
                <bean class="redis.clients.jedis.HostAndPort">
                    <constructor-arg name="host" value="127.0.0.1"/>
                    <constructor-arg name="port" value="7001"/>
                </bean>
                <bean class="redis.clients.jedis.HostAndPort">
                    <constructor-arg name="host" value="127.0.0.1"/>
                    <constructor-arg name="port" value="7002"/>
                </bean>
                <!-- 建議配置 3-6 個種子節點,確保至少一個可連通 -->
            </set>
        </constructor-arg>
    </bean>
</beans>

關鍵說明:

  • 種子節點無需配置全量節點,Jedis 會自動發現叢集拓撲。
  • 為高可用,建議配置多個 Master 節點,防止單點故障導致啟動失敗。
  • 生產環境建議額外配置 `JedisPoolConfig`(連接池參數:`maxTotal`、`maxIdle`、`minIdle`、`maxWaitMillis` 等),透過構造器注入。

二、JedisCluster 封裝工具類:統一 Key 管理與五大結構操作

package com.cnblogs.yjmyzz.redis;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import redis.clients.jedis.JedisCluster;
import redis.clients.jedis.JedisPool;
import java.util.*;

public class AppDemo {
    private static final Logger logger = LoggerFactory.getLogger(AppDemo.class);
    private static JedisCluster jc = null;
    
    // Key 分類常數——避免使用 KEYS * 掃描全庫
    private static final String KEYS_STRING = "SYS:KEYS:STRING";
    private static final String KEYS_SET = "SYS:KEYS:SET";
    private static final String KEYS_LIST = "SYS:KEYS:LIST";
    private static final String KEYS_HASH = "SYS:KEYS:HASH";
    private static final String KEYS_ZSET = "SYS:KEYS:ZSET";

    // 統一註冊 Key 到分類 Set
    private static void addKey(String category, String key) {
        if (!jc.exists(category)) {
            jc.sadd(category, key);
        } else if (!jc.smembers(category).contains(key)) {
            jc.sadd(category, key);
        }
    }

    // ========== String ==========
    private static String set(String key, String value) {
        String result = jc.set(key, value);
        addKey(KEYS_STRING, key);
        return result;
    }

    // ========== Set ==========
    private static Long sadd(String key, String... members) {
        Long result = jc.sadd(key, members);
        addKey(KEYS_SET, key);
        return result;
    }

    // ========== List ==========
    private static Long lpush(String key, String... values) {
        Long result = jc.lpush(key, values);
        addKey(KEYS_LIST, key);
        return result;
    }

    // ========== Hash ==========
    private static Long hset(String key, String field, String value) {
        Long result = jc.hset(key, field, value);
        addKey(KEYS_HASH, key);
        return result;
    }

    // ========== ZSet ==========
    private static Long zadd(String key, double score, String member) {
        Long result = jc.zadd(key, score, member);
        addKey(KEYS_ZSET, key);
        return result;
    }
    private static Long zadd(String key, String member) { // 預設 score=0
        return zadd(key, 0d, member);
    }

    // ========== 主程式演示 ==========
    public static void main(String[] args) {
        ApplicationContext ctx = new ClassPathXmlApplicationContext("spring-redis.xml");
        jc = ctx.getBean(JedisCluster.class);
        
        // 1. 列印叢集節點資訊並清空測試資料
        Map nodes = jc.getClusterNodes();
        for (Map.Entry entry : nodes.entrySet()) {
            logger.info("節點: {} => {}", entry.getKey(), entry.getValue());
            try { entry.getValue().getResource().flushDB(); } 
            catch (Exception e) { logger.info("Slave 節點 flushDB 失敗: {}", e.getMessage()); }
        }
        
        // 2. String 測試
        logger.info("String 寫入: {}", set("a", "hello world!"));
        logger.info("String 讀取: {}", jc.get("a"));
        
        // 3. Set 測試
        logger.info("Set 寫入: {}", sadd("set1", "a", "b", "c"));
        logger.info("Set 類型: {}", jc.type("set1"));
        jc.smembers("set1").forEach(s -> logger.info("Set 成員: {}", s));
        
        // 4. List 測試
        logger.info("List 寫入: {}", lpush("list1", "1", "2", "3"));
        jc.lrange("list1", 0, 999).forEach(s -> logger.info("List 元素: {}", s));
        
        // 5. Hash 測試
        logger.info("Hash 寫入: {}", hset("hash1", "jimmy", "楊俊明"));
        logger.info("Hash 寫入: {}", hset("hash1", "CN", "中國"));
        jc.hgetAll("hash1").forEach((k,v) -> logger.info("Hash: {}={}", k, v));
        
        // 6. ZSet 測試
        logger.info("ZSet 寫入: {}", zadd("zset1", "3"));
        logger.info("ZSet 寫入: {}", zadd("zset1", 1.5, "2")); // 帶分數
        jc.zrange("zset1", 0, 999).forEach(s -> logger.info("ZSet 有序: {}", s));
        
        // 7. 遍歷所有分類 Key(替代危險的 KEYS *)
        logger.info("所有 String Key: {}", jc.smembers(KEYS_STRING));
        logger.info("所有 Hash Key: {}", jc.smembers(KEYS_HASH));
        logger.info("所有 Set Key: {}", jc.smembers(KEYS_SET));
        logger.info("所有 List Key: {}", jc.smembers(KEYS_LIST));
        logger.info("所有 ZSet Key: {}", jc.smembers(KEYS_ZSET));
    }
}

三、關鍵最佳實踐總結

  • 禁用 KEYS *
  • 種子節點多配幾個
  • 連接池配置
  • 管道 Pipeline
  • 只讀操作走 Slave
  • 異常處理
  • 實踐說明
    Cluster 模式下 `KEYS` 需掃描所有分片,極易導致阻塞。改用應用層維護 Key 分類 Set(如本例 `SYS:KEYS:*`)。
    啟動時只需一個可達節點,但為容錯建議配 3-6 個不同 Master。
    生產必須配置 `JedisPoolConfig`:`maxTotal=200`、`maxIdle=50`、`minIdle=10`、`maxWaitMillis=3000`、`testOnBorrow=true`。
    批量操作時使用 `jc.pipelined()` 減少網路往返(Cluster 模式下需注意槽位分佈)。
    配置 `JedisCluster` 時可設 `readFrom=SLAVE_PREFERRED` 分散讀壓力(需 Redis 2.8+)。
    捕獲 `JedisConnectionException`、`JedisMovedDataException`、`JedisClusterMaxRedirectionsException`,實施重試/降級。

    四、Jedis 常用命令前綴速查

    Jedis 方法命名遵循規律,無官方文檔時可按前綴推斷:

    • 無前綴:`set`、`get`、`del`、`exists` → String
    • `s` 開頭:`sadd`、`smembers`、`srem` → Set
    • `l`/`r` 開頭:`lpush`、`rpop`、`lrange` → List
    • `h` 開頭:`hset`、`hget`、`hgetAll` → Hash
    • `z` 開頭:`zadd`、`zrange`、`zrem` → ZSet

    五、原始碼與參考資源

    結語

    在 Spring Data Redis 未支援 Cluster 的版本窗口期,原生 Jedis 是操作 Redis Cluster 的最佳選擇。透過Spring XML 整合、統一 Key 管理策略、五大資料結構封裝,能構建出穩定、高效、可維護的分散式快取存取層。升級至 Spring Data Redis 1.7+ 後,可平滑遷移至 `RedisTemplate` + `EnableRedisCluster` 註解配置,業務代碼幾乎無需變動。

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