1:setnx方式
public String redisLock() throws InterruptedException{String lockKey = "locke_key";String clientId = UuidUtils.generateUuid();try{Boolean locked = stringRedisTemplate.opsForValue().setIfAbsent(lockKey, clientId, 30, TimeUnit.MICROSECONDS);if (!locked){return "已被其他线程锁定";}//业务逻辑扣除数据的}finally {String value = stringRedisTemplate.opsForValue().get(lockKey);if (value.equals(clientId)){stringRedisTemplate.delete(lockKey);}}return "end";}
存在的问题:
- 超时问题,已经超时,逻辑还没执行完成
- 设置vlaue的原因,防止线程3,删除线程2的锁
- 锁延长的机制,开启timer
jedis执行luna脚本:
2:Redisson方式
底层依靠Luna脚本,保证原子操作实现的分布式锁
依赖redis 的单线程保证分布式是锁\
官网: https://redisson.org/
分布式场景下的应用
分布式锁,bloom Filter
锁支持重入
使用:
导包:
org.redisson
redisson-spring-boot-starter
3.17.0
redisson方式分布式锁
public String redisLock2() throws InterruptedException{String lockKey = "locke_key";RLock lock = redisson.getLock(lockKey);try{lock.lock();//业务逻辑扣除数据的}finally {lock.unlock();}return "end";}
private <T> RFuture<Long> tryAcquireAsync(long leaseTime, TimeUnit unit, long threadId) {if (leaseTime != -1) {return tryLockInnerAsync(leaseTime, unit, threadId, RedisCommands.EVAL_LONG);}RFuture<Long> ttlRemainingFuture = tryLockInnerAsync(commandExecutor.getConnectionManager().getCfg().getLockWatchdogTimeout(), TimeUnit.MILLISECONDS, threadId, RedisCommands.EVAL_LONG);ttlRemainingFuture.onComplete((ttlRemaining, e) -> {if (e != null) {return;}// lock acquiredif (ttlRemaining == null) {//负责锁超时的续费功能scheduleExpirationRenewal(threadId);}});return ttlRemainingFuture;}
<T> RFuture<T> tryLockInnerAsync(long leaseTime, TimeUnit unit, long threadId, RedisStrictCommand<T> command) {internalLockLeaseTime = unit.toMillis(leaseTime);return commandExecutor.evalWriteAsync(getName(), LongCodec.INSTANCE, command,"if (redis.call('exists', KEYS[1]) == 0) then " +"redis.call('hset', KEYS[1], ARGV[2], 1); " +"redis.call('pexpire', KEYS[1], ARGV[1]); " +"return nil; " +"end; " +"if (redis.call('hexists', KEYS[1], ARGV[2]) == 1) then " +"redis.call('hincrby', KEYS[1], ARGV[2], 1); " +"redis.call('pexpire', KEYS[1], ARGV[1]); " +"return nil; " +"end; " +"return redis.call('pttl', KEYS[1]);",Collections.<Object>singletonList(getName()), internalLockLeaseTime, getLockName(threadId));}
private void renewExpiration() {ExpirationEntry ee = EXPIRATION_RENEWAL_MAP.get(getEntryName());if (ee == null) {return;}Timeout task = commandExecutor.getConnectionManager().newTimeout(new TimerTask() {@Overridepublic void run(Timeout timeout) throws Exception {ExpirationEntry ent = EXPIRATION_RENEWAL_MAP.get(getEntryName());if (ent == null) {return;}Long threadId = ent.getFirstThreadId();if (threadId == null) {return;}RFuture<Boolean> future = renewExpirationAsync(threadId);future.onComplete((res, e) -> {if (e != null) {log.error("Can't update lock " + getName() + " expiration", e);return;}if (res) {// reschedule itselfrenewExpiration();}});}}, internalLockLeaseTime / 3, TimeUnit.MILLISECONDS);ee.setTimeout(task);}
实现事务的原理:Luna脚本
主节点挂了,导致的不在可用的解决方案:
换组件 使用zk, zk 强一致性CP架构

3:RedLock
解决redis的主从挂断的问题
原理是:
redlock对多个redisson节点发送lock ,只有半数加锁成功,返回true,认为加锁成功
问题:
性能较差,各个节点存在网络延迟,失败回滚的场景
public String redisLock2() throws InterruptedException{String lockKey = "locke_key";RLock lock1 = redisson1.getLock(lockKey);RLock lock2 = redisson2.getLock(lockKey);RLock lock3 = redisson3.getLock(lockKey);RedissonRedLock redissonRedLock = new RedissonRedLock(lock1,lock2,lock3);try{boolean b = redissonRedLock.tryLock();if (b){//业务逻辑扣除数据的}}finally {redissonRedLock.unlock();}return "end";}

优化方案:
分段:
在不同的节点存储不同的分段
