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

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

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

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

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

在开发很多业务场景会使用到锁,例如库存控制,抽奖等。

例如库存只剩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值作为锁标记值,而是使用一个不易被猜中的随机值, 业内称为token

4.不使用DEL命令释放锁,而是发送script去移除key

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

脚本如下:

if redis.call("get",KEYS1] ==ARGV[1])
then return  redis.call("DEL",KEYS[1])
else return 0
end 

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

/// 

/// Acquires the lock. /// /// /// 随机值 /// /// 非阻塞锁 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; } /// /// Releases the lock. /// /// true, if lock was released, false otherwise. /// Key. /// value 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 lazyConnection = new Lazy(() => { 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命令做分布式锁的设计考量,好记性不如烂笔头。

详解Redis分布式锁相关推荐

  1. Redis系列教程(八):分布式锁的由来、及Redis分布式锁的实现详解

    在很多场景中,我们为了保证数据的最终一致性,需要很多的技术方案来支持,比如分布式事务.分布式锁等.那具体什么是分布式锁,分布式锁应用在哪些业务场景.如何来实现分布式锁呢?今天来探讨分布式锁这个话题. ...

  2. Redis分布式锁详解

    Redis分布式锁详解 1. 分布式所概述 1.1 分布式锁 2. 缓存数据库Redis 2.1 redis简介 2.2 Springboot整合Redis两种方式 3. 实现验证 3.1 环境准备 ...

  3. SpringCloud Alibaba 之 Config配置中心,Redis分布式锁详解

    目录 1.服务配置中心 1.1 服务配置中心介绍 1.2 Nacos Config 实践 1.2.1 Nacos config 入门案例 1.2.2  Nacos 配置动态刷新 1.2.3 配置共享 ...

  4. redis watch使用场景_详解redis中的锁以及使用场景

    分布式锁 什么是分布式锁? 分布式锁是控制分布式系统之间同步访问共享资源的一种方式. 为什么要使用分布式锁? ​ 为了保证共享资源的数据一致性. 什么场景下使用分布式锁? ​ 数据重要且要保证一致性 ...

  5. Redis系列教程(二):详解Redis的存储类型、集群架构、以及应用场景

    高并发架构系列 高并发架构系列:数据库主从同步的3种一致性方案实现,及优劣比较 高并发架构系列:Spring Cloud的核心成员.以及架构实现详细介绍 高并发架构系列:服务注册与发现的实现原理.及实 ...

  6. Redis 分布式锁笔记

    Redis 分布式锁笔记 (公众号:水滴与银弹)深度剖析:Redis分布式锁到底安全吗? 一.初识分布式锁 1.什么是分布式锁 ​ 分布式环境下,我们在写多线程程序时,避免同时操作一个共享变量产生数据 ...

  7. C# Redis 分布式锁 续集 (新增Mysql分布式锁[还有更多种姿势])

    上个文章 <.Net Redis 实现分布式锁 >发出去之后,有很多大佬就有了自己的见解,让我获益良多,又学到了一些新姿势(知识). 上篇主要是采用了 StackExchange.Redi ...

  8. golang常用库之-操作redis go-redis/redis库、 go-redsync/redsync库(redis分布式锁)

    文章目录 golang常用库之-操作redis go-redis/redis库. go-redsync/redsync库(redis分布式锁) 一.什么是go-redis/redis库 go-redi ...

  9. Redis分布式锁(图解 - 秒懂 - 史上最全)

    文章很长,而且持续更新,建议收藏起来,慢慢读! 高并发 发烧友社群:疯狂创客圈(总入口) 奉上以下珍贵的学习资源: 疯狂创客圈 经典图书 : 极致经典 + 社群大片好评 < Java 高并发 三 ...

最新文章

  1. python如何导入txt文件-数据从txt文本导入python
  2. 最实用的Office使用技巧
  3. html表格数据循环展示,MVC在View中循环数据在table中显示
  4. 基于Java的全文索引
  5. 研诺首款D类音频放大器延长便携应用中的电池寿命
  6. 计算机windows无响应,Windows电脑便笺没反应不能用了怎么办?
  7. php curl 链接无效时,在PHP中使用CURL的POST会产生无效的请求错误
  8. erlang四大behaviour之四-supervisor(转载)
  9. oracle建表时添加comment,MYSQL中创建表时可以直接声明comment,ORACLE中似乎不可以,那么oracle该怎样简明地声明comment...
  10. JS数据结构第六篇 --- 二叉树力扣练习题
  11. 【交通标志识别】基于matlab GUI矩匹配算法路标识别【含Matlab源码 1175期】
  12. 怎么在Vue中使用和安装axios
  13. 华为光猫虚拟服务器怎么设置,华为路由器连接光猫怎么设置
  14. 百度网盘 海量资源链接
  15. 阿里云服务器被恶意程序攻击
  16. 运用滤波反投影的方法对图像进行重建matlab仿真
  17. 写脚本的作用是什么?做自媒体,什么样的视频一定要写脚本?
  18. JavaScript的事件系列二keydown,keypress,获取键码------JavaScript学习之路4
  19. oracle报错解决
  20. springboot+校园新闻数据化系统-计算机毕设 附源码99293

热门文章

  1. 爱奇艺才被做空又爆裁员,技术研发为裁员重灾区
  2. 漫画:5分钟弄懂分治算法!它和递归算法的关系!
  3. 蓝牙的自适应跳频技术
  4. ImageNet 的衰落
  5. OpenCV深度神经网络实现人体姿态评估
  6. 使用关键点进行小目标检测
  7. 【OpenCV 4开发详解】轮廓发现与绘制
  8. 使用ExpandableListView以及如何优化view的显示减少内存占用
  9. 自定义Android注解Part3:绑定
  10. sudo找不到命令:修改sudo的PATH路径