背景
由于项目中多出涉及同步数据,同步过程就是从设备上查询数据,将数据库中该设备数据删除,将新数据导入到数据库;多次同步数据或多用户操作,会导致数据库出现重复数据,例如,两个线程同时删除设备数据,同时导入数据,就会出现双倍数据;还有线程1正在导入数据,中途线程2将线程1导入数据之前删除,那么线程1后面导入的数据也会和线程2重复,会出现部分重复;故此,必须对操作数据库添加锁;
实现方式
思路:向redis中添加key-value,当存在key-value则为有锁,反之则为没锁
Spring 封装了 RedisTemplate 对象来进行对redis的各种操作,它支持所有的 redis 原生的 api。 查阅点资料下面总结看下Redis中opsForValue()方法的使用介绍:
要使用 RedisTemplate,必须要先引入它,下面是它的「maven依赖」:
<!--redis-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
<!--<version>2.1.4.RELEASE</version>-->
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-pool2</artifactId>
</dependency>
<!-- fastjson -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.68</version>
</dependency>
引入依赖后,我们需要配置 RedisTemplate。比如:Redis 序列化方式配置:
@Configuration public class RedisConfig {
// 设置Redis序列化方式,默认使用的JDKSerializer的序列化方式,效率低,这里我们使用 FastJsonRedisSerializer
@Bean
public RedisTemplate<String, Object> redisTemplate(LettuceConnectionFactory redisConnectionFactory) {
RedisTemplate<String, Object> redisTemplate = new RedisTemplate<>(); // key序列化
redisTemplate.setKeySerializer(new StringRedisSerializer()); // value序列化
redisTemplate.setValueSerializer(new FastJsonRedisSerializer<>(Object.class)); // Hash key序列化
redisTemplate.setHashKeySerializer(new StringRedisSerializer()); // Hash value序列化
redisTemplate.setHashValueSerializer(new StringRedisSerializer());
redisTemplate.setConnectionFactory(redisConnectionFactory); return redisTemplate;
}
}
设置当前的 key 以及 value 值:
redisTemplate.opsForValue().set(key, value)
redisTemplate.opsForValue().set("num","123");
设置当前的 key 以及 value 值并且设置过期时间:
redisTemplate.opsForValue().set(key, value, timeout, unit)
redisTemplate.opsForValue().set("num","123",10, TimeUnit.SECONDS);
//TimeUnit.DAYS //天
//TimeUnit.HOURS //小时
//TimeUnit.MINUTES //分钟
//TimeUnit.SECONDS //秒
//TimeUnit.MILLISECONDS //毫秒
将旧的 key 设置为 value,并且返回旧的 key(设置 key 的字符串 value 并返回其旧值):
redisTemplate.opsForValue().getAndSet(key, value);
在原有的值基础上新增字符串到末尾:
redisTemplate.opsForValue().append(key, value)
获取字符串的长度:
redisTemplate.opsForValue().size(key)
重新设置 key 对应的值,如果存在返回 false,否则返回 true:
redisTemplate.opsForValue().setIfAbsent(key, value)
设置 map 集合到 redis:
Map valueMap = new HashMap();
valueMap.put("valueMap1","map1");
valueMap.put("valueMap2","map2");
valueMap.put("valueMap3","map3");
redisTemplate.opsForValue().multiSet(valueMap);
如果对应的 map 集合名称不存在,则添加否则不做修改:
Map valueMap = new HashMap();
valueMap.put("valueMap1","map1");
valueMap.put("valueMap2","map2");
valueMap.put("valueMap3","map3");
redisTemplate.opsForValue().multiSetIfAbsent(valueMap);
通过 increment(K key, long delta) 方法以增量方式存储 long 值(正值则自增,负值则自减):
redisTemplate.opsForValue().increment(key, increment);
批量获取值:(返回传入 key 所存储的值的类型)
public List<String> multiGet(Collection<String> keys) {
return redisTemplate.opsForValue().multiGet(keys);
}
修改 redis 中 key 的名称:
public void renameKey(String oldKey, String newKey) {
redisTemplate.rename(oldKey, newKey);
}
如果旧值 key 存在时,将旧值改为新值:
public Boolean renameOldKeyIfAbsent(String oldKey, String newKey) {
return redisTemplate.renameIfAbsent(oldKey, newKey);
}
判断是否有 key 所对应的值,有则返回 true,没有则返回 false:
redisTemplate.hasKey(key)
删除单个 key 值:
redisTemplate.delete(key)
批量删除 key:
redisTemplate.delete(keys) //其中keys:Collection<K> keys
设置过期时间:
public Boolean expire(String key, long timeout, TimeUnit unit){
return redisTemplate.expire(key, timeout, unit);
}
public Boolean expireAt(String key, Date date) {
return redisTemplate.expireAt(key, date);
}
返回当前 key 所对应的剩余过期时间:
redisTemplate.getExpire(key);
返回剩余过期时间并且指定时间单位:
public Long getExpire(String key, TimeUnit unit) {
return redisTemplate.getExpire(key, unit);
}
查找匹配的 key 值,返回一个 Set 集合类型:
public Set<String> getPatternKey(String pattern) {
return redisTemplate.keys(pattern);
}
将 key 持久化保存:
public Boolean persistKey(String key) {
return redisTemplate.persist(key);
}
将当前数据库的 key 移动到指定 redis 中数据库当中:
public Boolean moveToDbIndex(String key, int dbIndex) {
return redisTemplate.move(key, dbIndex);
}
这里用到的锁比较简单,因此只需要判断key是否存在即可,不过要求项目中不可出现同样的key,否则会出错,例如:3-1-a,因此添加锁用到的命令为:
添加key:
redisTemplate.opsForValue().set(key, value)
判断key是否存在:
redisTemplate.hasKey(key)
删除key:
redisTemplate.delete(key)
较为复杂的功能可以通过给key设置不同的value实现,像我后续的一个功能要求实现线程池指定线程的开启和关闭,显然,Java的线程具有原子性,无法互相干扰,那么就可以通过给key赋值当前时间,从而在需要的时候判断value,实现线程之间的影响
参考:从入门到精通,超强 RedisTemplate 方法详解!