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来实现分布式锁 ...
随机推荐
- 踏上Revit二次开发之路 2 从“HelloWorld”入手
2 从"HelloWorld"入手 在欧特克的官方网页上有个叫<My First Plug-in Training>的项目,号称可以让一个完全没有编程基础的人照着做出一 ...
- 一个方便 LeetCode 复习的脚本
这个脚本半年前就在用了,只不过一直没有公开. 这是一个简易的 LeetCode 自动统计程序, 可自动统计最近提交通过的题目, 并以 Markdown 的形式展示相关的数据. 采用 GitHub Ac ...
- USB2.0协议学习笔记---USB数据包结构
USB包类型和传输过程 USB是一种串行总线,因此数据都是一位一位传输的,如同串口那样,但是USB在真实物理电路上却不是TTL电平,而是一种差分信号采用NRZI编码,就是用变化表示0,不变表示1,同 ...
- js & document.execCommand
js & document.execCommand click copy document.execCommand 已废弃 过时的 此功能已过时.尽管它可能在某些浏览器中仍然可以使用,但不建议 ...
- chmod +x vs chmod 755
chmod +x vs chmod 755 What is the difference between "chmod +x" and "chmod 755"? ...
- Oh My Zsh All In One
Oh My Zsh All In One https://ohmyz.sh/ install # CURL $ sh -c "$(curl -fsSL https://raw.github. ...
- React render twice bug
React render twice bug React bug constructor render twice bug update render twice bug StrictMode htt ...
- Error: Cannot find module 'koa-router'
Error: Cannot find module 'koa-router' koa-router !== koa-route # install OK $ yarn add koa-router h ...
- Web 全栈开发 Redis 面试题
Web 全栈开发 Redis 面试题 Redis 大 key 问题 https://www.cnblogs.com/xgqfrms/p/13601959.html Redis 数据结构 Redis 初 ...
- Spyder & Kite
Spyder & Kite Spyder The Scientific Python Development Environment / IDE https://www.spyder-ide. ...