redis分布式锁详解

redis分布式锁就几个方法

1、setnx(key,value) 返回boolean   1为获取锁 0为没获取锁

2、expire()  设置锁的有效时间

3、getSet(key,value) 获取锁当前key对应的锁的有效时间

4、deleteKey() 删除锁

  • setnx(lockkey, 当前时间+过期超时时间),如果返回 1,则获取锁成功;如果返回 0 则没有获取到锁,转向 2。
  • get(lockkey) 获取值 oldExpireTime ,并将这个 value 值与当前的系统时间进行比较,如果小于当前系统时间,则认为这个锁已经超时,可以允许别的请求重新获取,转向 3。
  • 计算 newExpireTime = 当前时间+过期超时时间,然后 getset(lockkey, newExpireTime) 会返回当前 lockkey 的值currentExpireTime。
  • 判断 currentExpireTime 与 oldExpireTime 是否相等,如果相等,说明当前 getset 设置成功,获取到了锁。如果不相等,说明这个锁又被别的请求获取走了,那么当前请求可以直接返回失败,或者继续重试。
  • 在获取到锁之后,当前线程可以开始自己的业务处理,当处理完毕后,比较自己的处理时间和对于锁设置的超时时间,如果小于锁设置的超时时间,则直接执行 delete 释放锁;如果大于锁设置的超时时间,则不需要再锁进行处理。

过程分析:

当A通过setnx(lockkey,currenttime+timeout)命令能成功设置lockkey时,即返回值为1,过程与原理1一致;
当A通过setnx(lockkey,currenttime+timeout)命令不能成功设置lockkey时,这是不能直接断定获取锁失败;因为我们在设置锁时,设置了锁的超时时间timeout,当当前时间大于redis中存储键值为lockkey的value值时,可以认为上一任的拥有者对锁的使用权已经失效了,A就可以强行拥有该锁;具体判定过程如下;
A通过get(lockkey),获取redis中的存储键值为lockkey的value值,即获取锁的相对时间lockvalueA
lockvalueA!=null && currenttime>lockvalue,A通过当前的时间与锁设置的时间做比较,如果当前时间已经大于锁设置的时间临界,即可以进一步判断是否可以获取锁,否则说明该锁还在被占用,A就还不能获取该锁,结束,获取锁失败;
步骤4返回结果为true后,通过getSet设置新的超时时间,并返回旧值lockvalueB,以作判断,因为在分布式环境,在进入这里时可能另外的进程获取到锁并对值进行了修改,只有旧值与返回的值一致才能说明中间未被其他进程获取到这个锁
lockvalueB == null || lockvalueA==lockvalueB,判断:若果lockvalueB为null,说明该锁已经被释放了,此时该进程可以获取锁;旧值与返回的lockvalueB一致说明中间未被其他进程获取该锁,可以获取锁;否则不能获取锁,结束,获取锁失败。

 

举个例子:

现在有两台redis服务器,两个进程同时访问redis代理,并且代理按顺序指向redis服务器,当访问A服务器时候,在setnx()执行后并且expiro()执行前A宕机了,这时候B在执行的时候就先去判断 系统当前时间是否大于oldtime+expiro()设置的时间设置的时间,如果大于oldtime+expiro()设置的时间,可以证明A事物的锁使用权已经失效了,我们就可以删除事物A的锁,然后在事物B上重新生成个锁。

 

 

1、什么是分布式锁

分布式锁就是 多个服务器都有redis,但是共用同一套资源。

2、分布式锁实现原理

主要就两个方法 

1、getlock() 获取锁方法

2、releaselock()释放锁方法

然后我们看一下 getlock()方法是怎么写的

当生成锁的时候会有一个key也就是上面的taskId,existskey()意思是在分布式的key中是否有和taskId一致的(这个taskId可以认识取随机数),如果没有一致的就获取锁

然后是releaselock()这个方法里就执行了 deletekey()删除key方法

 

上述做法有几个问题

1、当获取锁之后同时还没有删除key,这时候断网了,那么就会导致我这个锁永远都无法delete

2、同一时间被不同服务器的调用获取到锁

先说第一个问题:我们可以通过设置一个释放锁的时间来解决这个问题比如2秒,如果断网了或者其他问题2秒之后自动释放

在说第二个问题:获取锁进行原子性,也就是说在获取锁的时候多一步操作,就是当前key不存在时候才可以获取锁

SET my_key my_value NX PX milliseconds

其中,NX表示只有当键key不存在的时候才会设置key的值,PX表示设置键key的过期时间,单位是毫秒。

就算是这样还会有问题:

3、当时间设置2秒,但是我的逻辑代码执行了3秒,这时候这个锁会被别的请求获取到

其实解决也很简单,我们在释放锁的时候设置一个随机数,在进行deletekey的时候进行随机数的判断如果相同才delete当然这个随机数是在逻辑方法执行完之后生成的

 

 

redis AOF持久化 配置方式

# Redis支持三种不同的刷写模式:
# appendfsync always #每次收到写命令就立即强制写入磁盘,是最有保证的完全的持久化,但速度也是最慢的,一般不推荐使用。
appendfsync everysec #**每秒钟强制写入磁盘一次,在性能和持久化方面做了很好的折中,是受推荐的方式。**
# appendfsync no     #完全依赖OS的写入,一般为30秒左右一次,性能最好但是持久化最没有保证,不被推荐。

 

 

说一个我们项目中用到的redis实现场景:

占用资源的时候由于频繁操作mysql会出现性能问题,因为资源的数据量是在太多了过千万,所以通过读写分离去处理,这时候通过redis做一个缓存机制,首先当占用排期的时候,订单会传skuId和一些信息,这时候资源方通过skuId去redis中找到这些skuid并且去修改是否被占状态,然后将这次请求扔到队列中,每分钟跑批一次存入mysql,如果在redis中没有找到skuId,则直接去mysql中查找,然后在存入redis中,这样就做到了读写分离,大大的提高了查询效率。

 

在说一下资源这边的存储方式 目前是 mysql + es + redis

mysql 主要存资源的价格(目前价格只挂在车型和关键字上 车型大概有300多个),但是资源的维度除了有车型还有品牌,媒体等等,只说品牌就有3000多个这样一来资源的数量就回笛卡尔积,这样一来如果都存到mysql肯定扛不住,所以我们通过es来做品牌维度的广告资源存储,其实我们的es中也存了价格信息,当我们广告资源修改价格的时候,先修改mysql中对应资源的价格,同时跑批去修改es中对应的资源价格,这还需要些时间,当然这个时间是和业务方沟通好的,当我们修改价格2分钟后才生效。至于redis,我们只存资源占用的状态以及基本信息。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值