Redis作为分布式锁

为什么需要使用分布式锁?

因为在一个分布式系统中,存在多个进程,多个进程之间也存在访问同一个公共资源的情况,多个进程之间的执行顺序是随机性的,此时就需要通过"锁"来达到互斥控制。

比如买票的时候:

此时我们就可以引入redis作为分布式锁

在redis中设置一个特殊的key-value,完成上述买票操作,再把key-value删除,其他服务器在也想买票的时候,就需要尝试设置key-value,如果发现key-value已经存在,就认定为加锁失败(放弃/等待)。执行完之后再删除对应的key,完成解锁操作。

什么是分布式锁

所谓的分布式锁,也是一个/一组单独的服务器程序,提供"加锁"这样的服务。(redis是一种典型的可以用来实现分布式锁的方案,但不是唯一一种,业界可能也会使用mysql/zookeeper这样的组件来实现分布式锁的效果)。

刚才买票的场景,使用mysql的事务也可以批量执行 查询+修改操作,但是分布式系统中,要访问的共享资源不一定是mysql...也可能是执行一段特定的操作,是通过统一的服务器完成执行动作。

引入过期时间

对于上述的设置key-value的方法,如果服务器在执行del突然服务器宕机了,就会出现无法皆所得情况。此时就可以对用来加锁的key设置过期时间,防止服务器宕机无法解锁。

比如使用 set ex nx 命令

引入校验id

上面设置的key-value,服务器1设置了key之后,其他服务器也可以进行删除,为了解决这个问题,我们引入校验id,比如把value设置为服务器的编号,这样其他服务器在获取到value之后,就可以根据value进行判断是不是自己设置的key。

引出lua脚本

此时虽然看上去没有什么问题,但是此时如果有另外一个服务器,在线程1删除之后设置了key,此时线程2再进行删除,就会导致这个另外的服务器的加锁被错误的解锁。

归根到底还是GET和DEL操作不原子的所引起的。

使用事务,能解决上述的问题(redis的事务虽然弱,但是能保证避免插队)

但是实践中往往使用更好的方案,lua脚本

lua是一个编程语言,作为redis内嵌语言,特别轻量,可以使用lua编写一些逻辑,把这个脚本上传到redis服务器上,然后让客户端来控制redi执行上述脚本,也是原子的,相当于一条命令一样

引出看门狗

在加锁的时候给key设置多久的过期时间比较合适呢?

如果设置的短,就可能在你的业务逻辑还没执行完,就释放锁了。

如果设置的太长,就会导致"锁释放的不及时"的问题

更好的办法是"动态续约"。(往往也需要一个服务器单独的线程负责续约这个事情)

比如:初始条件下,设置一个过期时间(比如设置1s)就提前在还剩下300ms(还剩下一段时间,不一定是300ms)的时候,如果当前任务还没执行完,就把过期时间再续上1s.等到时间又快到了,如果没执行完,再续....

如果服务器中途崩溃了,自然就没人负责续约了,此时,锁也就能够在较短的时间里释放了。

引出Redlock算法

使用redis作为分布式锁,redis自身也是有可能挂的,所以必需要保证redis本身的高可用性

redis可以使用集群(主要是解决内存问题)或者哨兵的方式保证高可用性

但是主节点和从节点之间的数据同步是存在延时的,可能主节点收到set请求之后,还没来得及给从节点同步,主节点就挂了,就会导致这次set的数据丢失。

redlock算法(作者给出的一个解决方案) 通过存放冗余的数据来保证数据不丢失

此处加锁,就是按照一定的顺序,针对这些组redis都进行加锁,如果某个节点挂了,继续给下一个节点加锁即可,如果写入key成功的节点个数超过总数的一半,就视为加锁成功。同时解锁也是相同的操作

上述介绍的只是一个简单的"互斥锁"

redis也是可以实现以下这些锁的

1.读写锁

2.公平锁

3.可重入锁

相关推荐
小马爱打代码4 小时前
分布式锁:Redisson的公平锁
分布式
白云偷星子4 小时前
MySQL笔记11
数据库·笔记·mysql
胖咕噜的稞达鸭4 小时前
C++中的父继子承:继承方式实现栈及同名隐藏和函数重载的本质区别, 派生类的4个默认成员函数
java·c语言·开发语言·数据结构·c++·redis·算法
稚辉君.MCA_P8_Java5 小时前
WebSocket 是什么原理?为什么可以实现持久连接?
网络·数据库·websocket·网络协议
凯子坚持 c5 小时前
Redis Hash 全解析:从入门到精通,解锁高性能对象存储的钥匙
redis·bootstrap·哈希算法
小光学长5 小时前
基于Vue的图书馆座位预约系统6emrqhc8(程序+源码+数据库+调试部署+开发环境)带论文文档1万字以上,文末可获取,系统界面在最后面。
前端·数据库·vue.js
code小毛孩5 小时前
如何简单的并且又能大幅度降低任务队列的锁粒度、提高吞吐量?
java·jvm·数据库
风随心飞飞6 小时前
linux 环境下mysql 数据库自动备份和清库 通过crontab 创建定时任务实现mysql数据库备份
linux·数据库·mysql
奥尔特星云大使6 小时前
读写分离中间件简介
数据库·mysql·中间件·读写分离