FreeRedis分布式锁实现以及使用
前言
今日上班听到同事在准备面试题分布式锁(准备溜溜球),随即加入了群聊复习了一波,于是有了这篇小作文。
场景
本文中的演示 DEMO, 以下订单减库存为例。
无锁裸奔表现
示例代码:
先来模拟一个库存服务呗!
/// <summary>
/// 模拟库存服务
/// </summary>
public class StockService
{
private static RedisClient cli = new RedisClient("127.0.0.1:6379");
/// <summary>
/// 减库存操作
/// </summary>
/// <param name="goodsCount">商品数</param>
/// <returns></returns>
public bool ReduceStock(int goodsCount)
{
var stockCount = cli.Get<int>("StockCount");
if (stockCount > 0 && stockCount >= goodsCount)
{
stockCount -= goodsCount;
cli.Set("StockCount", stockCount, 10);
Console.WriteLine($"线程Id:{Thread.CurrentThread.ManagedThreadId},抢购成功!库存数:{stockCount}");
return true;
}
Console.WriteLine($"线程Id:{Thread.CurrentThread.ManagedThreadId},抢购失败!");
return false;
}
}
模拟500个并发请求,开始测试。
static void Main(string[] args)
{
var stockService = new StockService();
// 初始化库存
var cli = new RedisClient("127.0.0.1:6379");
cli.Set("StockCount", 10, 10);
// 模拟 500 个并发
Parallel.For(0, 500, (i) => { Task.Run(() => { stockService.ReduceStock(1); }); });
}
执行完成后,结果如下图所示:

我们的库存只有 10 个,截图可见,至少有 29 个请求抢购成功了,出现了超卖的现象。
上分布式锁表现
针对无锁情况下出现的并发问题,如果是单体应用,用 lock 可以解决,但不适用于分布式应用。FreeRedis 中已有现成实现的分布式锁,我们先来看看是如何使用的吧!
修改一下订单服务代码:
/// <summary>
/// 模拟库存服务
/// </summary>
public class StockService
{
private static RedisClient cli = new RedisClient("127.0.0.1:6379");
private static readonly string _distributedLockKey = "DISTRIBUTEDLOCKKEY";
/// <summary>
/// 减库存操作
/// </summary>
/// <param name="goodsCount">商品数</param>
/// <returns></returns>
public bool ReduceStock(int goodsCount)
{
// 取锁
var lockObj = cli.Lock(_distributedLockKey, 1);
if (lockObj != null)
{
var stockCount = cli.Get<int>("StockCount");
if (stockCount > 0 && stockCount >= goodsCount)
{
stockCount -= goodsCount;
cli.Set("StockCount", stockCount, 10);
Console.WriteLine($"线程Id:{Thread.CurrentThread.ManagedThreadId},抢购成功!库存数:{stockCount}");
lockObj.Unlock(); // 解锁
return true;
}
Console.WriteLine($"线程Id:{Thread.CurrentThread.ManagedThreadId},抢购失败!");
lockObj.Unlock(); // 解锁
}
return false;
}
}
执行结果如下所示:

从输出结果中可以看出,库存有序的扣除中,确实只有 10 个请求是抢购成功。
看看 FreeRedis 实现的分布式锁
通过上面示例可以看见,分布式锁的使用无非就是 Lock 和 UnLock 的操作。我这里直接用编辑器调试进去看了,就不是上 GitHub 上下载代码看了。体验不好,还请担待。
上锁

- 循环检测获取锁操作是否过期,过期直接返回
Null, 否则继续步骤二 SetNx设置值,如果成功,创建分布式锁对象,否则线程等待一会,继续第一步,如此循环
为啥不可以设置唯一值呢?在没有启动自动续时(看门狗机制),业务执行时间超过了锁的过期时间时,会引发问题。
- 比如说现在
请求1,请求2,请求3同时过来,请求1先抢到了锁,开始执行。 - 但是
请求1的业务执行时间比较长,锁已经过期失效了,业务还没有执行完成。这时请求2获取到锁,执行自己的业务。就出现了请求1和请求2并发执行了 - 当
请求1执行完自己的业务的时候,执行解锁操作,因为键值都一样,会误把请求2的锁给释放掉,导致故障
通过设置值的唯一,当删除缓存的时候,还需要判断一下值是不是一致,来防止误释放其他锁。
看门狗机制


- 定时执行
Refresh方法 - 通过
lua脚本设置新的过期时间,不成功的话(已解锁),删除定时器
解锁

