程序员修神之路--redis做分布式锁可能不那么简单

菜哥,复联四上映了,要不要一起去看看?

又想骗我电影票,对不对?

呵呵,想去看了叫我呀

看来你工作不饱和呀

哪有,这两天我刚基于redis写了一个分布式锁,很简单

不管你基于什么做分布式锁,你觉得很简单吗?来来来

在计算机世界里,对于锁大家并不陌生,在现代所有的语言中几乎都提供了语言级别锁的实现,为什么我们的程序有时候会这么依赖锁呢?这个问题还是要从计算机的发展说起,随着计算机硬件的不断升级,多核cpu,多线程,多通道等技术把计算机的计算速度大幅度提升,原来同一时间只能执行一条cpu指令的时代已经过去。随着多条cpu指令可以并行执行的原因,原来不曾出现的资源竞争随着出现,在程序中的体现就是随处可见的多线程环境。比如要更新数据库的一个信息,如果没有并发控制,多个线程同时操作的话,就会出现互相覆盖的现象发生。

锁要解决的就是资源竞争的问题,也就是要把执行的指令顺序化

01为什么需要分布式锁

随着互联网的兴起,现代软件发生了翻天覆地的变化,以前单机的程序,已经支撑不了现代的业务。无论是在抗压,还是在高可用等方面都需要多台计算机协同工作来解决问题。现代的互联网系统都是分布式部署的,分布式部署确实能带来性能和效率上的提升,但为此,我们就需要多解决一个分布式环境下,数据一致性的问题。

当某个资源在多系统之间共享的时候,为了保证大家访问这个资源数据是一致的,那么就必须要求在同一时刻只能被一个客户端处理,不能并发的执行,否者就会出现同一时刻有人写有人读,大家访问到的数据就不一致了。

在分布式系统的时代,传统线程之间的锁机制,就没作用了,系统会有多份并且部署在不同的机器上,这些资源已经不是在线程之间共享了,而是属于进程(服务器)之间共享的资源。

因此,为了解决这个问题,我们就必须引入「分布式锁」。分布式锁,是指在分布式的部署环境下,通过锁机制来让多客户端互斥的对共享资源进行访问。分布式锁的特点如下:

1互斥性和我们本地锁一样互斥性是最基本,但是分布式锁需要保证在不同节点的不同线程的互斥。2可重入性同一个节点上的同一个线程如果获取了锁之后那么也可以再次获取这个锁。3锁超时和本地锁一样支持锁超时,防止死锁。4高效,高可用加锁和解锁需要高效,同时也需要保证高可用防止分布式锁失效,可以增加降级。5支持阻塞和非阻塞和 ReentrantLock 一样支持 lock 和 trylock 以及 tryLock(long timeOut)。

02基于redis分布式锁

如果你通过网络搜索分布式锁,最多的就是基于redis的了。基于redis的分布式锁得益于redis的单线程执行机制,单线程在执行上就保证了指令的顺序化,所以很大程度上降低了开发人员的思考设计成本。但是,基于redis做分布式锁难道真的这么容易吗?

1原子操作

基于redis的分布式锁常用命令是

SETNX key value

只在键 key 不存在的情况下,将键 key的值设置为value 。若键key 已经存在, 则SETNX 命令不做任何动作。SETNX 是『SET if Not eXists』(如果不存在,则 SET)的简写。代码示例:

redis> SETNX redislock "redislock"    # redislock 设置成功
(integer) 1
redis> SETNX redislock "redislock2"   # 尝试覆盖 redislock ,失败
(integer) 0
redis> GET redislock                   # 没有被覆盖
"redislock"

成功获取到锁之后,然后设置一个过期时间(这里避免了客户端down掉,锁得不到释放的问题)

redis> expire redislock 5

成功拿到锁的客户端顺利进行自己的业务,业务代码执行完,然后再删除该key

redis> DEL redislock

如果一切都想想象的那么顺利,程序员TMD就不用996了。假如客户端拿到锁之后,执行设置超时指令之前down掉了(现实总是那么悲剧),那这个锁就永远都释放不了.也许你会想到用 Redis 事务来解决。但是这里不行,因为 expire 是依赖于 setnx 的执行结果的,如果 setnx 没抢到锁,expire 是不应该执行的。事务里没有 if-else 分支逻辑,事务的特点是一口气执行,要么全部执行要么一个都不执行。公司几个亿的业务又被你耽误了...

以上情况的出现是因为两个命令并非一个原子性操作,所以在redis 2.8 版本之后出现了新的命令

SETEX key seconds value

所以现在可以利用一条原子性操作的命令来获取锁

