基于redis的分布式锁

server/2024/10/18 12:24:48/

 目前业务组新参与了一个项目,涉及到用户积分的增删改查核销等等,预计上线后qps大概在20k左右,我们使用了基于redis分布式锁来保证数据的一致性。

@Slf4j
@Service
public class RedisLockService {/*** 加锁lua脚本*/private static final String REDIS_LOCK_LUA_SCRIPT = "if redis.call('setnx',KEYS[1],ARGV[1]) == 1 then  return redis.call('expire',KEYS[1],ARGV[2])  else return 0 end";/*** 解锁lua脚本*/private static final String REDIS_UN_LOCK_LUA_SCRIPT = "if redis.call('get', KEYS[1]) == ARGV[1] then return redis.call('del', KEYS[1]) else return 0 end";/*** 数据库操作key*/private static final String DB_OPERATION_KEY_PREFIX = "operation_key_";@Resourceprivate RedisClient redisClient;/*** 添加DB数据库锁** @param userId 用户名* @return 加锁成功之后,返回sequenceId*/public String dbLock(int userId) {String sequenceId = String.valueOf(System.nanoTime());if (lock(DB_OPERATION_KEY_PREFIX + userId, sequenceId, 10, TimeUnit.SECONDS)) {return sequenceId;} else {recordOne("redis_lock_service_db_lock_fail");return null;}}/*** 解数据库锁** @param userId     用户名* @param sequenceId 序列号* @return 返回是否解锁成功*/public boolean dbUnlock(int userId, String sequenceId) {boolean result = unLock(DB_OPERATION_KEY_PREFIX + userId, sequenceId);if (!result) {recordOne("redis_lock_service_db_un_lock_fail");}return result;}/*** 添加分布式锁** @param key        锁的key* @param sequenceId 唯一Id* @param expireTime 过期时间* @param unit       过期时间单位* @return 返回是否加锁成功*/public boolean lock(String key, String sequenceId, int expireTime, TimeUnit unit) {long start = System.currentTimeMillis();try {if (tryLock(key, sequenceId, expireTime, unit, 0)) {return true;} else {log.error("添加分布式锁失败:key={}", key);recordOne("redis_lock_service_lock_fail");return false;}} catch (Exception e) {log.error("添加分布式锁异常:key={}", key, e);recordOne("redis_lock_service_lock_exception");} finally {recordQuantile("redis_lock_service_lock", System.currentTimeMillis() - start);}return false;}public boolean lock(String key, String sequenceId, int expireTime, TimeUnit unit, int waitTimeInMills) {long start = System.currentTimeMillis();try {if (tryLock(key, sequenceId, expireTime, unit, waitTimeInMills)) {return true;} else {log.error("添加分布式锁失败:key={}", key);recordOne("redis_lock_service_lock_fail");return false;}} catch (Exception e) {log.error("添加分布式锁异常:key={}", key, e);recordOne("redis_lock_service_lock_exception");} finally {recordQuantile("redis_lock_service_lock", System.currentTimeMillis() - start);}return false;}public String lockWithWaitTime(String key, int expireTime, int waitTimeInMills) {long start = System.currentTimeMillis();String sequenceId = String.valueOf(System.nanoTime());if (tryLock(DB_OPERATION_KEY_PREFIX + key, sequenceId, expireTime, TimeUnit.SECONDS, waitTimeInMills)) {recordQuantile("redis_lockNew_service_lock", System.currentTimeMillis() - start);return sequenceId;} else {log.error("lockNew 添加分布式锁失败:key={}", key);recordOne("redis_lockNew_service_lock_fail");return null;}}/*** 解分布式锁** @param key        解锁key* @param sequenceId 唯一的一个Id* @return 返回解锁是否成功*/public boolean unLock(String key, String sequenceId) {long start = System.currentTimeMillis();try {long result = redisClient.getSession(key, true).getSingleConnectionExecutor().evalAutoSha(REDIS_UN_LOCK_LUA_SCRIPT, ResultType.INTEGER, new String[]{key}, new String[]{sequenceId}).get(500, TimeUnit.MILLISECONDS);if (result > 0) {return true;} else {log.error("解锁失败:key={}", key);recordOne("redis_lock_service_un_lock_fail");return false;}} catch (Exception e) {recordOne("redis_lock_service_un_lock_exception");log.error("解锁异常:key={}", key, e);} finally {recordQuantile("redis_lock_service_un_lock", System.currentTimeMillis() - start);}return false;}/*** Redis 尝试加锁** @param key              加锁的Key* @param expireTime       过期时间* @param timeUnit         时间单位* @param waitTimeInMills 等待时间* @return 返回加锁是否成功*/private boolean tryLock(final String key, String sequenceId, int expireTime, TimeUnit timeUnit, int waitTimeInMills) {log.info("redis_lock_service_tryLock_add, userId={}, sequenceId={}", key, sequenceId);long start = System.currentTimeMillis();long expireTimeSecond = timeUnit.toSeconds(expireTime);try {while (redisClient.getSession(key, true).getSingleConnectionExecutor().evalAutoSha(REDIS_LOCK_LUA_SCRIPT, ResultType.INTEGER, new String[]{key}, new String[]{sequenceId, String.valueOf(expireTimeSecond)}).get(500, TimeUnit.MILLISECONDS) <= 0) {long time = System.currentTimeMillis() - start;if (time > waitTimeInMills) {log.error("尝试添加分布式锁失败:key={} time:{}, 超时返回", key, time);log.info("redis_lock_service_tryLock_timeout_fail, userId={}, sequenceId={}", key, sequenceId);recordOne("redis_lock_service_tryLock_timeout_fail");return false;}log.info("尝试添加分布式锁成功:key={} time:{}, 等待中", key, time);TimeUnit.MILLISECONDS.sleep(10);}return true;} catch (Exception e) {Long unlockResult = null;try {unlockResult = redisClient.getSession(key, true).getSingleConnectionExecutor().evalAutoSha(REDIS_UN_LOCK_LUA_SCRIPT, ResultType.INTEGER, new String[]{key}, new String[]{sequenceId}).get(500, TimeUnit.MILLISECONDS);} catch (Exception exception) {log.error("尝试添加分布式锁失败后解锁异常:key={}", key, e);recordOne("redis_lock_service_try_lock_unlock_exception");}log.error("尝试添加分布式锁失败:key={}, 解锁结果:{}", key, unlockResult, e);recordOne("redis_lock_service_try_lock_exception");} finally {recordQuantile("redis_lock_service_try_lock", System.currentTimeMillis() - start);}log.info("redis_lock_service_tryLock_fail, userId={}, sequenceId={}", key, sequenceId);return false;}
}


