分布式锁的实现之Redis
前面我们有聊过乐观锁和悲观锁的实现,均是对于单体架构的场景下的实现。那么现在我们来总结看下分布式情况下如何实现锁机制。
常见场景
我们来看下一个场景,假设我现在在分布式系统下要做一个业务逻辑的消费动作,我如何保证我的消费动作只被消费一次不重复消费?有的同学第一时间就想到了MQ,诸如Zookeeper。我们今天暂不谈MQ,那其实核心还是代码执行的锁机制问题。
我们再来看一个场景,我们有个接口需要经常查数据库DB数据,假如场景允许我们经常会对其加一层缓存,并设定过期时间。假设在某一瞬间,缓存过期,但此时并发量又很大,会有大量的请求穿透去数据库请求数据,造成缓存雪崩效应。于是,我们即可以考虑加锁机制,只让一个请求去执行查询DB升级缓存的操作。
基本原理
回顾下我们之前聊到锁的原理,分布式锁也是一样的,要实现它必需满足:
1.互斥:任何时刻只能有一个用户端对其加锁;
2.避免死锁:要充分考虑某用户端在持有锁的期间崩溃,也不能导致后续其余用户端不能加锁;
3.谁加锁谁解锁:加锁和解锁必需是同一个用户端,否则容易出现A用户端把B用户端的锁给解了,导致锁机制失效。
示例实践
我们仅以Redis实现分布式锁为例来说明分布式锁的实现。以单机单机部署Redis的情况为例,假如有分布式Redis集群部署的情况,可以参考Redlock算法的实现。下面我们进入Redis+Lua实现分布式锁的实践。
我们来看示例代码,php为例。
加锁
加锁
注意到代码的每个细节了么?都是至关重要的。上面的set是封装过的,那我们来简单说明一下这个方法吧,该方法分别对应了上面的锁需要满足的条件。比方,NX操作保证了锁的互斥,设置过期时间避免了死锁,唯一请求ID用来标注用户端,在解锁的时候可以用来校验是不是同一个用户端自己的锁。
解锁
解锁这个动作就有趣了,看似简单却暗藏玄机,也是很重要的环节。由于解锁存在一个判断是都本用户端的锁的操作,之后才执行解锁。而这个if判断在高并发的情况下我们不得不考虑操作的原子性,这其实和PHP等其余语言代码考虑高并发的原理是大相径庭(有兴趣的看官也可以思考下,为什么有判断就要保证原子性呢,有哪些可能出现问题的场景)。那我们假如保证操作的原子性呢?第一反应是想到事务?我们这里借助Lua脚原本保证原子性,Redis的eval命令执行Lua脚本保证原子性。注意这里指的是redis在执行整个lua脚本时是排他的,同一时间只能一个线程在执行这个脚本,这点很关键,可以用于一切高并发场景下多线程需要排他的原子性保证,如秒杀的库存判断和扣除,避免库存为负,超卖。redis本身是单线程的(与每个PHP文件的执行是单线程的不同的是,但是PHP服务器(apache/nigix/php-fpm)是多线程的;redis是排队解决每一个进来的请求,后面的解决才是多线程的和多路io复用。所以PHP是有并发问题的,redis是线程安全的),具备原子性,lua本身也具备原子性。
解锁
我们同样来说明下面的解锁代码。其实很简单,就是执行了一个Lua脚本,这个脚本实现了获取当前锁的值,即唯一请求ID值,判断能否同一个用户端的请求ID,假如是,则执行Redis的del操作。

好了,关于Redis实现分布式的锁例子就到这里了,这里只是简单的示例便于了解,实际生产将需要考虑更多的场景和因素,比方集群,Zookeeper方式实现,时间和能力有限,这里就不开展赘述。
参考资料:
浅谈Redis分布式锁实现
两个nginx嵌入lua的例子(nginx-lua模块。根据请求参数不同返回指定5xx错误快照,避免直接在nginx里写逻辑难以维护;直接把PHP请求分发成异步,相似ajax但比其请求减少,启动lua协程异步请求各部分页面内容异步输出。python本身就没有协程。)
Lua在游戏中作为被C调用的逻辑解决脚本例子(协程,与C/C++好集成,热升级。从语言的的笼统层面来说C/C++的笼统低更加适合于底层逻辑的支持,而Lua脚本笼统层次高,更加适合游戏逻辑的实现。脚本语言运行在虚拟机之上,而虚拟机运行在游戏逻辑之上,作为一种解释型语言,我们可以随时修改并及时表现在游戏之中,快速完成开发,当然游戏核心还是C++来。C/C++却做不到,对一个巨大的游戏工程,每次修改都需要重新编译,成本很高。设想一下,假如所有的功能都是使用C/C++实现的话,那么对开发人员来说简直是一场灾难。)
相关传送门:乐观锁与悲观锁的实现
1. 本站所有资源来源于用户上传和网络,如有侵权请邮件联系站长!
2. 分享目的仅供大家学习和交流,您必须在下载后24小时内删除!
3. 不得使用于非法商业用途,不得违反国家法律。否则后果自负!
4. 本站提供的源码、模板、插件等等其他资源,都不包含技术服务请大家谅解!
5. 如有链接无法下载、失效或广告,请联系管理员处理!
6. 本站资源售价只是摆设,本站源码仅提供给会员学习使用!
7. 如遇到加密压缩包,请使用360解压,如遇到无法解压的请联系管理员
开心源码网 » 分布式锁的实现之Redis