redis> SETEX redislock 60 redislock
OK
redis> GET redislock  # 值
"redislock"
redis> TTL redislock  # 剩余生存时间
(integer) 49

2超时问题

在正常的业务当中,当一个线程获取到锁并且设置了锁的过期时间之后,会出现由于业务代码执行时间过长,锁由于到达超时时间自动释放的情况。自动释放之后,其他的线程就会获取到分布式锁,导致业务代码不会串行执行。如果业务上允许这样的情况偶尔发生,那程序员就开干吧,最后顶多人工干预一下,update 一下数据库。

为了避免这类情况发生,在使用redis分布式锁的时候,业务方应尽量避免长时间执行的代码任务。

如果设置锁的超时时间比较长,在一定程度上可以缓解业务代码执行时间长锁自动到期的问题,但是一旦业务代码down掉,其他等待锁的线程等待的时间会比较长,这种情况下,确保获取到锁的程序不会down 成为了主要问题。

3获取锁失败

当锁被一个调用方获取之后,其他调用方在获取锁失败之后,是继续轮询还是直接业务失败呢?如果是继续轮询的话,同步情况下当前线程会一直处于阻塞状态,所以这里轮询的情况还是建议使用异步。

4可重入性

可重入性是指已经拥有锁的客户端再次请求加锁,如果锁支持同一个客户端重复加锁,那么这个锁就是可重入的。如果基于redis的分布式锁要想支持可重入性,需要客户端封装,可以使用threadlocal存储持有锁的信息。这个封装过程会增加代码的复杂度,所以菜菜不推荐这样做。

5redis挂了

如果在多个客户端获取锁的过程中,redis 挂了怎么办呢?假如一个客户端已经获取到了锁,这个时候redis挂了(假如是redis集群),其他的redis服务器会接着提供服务,这个时候其他客户端可以在新的服务器上获取到锁了,这也导致了锁意义的丢失。有兴趣的同学可以去看看RedLock,这种方案以牺牲性能的代价解决了这个问题。

6时钟跳跃问题

在某些时候,redis的服务器时间发生的跳跃,由于锁的过期时间依赖于服务器时间,所以也会出现两个客户端同时获取到锁的情况发生。

当把以上问题都有解决方案了之后,基于redis的分布式锁才可以放心使用

基于redis设计简单分布式锁容易,但是设计完美分布式锁不易, 还觉得基于redis的分布式锁好做吗?

原文地址:https://www.cnblogs.com/ExMan/p/10811946.html

时间: 2024-07-29 17:32:01

程序员修神之路--redis做分布式锁可能不那么简单的相关文章

程序员修神之路--做好分库分表其实很难之一(继续送书)

菜哥,领导让我开发新系统了 这么说领导对你还是挺信任的呀~ 必须的,为了设计好这个新系统,数据库设计我花了好多心思呢 做一个系统我觉得不应该从数据库入手,应该从设计业务模型开始,先不说这个,说说你的数据库设计的优势 为了高性能我首先设计了分库 分表策略,为以后打下基础 那你的数据量将来会很大吗?分库分表其实涉及到很多难题,你了解过吗? 我觉得分库分表很容易呀 是吗? 是否需要分 说到数据库分库分表,不能一味的追求,我们要明白为什么要进行分库分表才是最终目的.现在网上一些人鼓吹分库分表如何应对了多

程序员修神之路--高并发优雅的做限流(有福利)

菜菜哥,有时间吗? YY妹,什么事? 我最近的任务是做个小的秒杀活动,我怕把后端接口压垮,X总说这可关系到公司的存亡 简单呀,你就做个限流呗 这个没做过呀,菜菜哥,帮妹子写一个呗,事成了,以后有什么要求随便说 那好呀,先把我工资涨一下 那算了,我找别人帮忙吧 跟你开玩笑呢,给哥2个小时时间 谢谢菜菜哥,以后你什么要求我都答应你 好嘞,年轻人就是豪爽 ◆◆ 技术分析 ◆◆ 如果你比较关注现在的技术形式,就会知道微服务现在火的一塌糊涂,当然,事物都有两面性,微服务也不是解决技术,架构等问题的万能钥匙

程序员修神之路--高并发下为什么更喜欢进程内缓存

