redis分布式锁(乐观锁)

redis分布式锁:setNx自定义锁

redis分布式锁原理:视频教程:【免费】redis高可用分布式锁精讲-1-jvm锁与分布式锁对比-谭亮的在线视频教程-CSDN程序员研修院

  • SET key value命令:

如果 key 已经持有其他值, SET 就覆写旧值,无视类型

  • SETEX key seconds value命令:

将值 value 关联到 key ,并将 key 的生存时间设为 seconds (以秒为单位)

如果 key 已经存在, SETEX 命令将覆写旧值,设置成功时返回 OK,当 seconds 参数不合法时,返回一个错误

  • SETNX key value命令:

将 key 的值设为 value ,当且仅当 key 不存在,若给定的 key 已经存在,则 SETNX 不做任何动作

成功返回1,失败返回0

流程

流程说明:

  • A、B线程同时通过setEX,setNX命令用同一个key去添加redis,由于redis是单线程的,只能有一个线程成功返回ok, A成功去处理业务逻辑,B失败但是一直在循环添加redis
  • A执行业务逻辑完成后删除redis的key,B就可以添加redis成功,去处理相应的业务逻辑
  • redis锁一定要设置过期时间,防止A拿到锁,当时还没释放程序中途出了异常,导致B一直拿不到锁,程序卡死

自定义锁代码实现:(以下为只用setNX实现方式)

  • lockkey可以是你的请求系统+请求流水等字段拼接的唯一的标识,过期时间比执行业务逻辑时间往上加一些就可以(一定要确保大于业务逻辑执行时间,否则A线程执行del时会因为自己的锁已经自动删除,而去把B线程的锁给删掉)
  • 每个线程的key对应的value都不一样,在删除时可以根据value判断是否是自己的redis,避免把其他线程的key删除
  • 如果A线程做删除操作时,通过value发现锁是B线程的,这时A线程应该回滚并抛出异常,防止业务重复执行

代码示例:基于redis组成的分布式锁解决方案为:

1、setNx一个锁key,相应的value为当前时间加上过期时间的时钟;
2、如果setNx成功,或者当前时钟大于此时key对应的时钟则加锁成功,否则加锁失败退出;
3、加锁成功执行相应的业务操作(处理共享数据源);
4、释放锁时判断当前时钟是否小于锁key的value,如果当前时钟小于锁key对应的value则执行删除锁key的操作

public boolean getLock(String lockKey, long timeout) {
		boolean success; // 默认获取锁失败 false
		long value = System.currentTimeMillis() + timeout; // 设置锁值
		success = setnx(lockKey,value);
		if (!success) {
			Long keyValue = queryObjectByKey(lockKey, Long.class);
			long oldValue = keyValue == null ? 0L : keyValue.longValue();
			// 锁已经超时
			if (oldValue < System.currentTimeMillis()) {
				long getValue = getSet(lockKey, value, Long.class);
				// 获取锁成功
				if (getValue == oldValue) {
					success = true;
				} else {
					// 已被其他进程捷足先登了
					success = false;
				}
			} else {
				// 未超时,则直接返回失败
				success = false;
			}
		}
		return success;
	}

业务代码使用自定义锁

// 生成redisKey:根据自己业务去拼接一个不唯一的字符串作为Key
String lockKey = RedisKeyConstant.REDIS_SENTINEL_PREFIX + KeysGeneratorUtil.createRedisKey(
        InfoBO.getRequestSystem(), InfoBO.getEquityNo());
try {
    // 获取锁
    boolean lockResult = redisUtil.getLock(lockKey, 5000L);

    if (!lockResult) {
        log.error("获取锁失败,redisKey:{}",lockKey);
        throw new EquityServiceException("错误码");
    }
} catch (Exception e) {
    log.error("获取锁异常,redisKey:{}",lockKey);
    throw new ServiceException("错误码");
}

// 加锁成功,处理业务代码

try {

   // 加锁成功,处理业务代码

} catch (Exception e ) {

   // 执行业务代码异常

} finally {

   // 根据锁Key释放redis锁:实际上是删除的setNx中的key

  redisUtil.deleteKey(lockKey);

}

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
Mybatis-plus乐观锁Redis分布式锁都是用于解决并发访问数据时的线程安全问题,但它们的实现方式和应用场景有所不同。 Mybatis-plus乐观锁是基于数据库乐观锁实现机制,通过在数据表中添加一个版本号字段来实现。当多个线程同时访问同一条数据时,每个线程会读取到这个版本号,并在更新时将版本号作为更新条件,如果版本号匹配,则执行更新操作;如果版本号不匹配,则说明其他线程已经修改了数据,当前线程更新失败。乐观锁适用于高并发读取、低并发更新的场景,可以减少数据库的锁冲突,提高并发性能。 Redis分布式锁是基于Redis实现的一种分布式锁机制。通过在Redis中设置一个特定的key作为锁,在获取锁时判断该key是否存在,如果存在则表示锁已被其他线程占用,当前线程需要等待;如果不存在,则表示当前线程获取到了锁,可以执行业务操作。分布式锁适用于分布式环境下的并发控制,可以保证多个节点之间的数据一致性和并发安全。 对比而言,Mybatis-plus乐观锁是在数据库层面实现的,适用于单个数据库实例的并发控制,可以减少数据库的锁冲突,但并不能解决分布式环境下的并发问题。而Redis分布式锁则是基于Redis实现的,适用于分布式环境下的并发控制,可以保证多个节点之间的数据一致性和并发安全。 在实际应用中,选择使用哪种机制还需要根据具体场景和需求来决定。如果是单个数据库实例的并发控制,可以选择Mybatis-plus乐观锁;如果是分布式环境下的并发控制,可以选择Redis分布式锁
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值