http://www.ppmy.cn/server/62300.html

相关文章

通过命令行工作流提升工作效率的实战教程(持续更新)

大家好,我是herosunly。985院校硕士毕业,现担任算法研究员一职,热衷于机器学习算法研究与应用。曾获得阿里云天池比赛第一名,CCF比赛第二名,科大讯飞比赛第三名。拥有多项发明专利。对机器学习和深度学习拥有自己独到的见解。曾经辅导过若干个非计算机专业的学生进入到算法…

记一次饱经挫折的阿里云ROS部署经历

前言 最近在参加的几个项目测评里&#xff0c;我发现**“一键部署”这功能真心好用&#xff0c;省下了不少宝贵时间和力气&#xff0c;再加上看到阿里云现在有个开源上云**的活动。趁着这波热潮&#xff0c;今天就聊聊怎么从头开始&#xff0c;一步步搞定阿里云的资源编排服务…

Python数据分析~~美食排行榜

目录 1.模块的导入和路径的选择 2.访问前面五行数据 3.按照条件进行筛选 4.获取店铺评分里面的最高分 5.打印对应的店铺的名字 1.模块的导入和路径的选择 # 导入pandas模块&#xff0c;简称为pd import pandas as pd # 使用read_csv()函数 # TODO 读取路径"/Users/fe…

基于LSTM及其变体的回归预测

1 所用模型 代码中用到了以下模型&#xff1a; 1. LSTM&#xff08;Long Short-Term Memory&#xff09;&#xff1a;长短时记忆网络&#xff0c;是一种特殊的RNN&#xff08;循环神经网络&#xff09;&#xff0c;能够解决传统RNN在处理长序列时出现的梯度消失或爆炸的问题。L…

STM32HAL库+ESP8266+cJSON+微信小程序_连接华为云物联网平台

STM32HAL库ESP8266cJSON微信小程序_连接华为云物联网平台 实验使用资源&#xff1a;正点原子F407 USART1&#xff1a;PA9P、A10&#xff08;串口打印调试&#xff09; USART3&#xff1a;PB10、PB11&#xff08;WiFi模块&#xff09; DHT11&#xff1a;PG9&#xff08;采集数据…

Github 2024-07-13 Rust开源项目日报 Top10

根据Github Trendings的统计,今日(2024-07-13统计)共有10个项目上榜。根据开发语言中项目的数量,汇总情况如下: 开发语言项目数量Rust项目10C项目1Zed: 由Atom和Tree-sitter的创建者开发的高性能多人代码编辑器 创建周期:1071 天开发语言:Rust协议类型:OtherStar数量:94…

Objective-C 中字符串的保存位置

在 Objective-C 中&#xff0c;字符串常量和动态创建的字符串&#xff08;例如通过 stringWithFormat:、initWithString: 等方法创建的字符串&#xff09;在内存中保存的位置一样么 &#xff1f; 在 Objective-C 中&#xff0c;字符串常量和动态创建的字符串在内存中的保存位置…

微信小程序 - 本地存储 增加有效期

小程序的本地存储API提供了wx.setStorageSync和wx.setStorage来存储数据&#xff0c;注意的是&#xff0c;小程序的本地存储并没有明确的有效期设置&#xff0c;存储的数据在不超过限制的情况下&#xff0c;会一直保留。 一、小程序本地存储API 小程序的本地存储API提供了设置…