如何实现分布式锁,聊聊你的想法?
1.如何实现分布式锁:
防止超卖:
单机版 synchronized(this) {代码块}
但是有多个服务器,Nginx 配置负载均衡之后--开始使用锁就不能是 synchronized,因为他是对于进程来说的--比如是简单的 Jvm 的底层来说的;
如果是多个服务器,多个进程,同一个资源库存减--会出现超卖(意思是 68 买了两次,出现两个 67);
1.1 所以这里我们优化成升级一下:
setnx: 分布式锁,(set if not exist )就加锁
如果存在--这个锁就不加了;
1.2 防止死锁:
finally: 删掉这个锁;
1.3 如果说,在执行这个过程中,还没有删除锁,进程被直接 kill -9 杀死了,
我们直接可以对数据进行原子性操作:----setnx 加一个过期时间,
锁自动释放掉; 也可能是 10 秒钟;
可能一般的互联网公司--并发需求不那么大的,这个 setnx 加防止死锁(过期时间)
也能抗住一些并发;
高可用;高并发
但是我们这次要了解的是,真正的互联网公司怎么使用缓存来做秒杀;
1.保证在互联网公司,高并发可以的状态需要注意的点
保证多个线程争夺时,redis 缓存中过期时间已经失效, 导致第一个线程对库存还未来得及操作,第二个线程已经对--可以拿到新的锁进程操作,接着壹号线程为了防止死锁,删除了第二个线程所在的锁---,以此类推,全乱了;
我自己线程的锁可能被别的线程释放了;
优化:可以将添加锁的时候,我们将数据直接对线程生成一个 id,当我删除的时候,再去对比一下加锁中的 id,和我正在执行的 client 是同一个吗;
锁超时失效的问题;
分线程开始使用—测试主线程是否存在索结构,开展锁续命的操作
这里 的逻辑代码已经有框架给我们完成了,Redission(分布式版本下的 jedis)
多数场景下我们使用的 Jedis,但是使用的过程中,分布式高性能,高并发的架构中,使用的一般是升级版本--Redission
引入依赖:
redission 分布式的实现原理:
1.将加锁的步骤用 Lua 脚本的特性展示出来,变成简单的--原子性操作;
2.加锁成功会开启后台的子线程,是否还持有锁,如果持有延长锁的时间
然后结束释放:
当然在此期间,线程 2 都处于一个尝试 while 循环加锁的操作:
如果说当前系统宕机了,死锁了,可以被找到过期时间自动释放掉;
lua 脚本:原子命令
redission 的对象--然后可以得到相应的 lock ;
底层还是类似于 setnx 的写法,(以为加锁和过期时间必须要一条语句,否则就会出现原子性问题)
但是 lua 脚本可以,redis 支持读取很多条的 redis 命令进行原子性操作,
也就是说一个字符串中存在很多条的 redis 加 lock,过期时间的操作,我们 lua 脚本在执行的过程中会直接实现将数据变化的问题---直接会解决原子性问题
lock 之后,timeTask 的定时任务:
默认
单机版的使用 redis,分布式锁实现非超卖的问题,这个就是互联网公司常见的写法
已经是比较完美的了;但是如果是集群架构那就另当别论了;
同一个商品逻辑加锁被多个线程共同所执行:
分布式锁一般会把并行的问题化为串行的逻辑,但是也会将性能变为安全的;
性能损坏;
redis 的单机的性能 QPS 可以接近 10 万了
可以满足大多数,性能提升(激增)
RedLock:
zookeeper 的结构来实现分布式:
CAP 的结构:
c:一致性
A:可用性
P:分区容错性;
zookeeper 保持的事 Cp:
主线程加锁之后,不会立马告知客户端开启业务代码,而是开始数据通同步到子节点,并且半数以上才可以;(保证数据)
redis 的集群架构: AP 架构;
zab 底层的选举机制---数据一致性(可以得到已经同步到最新数据的节点上,当 leader 节点)、
集群架构:
主从,哨兵等架构就不一样了
版权声明: 本文为 InfoQ 作者【卢卡多多】的原创文章。
原文链接:【http://xie.infoq.cn/article/c738060abb78c87bbbd791ffb】。文章转载请联系作者。
评论