Redis 作為高性能內存數據結構存儲,是現代 Web 系統緩存層首選。本文系統講解 Spring MVC 環境下操作 Redis 單機與集群的兩種主流方案:原生 Jedis 手動注入 與 Spring Data Redis 封裝集成,並給出生產級配置範例。
<!-- Spring 4.3.2+ -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>4.3.2.RELEASE</version>
</dependency>
<!-- Jedis 2.9.0+ -->
<dependency>
<groupId>redis.clients</groupId>
<artifactId>jedis</artifactId>
<version>2.9.0</version>
</dependency>
<!-- Spring Data Redis 1.7.2+ (集群支持需要此版本) -->
<dependency>
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-redis</artifactId>
<version>1.7.2.RELEASE</version>
</dependency>
public class JedisClient {
private static final String HOST = "192.168.31.121";
private static final JedisClient INSTANCE = new JedisClient();
private JedisPoolConfig poolConfig() {
JedisPoolConfig cfg = new JedisPoolConfig();
cfg.setMaxIdle(10);
cfg.setMaxTotal(100);
cfg.setMaxWaitMillis(3000);
return cfg;
}
public Boolean add(String key, String value) {
try (JedisPool pool = new JedisPool(poolConfig(), HOST);
Jedis jedis = pool.getResource()) {
if (jedis.exists(key)) throw new RuntimeException("Key 已存在");
jedis.set(key, value);
return true;
}
}
public String get(String key) {
try (JedisPool pool = new JedisPool(poolConfig(), HOST);
Jedis jedis = pool.getResource()) {
return jedis.get(key);
}
}
// 單例獲取...
}
public class JedisClusterClient {
private static final JedisClusterClient INSTANCE = new JedisClusterClient();
private static final Set<HostAndPort> NODES = new HashSet<>();
static {
NODES.add(new HostAndPort("192.168.31.245", 7000));
NODES.add(new HostAndPort("192.168.31.245", 7001));
NODES.add(new HostAndPort("192.168.31.245", 7002));
NODES.add(new HostAndPort("192.168.31.210", 7003));
NODES.add(new HostAndPort("192.168.31.210", 7004));
NODES.add(new HostAndPort("192.168.31.210", 7005));
}
public void save() {
try (JedisCluster jc = new JedisCluster(NODES, poolConfig())) {
jc.set("cluster", "this is a redis cluster");
System.out.println(jc.get("cluster"));
}
}
}
@Service
public class RedisService {
public String get(String key) {
return JedisClient.getInstance().get(key);
}
}
@Controller
@RequestMapping("redisAllInOne")
public class RedisController {
@Autowired RedisService redisService;
@GetMapping("get")
@ResponseBody
public String get(String key) { return redisService.get(key); }
}
redis.maxIdle=300
redis.maxWait=3000
redis.testOnBorrow=true
redis.host=192.168.31.121
redis.port=6379
redis.password=password
redis.timeout=3000
<beans ...>
<!-- 連接池 -->
<bean id="poolConfig" class="redis.clients.jedis.JedisPoolConfig">
<property name="maxIdle" value="${redis.maxIdle}"/>
<property name="maxWaitMillis" value="${redis.maxWait}"/>
<property name="testOnBorrow" value="${redis.testOnBorrow}"/>
</bean>
<!-- 連接工廠 -->
<bean id="connectionFactory" class="org.springframework.data.redis.connection.jedis.JedisConnectionFactory">
<property name="poolConfig" ref="poolConfig"/>
<property name="hostName" value="${redis.host}"/>
<property name="port" value="${redis.port}"/>
<property name="timeout" value="${redis.timeout}"/>
<property name="database" value="1"/> <!-- DB 索引 -->
</bean>
<!-- 序列化器 -->
<bean id="stringRedisSerializer" class="org.springframework.data.redis.serializer.StringRedisSerializer"/>
<!-- RedisTemplate -->
<bean id="commonRedisTemplate" class="org.springframework.data.redis.core.RedisTemplate">
<property name="connectionFactory" ref="connectionFactory"/>
<property name="keySerializer" ref="stringRedisSerializer"/>
<property name="valueSerializer" ref="stringRedisSerializer"/>
<property name="hashKeySerializer" ref="stringRedisSerializer"/>
<property name="hashValueSerializer" ref="stringRedisSerializer"/>
</bean>
<!-- 第二個 Template 對應 DB 2 -->
<bean id="connectionFactory1" class="org.springframework.data.redis.connection.jedis.JedisConnectionFactory">
<property name="poolConfig" ref="poolConfig"/>
<property name="hostName" value="${redis.host}"/>
<property name="port" value="${redis.port}"/>
<property name="timeout" value="${redis.timeout}"/>
<property name="database" value="2"/>
</bean>
<bean id="cacheRedisTemplate" class="org.springframework.data.redis.core.RedisTemplate">
<property name="connectionFactory" ref="connectionFactory1"/>
<property name="keySerializer" ref="stringRedisSerializer"/>
<property name="valueSerializer" ref="stringRedisSerializer"/>
<property name="hashKeySerializer" ref="stringRedisSerializer"/>
<property name="hashValueSerializer" ref="stringRedisSerializer"/>
</bean>
</beans>
<beans ...>
<bean id="poolConfig" class="redis.clients.jedis.JedisPoolConfig"> ... </bean>
<bean id="redisClusterConfig" class="org.springframework.data.redis.connection.RedisClusterConfiguration">
<property name="maxRedirects" value="3"/>
<property name="clusterNodes">
<set>
<bean class="org.springframework.data.redis.connection.RedisNode">
<constructor-arg name="host" value="192.168.31.245"/>
<constructor-arg name="port" value="7000"/>
</bean>
<!-- 其餘 5 個節點 -->
</set>
</property>
</bean>
<bean id="redis4CacheConnectionFactory" class="org.springframework.data.redis.connection.jedis.JedisConnectionFactory">
<constructor-arg name="clusterConfig" ref="redisClusterConfig"/>
<property name="poolConfig" ref="poolConfig"/>
<property name="timeout" value="${redis.timeout}"/>
</bean>
<bean id="clusterRedisTemplate" class="org.springframework.data.redis.core.RedisTemplate">
<property name="connectionFactory" ref="redis4CacheConnectionFactory"/>
<property name="keySerializer" ref="stringRedisSerializer"/>
<property name="valueSerializer" ref="stringRedisSerializer"/>
<property name="hashKeySerializer" ref="stringRedisSerializer"/>
<property name="hashValueSerializer" ref="stringRedisSerializer"/>
</bean>
</beans>
@Repository
public class RedisCache {
@Resource(name = "cacheRedisTemplate")
private RedisTemplate<String, String> redisTemplate;
public void put(Object key, Object value) {
if (value == null) return;
final String k = key.toString();
final Object v = value;
final long ttl = 86400;
redisTemplate.execute((RedisCallback<Long>) con -> {
byte[] kb = k.getBytes();
byte[] vb = serialize(v);
con.set(kb, vb);
if (ttl > 0) con.expire(kb, ttl);
return 1L;
});
}
public Object get(Object key) {
final String k = key.toString();
return redisTemplate.execute((RedisCallback<Object>) con -> {
byte[] vb = con.get(k.getBytes());
return vb == null ? null : deserialize(vb);
});
}
// 序列化/反序列化方法省略...
}
| 問題 | 解決方案 |
|---|---|
| 版本不兼容 | Spring Data Redis 1.7.2+ 才支持集群,需匹配 Spring 4.3+ |
| 實體對象存儲 | Value 必須實現 Serializable 接口 |
| 序列化選擇 | String 值用 StringRedisSerializer;對象用 GenericJackson2JsonRedisSerializer 或 JDK 序列化 |
| 連接池調優 | maxTotal 根據業務併發設置,testOnBorrow=true 保證連接有效 |
| 集群槽位 | 確保節點配置覆蓋所有 16384 槽位,maxRedirects 防止無限重定向 |
掌握雙方案,根據項目規模與運維能力靈活選擇,讓 Redis 真正成為系統性能的加速器。