Redis事务与持久化
事务特性
事务特点
- 批量执行但不保证原子性
- 无回滚机制
- 执行期间不会被其他命令打断
- 使用WATCH实现乐观锁
持久化机制
RDB快照
# redis.conf配置示例
save 900 1 # 900秒内至少1个key变化
save 300 10 # 300秒内至少10个key变化
dbfilename dump.rdb
AOF日志
appendonly yes
appendfsync everysec # 每秒同步
auto-aof-rewrite-percentage 100
auto-aof-rewrite-min-size 64mb
高可用架构
主从复制
# 从节点配置
replicaof 127.0.0.1 6379
replica-read-only yes
哨兵模式
# sentinel.conf配置
sentinel monitor mymaster 127.0.0.1 6379 2
sentinel down-after-milliseconds mymaster 5000
sentinel failover-timeout mymaster 60000
Java客户端配置
// 哨兵模式连接
JedisPoolConfig poolConfig = new JedisPoolConfig();
JedisSentinelPool pool = new JedisSentinelPool(
"mymaster",
new HashSet<>(Arrays.asList("sentinel1:26379")),
poolConfig
);
生产建议
- 根据业务选择持久化方式
- 主从节点配置合理的内存
- 哨兵至少部署3个节点
- 监控持久化状态
- 定期备份数据
Java 操作 Redis 实战
环境准备
1. 添加依赖
<!-- Spring Boot Starter Data Redis -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<!-- 连接池 -->
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-pool2</artifactId>
</dependency>
2. 配置文件
spring:
redis:
host: localhost
port: 6379
password: your_password
database: 0
lettuce:
pool:
max-active: 8
max-idle: 8
min-idle: 0
max-wait: -1ms
基础操作
1. 配置类
@Configuration
public class RedisConfig {
@Bean
public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory factory) {
RedisTemplate<String, Object> template = new RedisTemplate<>();
template.setConnectionFactory(factory);
// 设置key的序列化方式
template.setKeySerializer(new StringRedisSerializer());
// 设置value的序列化方式
template.setValueSerializer(new GenericJackson2JsonRedisSerializer());
return template;
}
}
2. 工具类
@Component
public class RedisUtil {
@Autowired
private RedisTemplate<String, Object> redisTemplate;
// 设置缓存
public void set(String key, Object value, long time) {
redisTemplate.opsForValue().set(key, value, time, TimeUnit.SECONDS);
}
// 获取缓存
public Object get(String key) {
return redisTemplate.opsForValue().get(key);
}
// 删除缓存
public Boolean delete(String key) {
return redisTemplate.delete(key);
}
// 设置过期时间
public Boolean expire(String key, long time) {
return redisTemplate.expire(key, time, TimeUnit.SECONDS);
}
}
实战案例
1. 商品缓存
@Service
public class ProductService {
@Autowired
private RedisTemplate<String, Object> redisTemplate;
@Autowired
private ProductMapper productMapper;
private static final String PRODUCT_KEY = "product:";
public Product getProduct(Long id) {
// 1. 查询缓存
String key = PRODUCT_KEY + id;
Product product = (Product) redisTemplate.opsForValue().get(key);
// 2. 缓存未命中,查询数据库
if (product == null) {
product = productMapper.selectById(id);
if (product != null) {
// 3. 写入缓存
redisTemplate.opsForValue().set(key, product, 1, TimeUnit.HOURS);
}
}
return product;
}
public void updateProduct(Product product) {
// 1. 更新数据库
productMapper.updateById(product);
// 2. 删除缓存
redisTemplate.delete(PRODUCT_KEY + product.getId());
}
}
2. 分布式锁
@Service
public class DistributedLock {
@Autowired
private RedisTemplate<String, Object> redisTemplate;
public boolean lock(String key, String value, long timeout) {
return redisTemplate.opsForValue()
.setIfAbsent(key, value, timeout, TimeUnit.SECONDS);
}
public boolean unlock(String key, String value) {
String script = "if redis.call('get', KEYS[1]) == ARGV[1] then " +
"return redis.call('del', KEYS[1]) else return 0 end";
return redisTemplate.execute(new DefaultRedisScript<>(script, Boolean.class),
Collections.singletonList(key), value);
}
}
3. 限流器
@Service
public class RateLimiter {
@Autowired
private RedisTemplate<String, Object> redisTemplate;
public boolean isAllowed(String key, int limit, int period) {
String script = "local current = redis.call('incr', KEYS[1]) " +
"if current == 1 then " +
"redis.call('expire', KEYS[1], ARGV[1]) end " +
"return current <= tonumber(ARGV[2])";
return redisTemplate.execute(new DefaultRedisScript<>(script, Boolean.class),
Collections.singletonList(key), period, limit);
}
}
4. 排行榜
@Service
public class RankingService {
@Autowired
private RedisTemplate<String, Object> redisTemplate;
private static final String RANKING_KEY = "ranking:";
public void addScore(String member, double score) {
redisTemplate.opsForZSet().add(RANKING_KEY, member, score);
}
public Set<ZSetOperations.TypedTuple<Object>> getTop(int count) {
return redisTemplate.opsForZSet().reverseRangeWithScores(RANKING_KEY, 0, count - 1);
}
public Long getRank(String member) {
return redisTemplate.opsForZSet().reverseRank(RANKING_KEY, member);
}
}
性能优化
1. Pipeline 批量操作
@Service
public class BatchOperation {
@Autowired
private RedisTemplate<String, Object> redisTemplate;
public void batchSet(Map<String, Object> map) {
redisTemplate.executePipelined((RedisCallback<Object>) connection -> {
map.forEach((key, value) -> {
connection.set(
key.getBytes(),
value.toString().getBytes()
);
});
return null;
});
}
}
2. 本地缓存
@Service
public class LocalCache {
private LoadingCache<String, Object> cache;
@PostConstruct
public void init() {
cache = CacheBuilder.newBuilder()
.maximumSize(1000)
.expireAfterWrite(10, TimeUnit.MINUTES)
.build(new CacheLoader<String, Object>() {
@Override
public Object load(String key) {
return loadFromRedis(key);
}
});
}
public Object get(String key) {
try {
return cache.get(key);
} catch (ExecutionException e) {
return null;
}
}
}
最佳实践
1. 异常处理
@Aspect
@Component
public class RedisAspect {
@Around("execution(* com.example.service.*.*(..))")
public Object handleRedisException(ProceedingJoinPoint point) {
try {
return point.proceed();
} catch (RedisConnectionFailureException e) {
// 处理连接异常
return null;
} catch (Throwable e) {
// 处理其他异常
throw new RuntimeException(e);
}
}
}
2. 监控指标
@Component
public class RedisMonitor {
@Autowired
private RedisTemplate<String, Object> redisTemplate;
@Scheduled(fixedRate = 60000)
public void monitor() {
// 监控连接数
Long clientCount = redisTemplate.execute((RedisCallback<Long>) connection ->
connection.serverCommands().clientList().stream().count());
// 监控内存使用
Properties info = redisTemplate.execute((RedisCallback<Properties>) connection ->
connection.serverCommands().info());
// 记录监控数据
log.info("Redis clients: {}", clientCount);
log.info("Redis memory: {}", info.getProperty("used_memory"));
}
}
注意事项
-
连接池配置:
- 根据并发量设置合适的连接数
- 定期检查连接状态
- 实现连接重试机制
-
序列化选择:
- 使用高效的序列化方式
- 考虑序列化大小
- 注意序列化性能
-
异常处理:
- 实现优雅降级
- 记录异常日志
- 设置重试机制
-
性能优化:
- 使用批量操作
- 实现本地缓存
- 避免大键
- 合理设置过期时间