Redis分布式锁抽丝剥茧

之前码甲哥写了两篇有关线程安全的文章:

•你管这叫线程安全?•.NET八股文:线程同步技术解读

分布式锁是"线程同步"的延续

最近首度应用"分布式锁",现在想想,分布式锁不是孤立的技能点,这其实就是跨主机的线程同步

进程内跨进程跨主机
Lock/Monitor、SemaphoreSlimMetux、Semaphore分布式锁
用户态线程安全内核态线程安全

单机服务器可以通过共享某堆内存来标记上锁/解锁,线程同步说到底是建立在单机操作系统的用户态/内核态对共享内存的访问控制。

而分布式服务器不是在同一台机器上:跨主机,因此需要将锁标记存储在所有机器进程都能看到的地方。

在开发很多业务场景会使用到锁,例如库存控制,抽奖等。
例如库存只剩1个商品,有三个用户同时打算购买,谁先购买库存立即清零,不能让其他二人也购买成功。

解读分布式锁

我们常说的线程安全、线程同步方案,包括此次的分布式锁都是基于  

“多线程/多进程对特定资源同时有更新操作”。

基本考量

1.分布式系统,一个锁在同一时间只能被一个服务器获取 (这是分布式锁的基础)2.具备锁失效机制,防止死锁 (防止某些意外,锁没有得到释放,别人也无法得到锁)

Redis SET resource-name anystring NX EX max-lock-time
是一种最简单的分布式锁实现方案。

SET 命令支持多个参数:

•EX seconds-- 设置过期时间(s)•NX -- 如果key不存在,则设置 ......
因为SET命令参数可以替代SETNX,SETEX,GETSET,这些命令在未来可能被废弃。

上面的命令返回OK(或经过重试),客户端就获取到这个锁;
使用DEL命令解锁;到达超时时间会自动释放锁。

在解锁时,增加一些设计,让系统更加健壮:

3.不要使用固定的String值作为锁标记值,而是使用一个不易被猜中的随机值, 业内称为token4.不使用DEL命令释放锁,而是发送script去移除key

第3、4点是为了解决 :“锁提前过期,客户端A还没有执行完,然后客户端B获取了锁,这时客户端A执行完了,会不会在删锁的时候把B的锁给删掉”            -- 4是3技术上的推荐实现。

脚本如下:

if redis.call("get",KEYS1] ==ARGV[1])
thenreturn  redis.call("DEL",KEYS[1])
elsereturn 0
end

下面使用StackExchange.Redis 写了基于以上考量的代码示例:

