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

640?wx_fmt=gif点击上方“蓝字”带你去看小星星

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

640?wx_fmt=png640?wx_fmt=jpeg

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

640?wx_fmt=png640?wx_fmt=jpeg

呵呵,想去看了叫我呀

640?wx_fmt=png640?wx_fmt=jpeg

看来你工作不饱和呀

640?wx_fmt=png640?wx_fmt=jpeg

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

640?wx_fmt=png640?wx_fmt=jpeg

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

640?wx_fmt=png640?wx_fmt=jpeg640?wx_fmt=png


在计算机世界里,对于锁大家并不陌生,在现代所有的语言中几乎都提供了语言级别锁的实现,为什么我们的程序有时候会这么依赖锁呢?这个问题还是要从计算机的发展说起,随着计算机硬件的不断升级,多核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 设置成功
(integer1

redis> SETNX redislock "redislock2"   # 尝试覆盖 redislock ,失败
(integer0

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的服务器时间发生的跳跃,由于锁的过期时间依赖于服务器时间,所以也会出现两个客户端同时获取到锁的情况发生。


640?wx_fmt=png

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

640?wx_fmt=png


640?wx_fmt=png640?wx_fmt=png640?wx_fmt=png

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

640?wx_fmt=png640?wx_fmt=png



640?wx_fmt=png


640?wx_fmt=gif●程序员修神之路--问世间异步为何物?●程序员修神之路--提高网站的吞吐量?●程序员修神之路--?分布式高并发下Actor模型如此优秀?●程序员过关斩将--论商品促销代码的优雅性●程序员过关斩将--请不要随便修改基类●程序员过关斩将--你的面向接口编程一定对吗?●程序员修神之路--高并发下为什么更喜欢进程内缓存●程序员修神之路--高并发优雅的做限流

架构师之路,菜菜与君一起成长

长按识别二维码关注

640?wx_fmt=jpeg

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/316045.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

CF1548A Web of Lies

CF1548A Web of Lies 题意: 题解: 第一感觉是先建边然后跑dfs,但是看数据范围肯定不对,现在我们开始考虑其中的性质 对于第三个操作,我们是要将所有>2的连通块进行判断的,我们考虑对于编号为x的点&…

Abp CLI 上线

Abp vNext CLI 工具随着0.17.0版本的发布已经上线.Abp中文网第一时间制作 Cn.Abp.Cli 工具. 目前提供下载加速, 后续Abp中文网CLI将会提供更多本土化功能, 并随时与官方CLI代码同步.源码(查看原文): https://github.com/cnAbp/abp-cli原文地址:https://mp.weixin.qq…

一篇短文带您了解一下EasyCaching

前言从2017年11月11号在Github创建EasyCaching这个仓库,到现在也已经将近一年半的时间了,基本都是在下班之后和假期在完善这个项目。由于EasyCaching目前只有英文的文档托管在Read the Docs上面,当初选的MkDocs现在还不支持多语言&#xff0c…

[开源]OSharpNS - .net core 快速开发框架 - 简介

OSharpNS全称OSharp Framework with .NetStandard2.0,是一个基于.NetStandard2.0开发的一个.NetCore快速开发框架。这个框架使用最新稳定版的.NetCore SDK(当前是.NET Core 2.2),对 AspNetCore 的配置、依赖注入、日志、缓存、实体…

2019年了,C#发展的怎么样了呢?

C# 8.0我估计大多数程序员对于C# 5.0之后的改进都没有什么太多的认知,的确从C# 5.0开始C#已经没什么太多东西可以从其他语言借鉴,Anders的重心也开始逐步倾斜到TypeScript,所以从5.0引入async之后C#语言发展速度开始减缓了。C#6引入了大量的语…

微软宣布新命令行工具 Windows Terminal 和 WSL2

微软在 Build 2019 已经宣布推出名为Windows Terminal的新款命令行工具 , 这款工具可以访问多种环境的中心位置。例如可以直接访问PowerShell、CMD.EXE、Windows Linux子系统等等,开发者使用此工具可以简化工作。这款新工具支持多选项卡同时连接多个环境或服务器&a…

CF1305E Kuroni and the Score Distribution

CF1305E Kuroni and the Score Distribution 题意: 题解: 一开始想这个题,想法是一开始顺着填1,2,3…然后多删少补 如果1,2,3,4…这样顺延的填,对于akka_{k}kak​k可以…

微软 Build 2019:Windows 10 隐退,IE 重生,Azure 成主角

019 年 5 月 6 日,一年一度的微软 Build 开发者大会正式到来。作为本次大会最为重磅的 Keynote 环节,微软 CEO Satya Nadella 的开场演讲无疑展示了这家巨头对于未来科技发展趋势的洞察和判断,并且也能够让人更加清晰地看到微软在 Azure 云计…

普通人看Build 2019 值得留心的亮点有哪些?

一场Keynote下来,是不是对纳德拉那套“Azure,azure,azure”,云云云的套路感觉有些乏味呢?当然这也不是你的问题,原本Build大会就是为开发者而设的,将近两个小时下来没看到什么能让自己眼前一亮的…

Build 2019:微软正式宣布 .NET 5

今天,我们宣布 .NET Core 3.0 之后的下一个版本将是 .NET 5 。这将是 .NET 系列的下一个重要版本。将来只会有一个 .NET ,您将能够使用它来开发 Windows,Linux,macOS,iOS,Android,tvOS&#xff…

LGV定理

老早就听说,一直没学,今天遇到一个LGV比较裸的题,特地学习一下 选自oi-wiki 定义: e(u,v)表示u到v这条路径上所有边的边权之积(路径计数时,可以将边权都设为1),很多路径统计问题就是用到这一点 引理: 答案…

2019牛客多校Monotonic Matrix

Monotonic Matrix 题意&#xff1a; 问有多少个n * m的矩阵A满足一下情况&#xff1a;答案mod 1e97 矩阵A的所有元素∈{0,1,2}Ai,j<Ai1,jA_{i,j}<A_{i1,j}Ai,j​<Ai1,j​Ai,j<Ai,j1A_{i,j}<A_{i,j1}Ai,j​<Ai,j1​ 题解&#xff1a; 我们先看看这个式子…

WPF框架教程 | 从0到1:使用Caliburn.Micro(WPF和MVVM)开发简单的计算器

之前时间一直在使用Caliburn.Micro这种应用了MVVM模式的WPF框架做开发&#xff0c;是时候总结一下了。Caliburn.Micro(https://blog.csdn.net/lzuacm/article/details/78886436)是一个轻量级的WPF框架&#xff0c;简化了WPF中的不少用法&#xff0c;推荐做WPF开发时优先使用。真…

微软全都要!Win10引入真Linux内核

继将 Bash shell、原生 OpenSSH、WSL 引入 Windows&#xff0c;以及在微软商店提供 Ubuntu、SUSE Linux 和 Fedora 等发行版&#xff0c;正在举办的 Microsoft Build 2019 大会上&#xff0c;微软又宣布了一个重大的决定 —— 将完整的 Linux 内核引入 Windows 10。按照微软的说…

Kafka基本知识整理

首先Kafka是一个分布式消息队列中间件&#xff0c;Apache顶级项目&#xff0c;https://kafka.apache.org/ 高性能、持久化、多副本备份、横向扩展。生产者Producer往队列里发送消息&#xff0c;消费者Consumer从队列里消费消息&#xff0c;然后进行业务逻辑。应用场景主要有&…

牛客练习赛89——牛牛小数点(未解决)

牛牛小数点 题意&#xff1a; 题解&#xff1a; 本题先说结论&#xff1a; 对于一个数x2a∗5b∗px2^a*5^b*px2a∗5b∗p 如果p1,也就是质因子只有2和5&#xff0c;则x是不循环小数&#xff0c;即f(x)0如果p!1,则x是循环的&#xff0c;且循环开始于小数点后第1max{p2,p5p_{2},…

针对.NET Core, Xamarin以及.NET的自动类型安全Rest库: Refit

本文大部分内容是针对Refit官网的翻译。官网地址&#xff1a; https://github.com/reactiveui/refitRefit是一个类似于Retrofit的Restful Api库&#xff0c;使用它&#xff0c;你可以将你的Restful Api定义在接口中。例如&#xff1a;public interface IGitHubApi { [Get(&quo…

用ProGet搭建本地私有NuGet仓库

搭建ProGet下载官网下载Windows版本的Inedo Hub &#xff08;https://inedo.com/proget/download&#xff09;下载下来的软件名&#xff1a; ProGetInstaller.exe安装点击ProGetInstaller.exe&#xff0c;出现如下安装界面Registration 选项选择 Free ;SQL Sever 选项选择 Spec…

CQRS架构下Equinox开源项目分析

一.DDD分层架构介绍本篇分析CQRS架构下的Equinox开源项目。该项目在github上star占有2.4k。便决定分析Equinox项目来学习下CQRS架构。再讲CQRS架构时&#xff0c;先简述下DDD风格&#xff0c;在DDD分层架构中&#xff0c;一般包含表现层、应用程序层(应用服务层)、领域层(领域服…

仿B站(一) 目的分析以及创建 WebAPI + Angular7 项目

前言&#xff1a;本系列文章主要为对所学 Angular 框架的一次微小的实践&#xff0c;对 b站页面作简单的模仿。本系列文章主要参考资料&#xff1a;微软文档&#xff1a;    https://docs.microsoft.com/zh-cn/aspnet/core/getting-started/?viewaspnetcore-2.1&tabsw…