菜菜哥,告诉你一个好消息 YY妹子,什么好消息,你有男票了? 不是啦,我做的一个网站,以前经常由于访问量太大而崩溃,现在我加上了缓存,很稳定啦 加的什么缓存呢? 我用的redis,号称业界最快的缓存组件了 你觉得现在的缓存操作应该是最快的了吗? 是的,我觉得没有缓存能比这种模式更快了 你先停停,我给你先讲个故事 进程内缓存是指缓存和应用程序在相同地址空间.即同一个进程内.分布式缓存是指缓存和应用程序位于不同进程的缓存,通常部署在不同服务器上. 从前有个机构,机构的主人叫做 CPU,这个机构专门派

程序员修神之路--为什么有了SOA,我们还用微服务?

菜菜哥,我最近需要做一个项目,老大让我用微服务的方式来做 那挺好呀,微服务现在的确很流行 我以前在别的公司都是以SOA的方式,SOA也是面向服务的方式呀 的确,微服务和SOA有相同之处 面向服务的架构(SOA)是一个组件模型,它将应用程序的不同功能单元(称为服务)进行拆分,并通过这些服务之间定义良好的接口和契约联系起来.接口是采用中立的方式进行定义的,它应该独立于实现服务的硬件平台.操作系统和编程语言.这使得构建在各种各样的系统中的服务可以以一种统一和通用的方式进行交互.它是一种设计方法,其中包

使用redis做分布式锁

为什么会有这个需求: 例如一个简单用户的操作,一个线程去修改用户状态,首先在在内存中读出用户的状态,然后在内存中进行修改,然后在存到数据库中.在单线程中,这是没有问题的.但是在多线程中由于读取,修改,写入是三个操作,不是原子操作(同时成功或失败),因此在多线程中会存在数据的安全性问题. 这个问题的话,就可以用分布式锁在限制程序的并发执行. 实现思路: 就是进来一个先占位,当别的线程进来操作的时候,发现有人占位了,就会放弃或者稍后再试. 占位的实现: 在redis中的setnx命令来实现,redi

程序员修仙之路- CXO让我做一个计算器!!

菜菜呀,个税最近改革了,我得重新计算你的工资呀,我需要个计算器,你开发一个吧 CEO,CTO,CFO于一身的CXO X总,咱不会买一个吗? 菜菜 那不得花钱吗,一块钱也是钱呀··这个计算器支持加减乘除运算就行,很简单 CEO,CTO,CFO于一身的CXO (尼玛)那能不能给我涨点工资呀? 菜菜 公司现在很困难,你这个计算器关系到公司的存亡,你要注意呀!! CEO,CTO,CFO于一身的CXO (关于撇开话题佩服的五体投地)好吧X总,我尽快做 菜菜 给你一天时间,我这里着急要用 CEO,CTO,C

程序员修仙之路--优雅快速的统计千万级别uv

菜菜,咱们网站现在有多少PV和UV了? Y总,咱们没有统计pv和uv的系统,预估大约有一千万uv吧 写一个统计uv和pv的系统吧 网上有现成的,直接接入一个不行吗? 别人的不太放心,毕竟自己写的,自己拥有主动权.给你两天时间,系统性能不要太差呀 好吧~~~ 定义PV是page view的缩写,即页面浏览量,通常是衡量一个网络新闻频道或网站甚至一条网络新闻的主要指标.网页浏览数是评价网站流量最常用的指标之一,简称为PV UV是unique visitor的简写,是指通过互联网访问.浏览这个网页的自

一个非科班出生Java程序员的阿里之路:JVM+分布式+Spring+数据库

拿到 阿里实习offer,经历了5次面试,其中4轮技术面,1轮HR面试.在这里分享一下自己的面试经验和学习总结.希望能够帮助更多的小伙伴. 我本科毕业于中南大学信管专业,真正开始学习Java是在大三下学期,研究生就读北航的移动云计算专业.刚开始也是小白,也是一步步成长起来的.需要提的一点是,你将来是需要靠这个吃饭的,所以请对找工作保持十二分的热情,而且越早准备越好. 阿里一面 一面是在上午9点多接到支付宝的面试电话的,因为很期望能够尽快接到阿里的电话,所以非常兴奋.电话接通之后还是非常紧张的,毕

zz 游戏程序员的学习之路(中文版)

游戏程序员的学习之路(中文版) Milo Yip · 1 天前 感谢 @楚天阔(tkchu)编写脚本及整理中文译本数据,自动从英文版生成中文版,SVG / PDF 版本中的书籍图片现在链接至豆瓣页面. Github miloyip/game-programmer 检视/下载中文版 SVG / PDF 「真诚赞赏,手留余香」 赞赏 15 人赞赏 程序员游戏开发书籍推荐 分享 举报 977 文章被以下专栏收录 Milo的编程 进入专栏 97 条评论 写下你的评论 trycatch 这是劝退吧...