/// <summary>
/// Acquires the lock.
/// </summary>
/// <param name="key"></param>
/// <param name="token">随机值</param>
/// <param name="expireSecond"></param>/// <param name="waitLockSeconds">非阻塞锁</param>
static bool Lock(string key, string token,int expireSecond=10, double waitLockSeconds = 0)
{var waitIntervalMs = 50;bool isLock;DateTime begin = DateTime.Now;do{isLock = Connection.GetDatabase().StringSet(key, token, TimeSpan.FromSeconds(expireSecond), When.NotExists);if (isLock)return true;//不等待锁则返回if (waitLockSeconds == 0) break;//超过等待时间,则不再等待if ((DateTime.Now - begin).TotalSeconds >= waitLockSeconds) break;Thread.Sleep(waitIntervalMs);} while (!isLock);return false;}/// <summary>  
/// Releases the lock.  
/// </summary>  
/// <returns><c>true</c>, if lock was released, <c>false</c> otherwise.</returns>  
/// <param name="key">Key.</param>  
/// <param name="value">value</param>  
static bool UnLock(string key, string value)
{string lua_script = @"  if (redis.call('GET', KEYS[1]) == ARGV[1]) then  redis.call('DEL', KEYS[1])  return true  else  return false  end  ";try{var res = Connection.GetDatabase().ScriptEvaluate(lua_script,new RedisKey[] { key },new RedisValue[] { value });return (bool)res;}catch (Exception ex){Console.WriteLine($"ReleaseLock lock fail...{ex.Message}");return false;}
}private static Lazy<ConnectionMultiplexer> lazyConnection = new Lazy<ConnectionMultiplexer>(() =>{ConfigurationOptions configuration = new ConfigurationOptions{AbortOnConnectFail = false,ConnectTimeout = 5000,};configuration.EndPoints.Add("10.100.219.9", 6379);return ConnectionMultiplexer.Connect(configuration.ToString());}); public static ConnectionMultiplexer Connection => lazyConnection.Value;

以上代码新增了第五点考量:

5. 为避免无限制抢锁,增加了非阻塞锁:轮询_s等待锁,未等到则不再抢锁

使用方式:

下面并行开启三个任务,同时减少库存:

static void Main(string[] args)
{// 尝试并行执行3个任务Parallel.For(0, 3, x =>{string token = $"loki:{x}";bool isLocked = Lock("loki", token, 5, 10);if (isLocked){Console.WriteLine($"{token} begin reduce stocks (with lock) at {DateTime.Now}.");Thread.Sleep(1000);Console.WriteLine($"{token} release lock {UnLock("loki", token)} at {DateTime.Now}. ");}else{Console.WriteLine($"{token} begin reduce stocks at {DateTime.Now}.");}});
}
可以看到三个并行任务依次获取/释放锁

输出总结

本文从基础的线程安全、线程同步,认识到分布式锁是跨主机的资源线程/进程同步方案, 以步步为营的风格 演示了RedisSET命令做分布式锁的设计考量,好记性不如烂笔头。

# 更多精彩

  • 面试八股文:你写过自定义任务调度器吗?

  • 看过这么多爆文,依旧走不好异步编程这条路?

  • 你管这叫"线程安全"?

  • 你怕是对MD5算法有误解

  • 全网最通透的“闭包”认知 · 跨越语言

  • 鹅厂二面,Nginx回忆录

资深.Net码农,现大厂架构部后端开发,文章不追热点、不记流水,尝试以流畅自然的思路解构晦涩难懂的技术,如理解有问题或偏差,请留言赐教(相当真诚☺️)。

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

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

相关文章

MFC,QT与WinForm,WPF简介

编程语言的组成 编程语言做为一种语言自然和英语这些自然语言有类似的地方.学英语时我们知道要先记26个字母,然后单词及其发音,接下来就是词组,句子.反正简单的说就是记单词,熟悉词法,句法.接下来就是应用了,听说读写.而使用相同语言的人大脑里都有个翻译器,可以把自己的想法翻…

让苹果CEO库克折服的程序员仅10岁!?

▲数据汪特别推荐点击上图进入玩酷屋在国外&#xff0c;编程教育课早已普及&#xff0c;美国、英国、新加坡等国家少儿编程已进入小学标准必修课程体系。韩国、日本也相继在2017年和2020年开展一年级至初三的编程教育普及。美国总统孙女&#xff0c;五岁开始学习在电脑上编程最…

WPF Datagrid合并表头的思路

在使用datagrid的时候&#xff0c;有很多情况下&#xff0c;都需要合并表头&#xff0c;多行表头之类的操作。这就需要我们自定义列了。本文给出一个思路&#xff0c;可以实现此需要&#xff0c;只是本人对这个研究不很明白&#xff0c;只是只是实现&#xff0c;仅此而已。下面…

linux firefox 插件开发教程,Firefox插件(plugins)开发实用指南

了解Firefox插件的开发知识的时候&#xff0c;找到了这篇文章。的确让我少走了弯路&#xff0c;比如&#xff0c;我的NP插件的DLL&#xff0c;之前不是NP开头的&#xff0c;在此非常感谢原作者&#xff0c;您文章让我节约了不少时间。转载传播此文章&#xff0c;希望对准备研究…

黑马程序员--IO【1】

------------------- android培训、java培训、期待与您交流&#xff01;------------------ /*System:类中的方法和属性都是静态的。out:标准输出&#xff0c;默认是控制台。in&#xff1a;标准输入&#xff0c;默认是键盘。描述系统一些信息。获取系统属性信息&#xff1a;Pro…

C# 爬虫:疫情实时信息图

运行结果&#xff1a;using System; using System.Drawing; using System.Text; using NSoup; using NSoup.Nodes; using System.IO; using System.Net; using System.Text.RegularExpressions; using System.Windows.Forms;namespace Pneumonia {public partial class MainFor…

史上最黑科技 | 人造肌肉、DNA折叠、柔性外骨骼…

全世界只有3.14 % 的人关注了数据与算法之美说起被机器人支配&#xff0c;一部分人恐惧得不行&#xff0c;另一部人只当个笑话&#xff0c;但无论哪一边&#xff0c;都忍不住想看看这个神秘的领域正在发生什么&#xff0c;这是本能&#xff1a;“我得盯着你&#xff0c;如果哪天…

linux l显示详细信息,fdisk -l显示信息详解

fdisk -l显示信息详解[rootwww.linuxidc.com ~]# fdisk -lDisk /dev/sda: 10.7 GB, 10737418240 bytes255 heads, 63 sectors/track, 1305 cylindersUnits cylinders of 16065 * 512 8225280 bytesSector size (logical/physical): 512 bytes / 512 bytesI/O size (minimum/o…

今天正式开通51CTO技术博客

今天正式开通51CTO技术博客&#xff0c;今后会把自己工作和学习过程中的点点滴滴记录在此博客中&#xff01;转载于:https://blog.51cto.com/dashuai/1160235

ML.NET Cookbook:(17)如何在分类数据上训练模型?

一般来说&#xff0c;所有的ML.NET学习器都希望这些特征是一个浮点向量。因此&#xff0c;如果您的一些数据不是一个float&#xff0c;您需要将其转换为float。如果我们的数据包含“分类”特征&#xff08;比如“enum”&#xff09;&#xff0c;我们需要以某种方式对它们进行“…

有钱真的能为所欲为,微软用75亿美元解决了比尔盖茨的“心头大患”

全世界只有3.14 % 的人关注了数据与算法之美2018年6月4日&#xff0c;微软在官方博客上宣布&#xff1a;以75 亿美元的价格收购了全球最大的开源代码托管平台GitHub。谁也没想到&#xff0c;微软和开源这场长达几十年的战争&#xff0c;到最后双方竟然喜结连理了。不过&#xf…

linux 逻辑卷 pe size 4.00 mib大小怎么改,linux逻辑卷的建立

开始的时候系统各目录的挂载情况如下&#xff1a;增加了一个8G大小的scsi磁盘启动系统之后。[rootpoint1 ~]#fdisk &#xfffd;Cl增加了一个sdb设别。一、分区并格式化磁盘[rootpoint1 ~]#fdisk /dev/sdb输入m是显示帮助菜单输入n创建一个分区&#xff0c;选择p创建主分区&…

svn 自动同步到web站点目录post-commit.bat

为什么80%的码农都做不了架构师&#xff1f;>>> 需求分析: 在服务器上搭建了visualSVN server &#xff0c;然后为了统一测试环境&#xff0c;又在服务器上搭建了web server。现在的需求是&#xff0c;当开发人员通过svn提交更新的时候&#xff0c;让svn自动将文件…

.NET之模型绑定和验证

介绍模型绑定就是接收将来自HTTP请求的数据映射到模型的过程。如果找不到模型属性的值&#xff0c;并不会报错&#xff0c;而是给该属性设置默认值。示例&#xff1a;比如我们有一个接口为[HttpGet("{id}")] public ActionResult<Pet> GetById(int id, bool do…

每日一笑 | 大学教室的真实写照...

全世界只有3.14 % 的人关注了数据与算法之美&#xff08;图片来源于网络&#xff0c;侵权删&#xff09;

linux调用v4l2获取视频,嵌入式Linux:V4L2视频采集操作流程和接口说明

一般操作流程(视频设备)&#xff1a;1. 打开设备文件。 int fdopen("/dev/video0",O_RDWR);2. 取得设备的capability&#xff0c;看看设备具有什么功能&#xff0c;比如是否具有视频输入,或者音频输入输出等。VIDIOC_QUERYCAP,struct v4l2_capability3. 选择视频输入…

面向对象技术——UML

UML&#xff0c;统一建模语言是一种可视化建模语言。 UML包括九种类型的图&#xff1a;用例图&#xff0c;类图&#xff0c;对象图&#xff0c;顺序图&#xff0c;协作图&#xff0c;状态图&#xff0c;活动图&#xff0c;构件图&#xff0c;及部署图&#xff0c;各种图示系统在…

面向.NET开发人员的Dapr——前言

Foreword前言With the wave of cloud adoption well underway, there is a major shift happening towards “cloud native” development, often built with microservice-architectures. These microservices are both stateless and stateful, and run on the cloud and…

美国返还中国文物,阿里谣言粉碎机获奖,教育部规范研究生培养,腾讯严打微信跑分活动,推动降低港澳漫游费,这就是今天的大新闻。...

今天是3月1日农历正月廿五今天星期五相信大家都很舍不得放下工作下面是今天的大新闻美国返还361件中国文物&#xff08;中国日报&#xff09;当地时间2月28日&#xff0c;美国政府向中国返还361件&#xff08;套&#xff09;流失文物。这些中国流失文物&#xff0c;由美国联邦调…

Linux怎么更新镜像,利用 Zsync 更新已有的 Ubuntu ISO 镜像

对! 是升级iso镜像, 不是升级系统. 从旧的镜像升级到新的镜像.可能有点迟了~大家都down好了镜像~ 我现在才有心情和时间写blog哦~由alpha的iso升到正式版都可以. 呃~ 当然,估计由alpha开始的话,下载量也与直接下载正式版区别不大~这么多人下载, 速度当然会慢喇~ 用zsync来升级镜…