- 通过
lua脚本匹配键和值都一样的key, 才能删除
分布式锁的坑参考连接
FreeRedis分布式锁实现以及使用的更多相关文章
- 使用redis构建可靠分布式锁
关于分布式锁的概念,具体实现方式,直接参阅下面两个帖子,这里就不多介绍了. 分布式锁的多种实现方式 分布式锁总结 对于分布式锁的几种实现方式的优劣,这里再列举下 1. 数据库实现方式 优点:易理解 缺 ...
- 分布式锁1 Java常用技术方案
前言: 由于在平时的工作中,线上服务器是分布式多台部署的,经常会面临解决分布式场景下数据一致性的问题,那么就要利用分布式锁来解决这些问题.所以自己结合实际工作中的一些经验和网上看到的一些资 ...
- .net 分布式架构之分布式锁实现
分布式锁 经常用于在解决分布式环境下的业务一致性和协调分布式环境. 实际业务场景中,比如说解决并发一瞬间的重复下单,重复确认收货,重复发现金券等. 使用分布式锁的场景一般不能太多. 开源地址:http ...
- 分布式任务&分布式锁(li)
目前系统中存在批量审批.批量授权等各个操作,批量操作中可能因为处理机器.线程不同,造成刷新缓存丢失授权等信息,如批量审批同一用户权限多个权限申请后,流程平台并发的发送多个http请求到acl不同服务器 ...
- jedisLock—redis分布式锁实现
一.使用分布式锁要满足的几个条件: 系统是一个分布式系统(关键是分布式,单机的可以使用ReentrantLock或者synchronized代码块来实现) 共享资源(各个系统访问同一个资源,资源的载体 ...
- ZooKeeper 笔记(6) 分布式锁
目前分布式锁,比较成熟.主流的方案有基于redis及基于zookeeper的二种方案. 大体来讲,基于redis的分布式锁核心指令为SETNX,即如果目标key存在,写入缓存失败返回0,反之如果目标k ...
- 一次基于etcd的分布式锁自动延时失败问题的排查
今天在测试基于etcd的分布式锁过程中,在测试获取锁后,释放之前超出TTL时长的情况下自动延长TTL这部分功能,在延长指定key的TTL时总是返回404错误信息,在对目标KEY更新TTL时目标KEY已 ...
- 基于redis 实现分布式锁的方案
在电商项目中,经常有秒杀这样的活动促销,在并发访问下,很容易出现上述问题.如果在库存操作上,加锁就可以避免库存卖超的问题.分布式锁使分布式系统之间同步访问共享资源的一种方式 基于redis实现分布式锁 ...
- 用Redis实现分布式锁
Redis有一系列的命令,特点是以NX结尾,NX是Not eXists的缩写,如SETNX命令就应该理解为:SET if Not eXists.这系列的命令非常有用,这里讲使用SETNX来实现分布式锁 ...
随机推荐
- linux环境下使用jmeter进行分布式测试
1.前言 熟练使用jmeter进行性能测试的工程师都知道,jmeter的客户端性能是有点差的.这会导致一个问题,其客户端的性能损耗会干扰到性能测试的结果,而且当线程数/并发大到一定程度时,客户端性能会 ...
- Redis 事务 & 消息队列
Redis 消息队列介绍 什么是消息队列 消息队列(Message Queue)是一种应用间的通信方式,消息发送后可以立即返回,有消息系统来确保信息的可靠传递,消息生产者只管把消息发布到消息队列中而不 ...
- 交换机上禁止某个MAC地址通信
当分析出网络中某台机器中毒时而有不知道它的具体位置,我们可以通过获取其MAC地址然后在交换机上禁止其MAC来达到隔离它的效果.通过ARP表查询IP地址对应的MAC地址,再将该MAC地址加入黑名单过滤. ...
- leetcode 1 两数之和 hashmap
主要是hashmap.还有边插入边查找,提高效率和降低空间复杂度. 之前一直用map,结果发现还有hashmap,效率更高. 注意名称空间为 using namespace __gnu_cxx; 问题 ...
- μC/OS-III---I笔记4---软件定时器
软件定时器是在硬件定时器的基础上开发的,通过将一个硬件定时器进行分频及管理就可以的到多个软件定时器.他和时间管理共同组成了系统的时间管理大部分的内容.系统一开始的系统初始化函数OSInit函数内调用了 ...
- 数理统计11:区间估计,t分布,F分布
在之前的十篇文章中,我们用了九篇文章的篇幅讨论了点估计的相关知识,现在来稍作回顾. 首先,我们讨论了正态分布两个参数--均值.方差的点估计,给出了它们的分布信息,并指出它们是相互独立的:然后,我们讨论 ...
- HTML a Tag All In One
HTML a Tag All In One HTML <a> target https://developer.mozilla.org/en-US/docs/Web/HTML/Elemen ...
- JSON-LD 结构化数据
JSON-LD 结构化数据 SEO JSON-LD JSON for Linking Data JSON 链接数据 https://json-ld.org/ https://en.wikipedia. ...
- Flutter in DartPad
Flutter in DartPad Gist Sharing https://github.com/dart-lang/dart-pad/wiki/Sharing-Guide https://gis ...
- js & bitwise operator
js & bitwise operator bitwise operator https://github.com/Advanced-Frontend/Daily-Interview-Ques ...