基于 RedisTemplate 和 lua 脚本的 redis 分布式锁的实现
分布式锁一般的实现方法有三种:基于数据库锁。(行级锁如唯一约束或乐观锁的版本号方式),基于redis 的分布式锁
和基于zookeeper 的分布式锁。网上有很多关于 redis 分布式锁的实现,本文介绍的是基于 redis 锁的一种简单易用实现方式:基于 spring-data-redis 的RedisTemplate 的实现方式。
实现分布式锁要满足的几个条件:
- 锁的特性:在任一时刻,只能有一个线程持有锁。
- 不能死锁:即使持锁线程崩溃没有主动释放锁也不会影响其他线程持续阻塞。基于redis 过期时间能很好的避免这个问题。
- 只有加锁的线程才能解锁,其他线程不能强制抢占锁,即:强制把别的线程的锁释放。
- 能容错:部分redis节点挂了,不会造成所有任务中止。
- 加锁操作必须是原子操作(网上有些基于java 的实现先set再del是不严谨的)
代码实现
- 首先,自定义一个RedisTemplate, 替换 spring boot 默认装载的 redisTemplate , 因为默认的redisTemplate 序列化用的是 Jackson2JsonRedisSerializer,序列化与反序列化过程中会出现格式问题)
@Bean
public RedisTemplate<String, Serializable> redisTemplateDemo(RedisConnectionFactory redisConnectionFactory) {
RedisTemplate<String, Serializable> template = new RedisTemplate<>();
template.setConnectionFactory(redisConnectionFactory);
// 使用 Jackson2JsonRedisSerializer 替换默认序列化
Jackson2JsonRedisSerializer<Object> jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer<>(Object.class);
ObjectMapper om = new ObjectMapper();
om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
om.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
jackson2JsonRedisSerializer.setObjectMapper(om);
// 字符串序列化器
StringRedisSerializer stringRedisSerializer = new StringRedisSerializer();
// 普通Key设置为字符串序列化器
template.setKeySerializer(stringRedisSerializer);
// Hash结构的key设置为字符串序列化器
template.setHashKeySerializer(stringRedisSerializer);
// 普通值和hash的值都设置为jackson序列化器
template.setValueSerializer(jackson2JsonRedisSerializer);
template.setHashValueSerializer(jackson2JsonRedisSerializer);
template.afterPropertiesSet();
return template;
}