目录
1.业务场景
2.解决方案
方案一:数据库存储字段设置为唯一
方案二:使用分布式锁
3.应用实例
1.引入依赖
2.配置redisson
3.使用分布式锁
1.获取锁对象
2.获取分布式锁
3.释放锁
4.常见问题
5.总结
1.业务场景
为提升服务性能,用redis缓存,将请求数据保存放到缓存中,每小时批量存储到数据库中,部署了三个节点,于是出现了同一时间三台机器重复存储数据
2.解决方案
方案一:数据库存储字段设置为唯一
优缺点分析:
优点:方便快捷,只需要将存储字段设置唯一约束
缺点:系统设计不太优雅,会有大量的错误日志出现
方案二:使用分布式锁
优缺点分析:
优点:相对优雅,不会出现大量错误日志
缺点:需要详细考虑到业务逻辑,否则也会触问题
3.应用实例
1.引入依赖
3.16.4
org.redisson
redisson
${redisson-version}
org.redisson
redisson-spring-boot-starter
${redisson-version}
3.16.4 org.redisson redisson${redisson-version} org.redisson redisson-spring-boot-starter${redisson-version}
2.配置redisson
package com.hhmt.delivery.config;
import com.alibaba.nacos.api.config.ConfigType;
import com.alibaba.nacos.api.config.annotation.NacosConfigurationProperties;
import com.alibaba.nacos.api.config.annotation.NacosValue;
import org.redisson.Redisson;
import org.redisson.api.RedissonClient;
import org.redisson.config.Config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
@NacosConfigurationProperties(dataId = "ocpx-service-config.yaml", groupId = "config", autoRefreshed = true, type = ConfigType.YAML)
public class RedissonManager {
@NacosValue(value = "${redis.password}",autoRefreshed = true)
private String password;
@NacosValue(value = "${redisson.address}",autoRefreshed = true)
private String address;
@Bean(destroyMethod = "shutdown")
public RedissonClient getRedisson() throws Exception {
Config config = new Config();
config.useSingleServer().setAddress(address).setPassword(password);
return Redisson.create(config);
}
}
3.使用分布式锁
private void batchSaveData(Listlist) { String logBy = this.getClass().getName() + ".batchSavedata:{}"; int listSize = list.size(); boolean isSuccess; String lockStr = "ocpx-redis-keys"; RLock rLock = redissonClient.getLock(lockStr); try { boolean isLock = rLock.tryLock(10, 10, TimeUnit.SECONDS); if (isLock) { isSuccess = listSize <= 0 || ocpxMediaCbInfoService.saveBatch(list); LOGGER.info(logBy, "insert data to DB:[" + listSize + "]" + isSuccess); rLock.unlock(); } } catch (Exception exception) { LOGGER.error(logBy, exception); throw new ServiceCustomerException(ErrorCode.BATCH_SAVE_EXCEPTION); } finally { if (rLock.isLocked()) { rLock.unlock(); } } }
1.获取锁对象
RLock rLock = redissonClient.getLock(lockStr);
2.获取分布式锁
boolean isLock = rLock.tryLock(10, 10, TimeUnit.SECONDS);
3.释放锁
rLock.unlock();
boolean isLock = rLock.tryLock(10, 10, TimeUnit.SECONDS);
3.释放锁
rLock.unlock();
注意:最终判断是否释放锁,并处理释放资源
4.常见问题
有朋友说在每次用完之后还要关闭redissonClient.shutdown(); 但是我自己测试之后发现我上面这种方式是会有问题的,第一次执行没问题,第二次会提示客户端关闭不能获取锁对象。
对于这种情况我想到了用单例模式去常见redissonClient客户端,理论上是可以的,还没试
5.总结
分布式锁还是挺好用的,可以优雅的解决系统中资源竞争问题
参考资源:Redis分布式客户端之Redisson的基本使用_南北雪树的专栏-CSDN博客_redisson使用
Redis实现分布式锁_scorpio的博客-CSDN博客



