Redis 不仅仅是简单的 Key-Value 存储,还支持多种数据结构:
数据结构 | 描述 | 示例命令 |
---|---|---|
String | 文本、数字、二进制数据 | SET key value , GET key |
List | 有序列表(支持双向操作) | LPUSH list elem , RPOP list |
Hash | 键值对集合(适合存储对象) | HSET user:1 name "Alice" |
Set | 无序唯一集合(去重、交集/并集) | SADD tags "redis" , SINTER |
Sorted Set | 带权重的有序集合(排行榜场景) | ZADD leaderboard 100 "user1" |
Bitmaps | 位操作(用户签到、布隆过滤器) | SETBIT login:2023-01-01 1 1 |
HyperLogLog | 基数统计(UV 去重统计) | PFADD visits user1 , PFCOUNT |
Stream | 消息队列(类似 Kafka) | XADD stream * field value |
MULTI
/EXEC
支持简单事务(不保证原子性,仅保证顺序执行)。dump.rdb
)。redis-check-aof
工具修复)。ZADD
和 ZRANGE
实现实时排名。LPUSH
/BRPOP
实现简单队列。SET key value NX EX 10
实现互斥锁(需配合 Lua 脚本避免误删)。当内存不足时,Redis 支持多种淘汰策略(通过 maxmemory-policy
配置):
volatile-lru
:从已设置过期时间的 key 中淘汰最近最少使用的。allkeys-lru
:从所有 key 中淘汰最近最少使用的。noeviction
:不淘汰,直接返回错误(默认)。特性 | Redis | Memcached |
---|---|---|
数据结构 | 支持多种复杂结构(Hash、Set 等) | 仅支持 String |
持久化 | 支持 RDB 和 AOF | 不支持 |
线程模型 | 单线程(避免竞争) | 多线程 |
适用场景 | 缓存、消息队列、实时计算 | 纯缓存 |
Spring Boot 提供了对 Redis 的自动配置支持,使得在应用中集成 Redis 变得非常简单。下面我将详细介绍如何整合 Redis,包括基本配置、使用方式以及高级功能。
首先需要在 pom.xml
中添加 Redis 相关依赖:
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-data-redisartifactId>
dependency>
<dependency>
<groupId>org.apache.commonsgroupId>
<artifactId>commons-pool2artifactId>
dependency>
在 application.yml
或 application.properties
中配置 Redis 连接信息:
spring:
redis:
host: localhost
port: 6379
password: # 如果有密码
database: 0 # 默认使用0号数据库
lettuce:
pool:
max-active: 8 # 连接池最大连接数
max-idle: 8 # 连接池最大空闲连接数
min-idle: 0 # 连接池最小空闲连接数
Spring Boot 自动配置了 RedisTemplate
和 StringRedisTemplate
,可以直接注入使用:
@RestController
public class RedisController {
@Autowired
private RedisTemplate<String, Object> redisTemplate;
@Autowired
private StringRedisTemplate stringRedisTemplate;
@GetMapping("/set")
public String setValue() {
// 使用 StringRedisTemplate 操作字符串
stringRedisTemplate.opsForValue().set("test:string", "Hello Redis");
// 使用 RedisTemplate 操作对象
User user = new User("张三", 25);
redisTemplate.opsForValue().set("user:1", user);
return "设置成功";
}
@GetMapping("/get")
public Object getValue() {
String str = stringRedisTemplate.opsForValue().get("test:string");
User user = (User) redisTemplate.opsForValue().get("user:1");
return Map.of("string", str, "user", user);
}
}
如果需要自定义序列化方式,可以配置自己的 RedisTemplate:
@Configuration
public class RedisConfig {
@Bean
public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory factory) {
RedisTemplate<String, Object> template = new RedisTemplate<>();
template.setConnectionFactory(factory);
// 使用Jackson2JsonRedisSerializer来序列化和反序列化redis的value值
Jackson2JsonRedisSerializer<Object> serializer = new Jackson2JsonRedisSerializer<>(Object.class);
ObjectMapper mapper = new ObjectMapper();
mapper.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
mapper.activateDefaultTyping(mapper.getPolymorphicTypeValidator(),
ObjectMapper.DefaultTyping.NON_FINAL);
serializer.setObjectMapper(mapper);
// 使用StringRedisSerializer来序列化和反序列化redis的key值
template.setKeySerializer(new StringRedisSerializer());
template.setValueSerializer(serializer);
// Hash的key也采用StringRedisSerializer的序列化方式
template.setHashKeySerializer(new StringRedisSerializer());
template.setHashValueSerializer(serializer);
template.afterPropertiesSet();
return template;
}
}
Spring Data Redis 支持类似 JPA 的 Repository 接口:
@RedisHash("persons")
public class Person {
@Id
private String id;
private String name;
private Integer age;
// getters and setters
}
public interface PersonRepository extends CrudRepository<Person, String> {
List<Person> findByName(String name);
}
@Service
public class PersonService {
@Autowired
private PersonRepository personRepository;
public void savePerson(Person person) {
personRepository.save(person);
}
public Person getPerson(String id) {
return personRepository.findById(id).orElse(null);
}
}
Spring Boot 可以轻松将 Redis 作为缓存使用:
@SpringBootApplication
@EnableCaching
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
spring:
cache:
type: redis
redis:
time-to-live: 60000 # 缓存过期时间(毫秒)
cache-null-values: false # 是否缓存空值
@Service
public class UserService {
@Cacheable(value = "userCache", key = "#id")
public User getUserById(String id) {
// 模拟数据库查询
return new User(id, "用户" + id, new Random().nextInt(100));
}
@CachePut(value = "userCache", key = "#user.id")
public User updateUser(User user) {
// 更新逻辑
return user;
}
@CacheEvict(value = "userCache", key = "#id")
public void deleteUser(String id) {
// 删除逻辑
}
}
@Configuration
public class RedisPubSubConfig {
@Bean
public RedisMessageListenerContainer container(RedisConnectionFactory factory,
MessageListenerAdapter listenerAdapter) {
RedisMessageListenerContainer container = new RedisMessageListenerContainer();
container.setConnectionFactory(factory);
container.addMessageListener(listenerAdapter, new ChannelTopic("chat"));
return container;
}
@Bean
public MessageListenerAdapter listenerAdapter(Receiver receiver) {
return new MessageListenerAdapter(receiver, "receiveMessage");
}
@Bean
public Receiver receiver() {
return new Receiver();
}
@Bean
public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory factory) {
RedisTemplate<String, Object> template = new RedisTemplate<>();
template.setConnectionFactory(factory);
return template;
}
}
@Component
public class Receiver {
public void receiveMessage(String message) {
System.out.println("收到消息: " + message);
}
}
@Service
public class Publisher {
@Autowired
private RedisTemplate<String, Object> redisTemplate;
public void publish(String message) {
redisTemplate.convertAndSend("chat", message);
}
}
@Service
public class LockService {
@Autowired
private StringRedisTemplate redisTemplate;
public boolean tryLock(String lockKey, String requestId, long expireTime) {
return redisTemplate.opsForValue().setIfAbsent(lockKey, requestId, expireTime, TimeUnit.SECONDS);
}
public boolean releaseLock(String lockKey, String requestId) {
String script = "if redis.call('get', KEYS[1]) == ARGV[1] then return redis.call('del', KEYS[1]) else return 0 end";
Long result = redisTemplate.execute(
new DefaultRedisScript<>(script, Long.class),
Collections.singletonList(lockKey),
requestId
);
return result != null && result == 1;
}
}
spring:
redis:
cluster:
nodes:
- 192.168.0.1:6379
- 192.168.0.2:6379
- 192.168.0.3:6379
max-redirects: 3 # 最大重定向次数
spring:
redis:
sentinel:
master: mymaster
nodes:
- 192.168.0.1:26379
- 192.168.0.2:26379
- 192.168.0.3:26379
问题1: 连接超时
解决: 检查网络连接,增加超时时间配置:
spring:
redis:
timeout: 3000 # 连接超时时间(毫秒)
问题2: 序列化异常
解决: 确保所有存储的对象实现了Serializable接口,或配置自定义序列化器
问题3: Lettuce连接池不生效
解决: 确保添加了commons-pool2依赖,并正确配置了pool属性
简介:
Jedis 是一个轻量级、同步阻塞的 Redis Java 客户端,API 简单直接,适合快速开发。
特点:
JedisPool
),避免频繁创建连接的开销。示例代码:
JedisPool pool = new JedisPool("localhost", 6379);
try (Jedis jedis = pool.getResource()) {
jedis.set("key", "value");
System.out.println(jedis.get("key"));
}
简介:
Lettuce 是一个基于 Netty 的高性能、异步非阻塞的 Redis 客户端,支持响应式编程模型。
特点:
StatefulRedisConnection
)。示例代码:
RedisClient client = RedisClient.create("redis://localhost:6379");
StatefulRedisConnection<String, String> connection = client.connect();
RedisAsyncCommands<String, String> commands = connection.async();
commands.set("key", "value").thenAccept(System.out::println);
// 响应式调用
RedisReactiveCommands<String, String> reactiveCommands = connection.reactive();
reactiveCommands.get("key").subscribe(System.out::println);
特性 | Jedis | Lettuce |
---|---|---|
IO 模型 | 同步阻塞 | 异步非阻塞(基于 Netty) |
线程安全 | 需通过连接池实现 | 单连接线程安全 |
性能 | 高吞吐但线程受限 | 更高并发,适合高负载场景 |
连接管理 | 依赖连接池(如 JedisPool ) |
内置长连接,自动管理 |
编程模型 | 传统同步调用 | 支持异步、响应式(Reactor/RxJava) |
功能扩展 | 基础功能完善 | 支持流式操作、SSL 等高级特性 |
适用场景 | 简单应用或旧项目 | 高并发、微服务、响应式系统 |
选 Jedis:
选 Lettuce:
建议根据项目需求和团队技术栈选择。新项目优先考虑 Lettuce,尤其是需要响应式支持时。
作者声明:本博客仅为记录作者自学过程中的点滴思考与心得,旨在分享学习之路的所见所感。由于个人知识水平有限,文中难免存在疏漏或不够严谨之处,恳请各位读者不吝赐教,提出宝贵意见。让我们携手共进,共同成长。另本博客有参考网上资料,如有侵犯,联系删除。