前言

今日上班听到同事在准备面试题分布式锁(准备溜溜球),随即加入了群聊复习了一波,于是有了这篇小作文。

场景

本文中的演示 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 实现的分布式锁

通过上面示例可以看见,分布式锁的使用无非就是 LockUnLock 的操作。我这里直接用编辑器调试进去看了,就不是上 GitHub 上下载代码看了。体验不好,还请担待。

上锁

  1. 循环检测获取锁操作是否过期,过期直接返回 Null, 否则继续步骤二
  2. SetNx 设置值,如果成功,创建分布式锁对象,否则线程等待一会,继续第一步,如此循环

为啥不可以设置唯一值呢?在没有启动自动续时(看门狗机制),业务执行时间超过了锁的过期时间时,会引发问题。

  • 比如说现在 请求1请求2请求3 同时过来,请求1 先抢到了锁,开始执行。
  • 但是 请求1 的业务执行时间比较长,锁已经过期失效了,业务还没有执行完成。这时 请求2 获取到锁,执行自己的业务。就出现了 请求1请求2 并发执行了
  • 请求1 执行完自己的业务的时候,执行解锁操作,因为键值都一样,会误把 请求2 的锁给释放掉,导致故障

通过设置值的唯一,当删除缓存的时候,还需要判断一下值是不是一致,来防止误释放其他锁。

看门狗机制



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

解锁

  1. 通过 lua 脚本匹配 都一样的key, 才能删除

分布式锁的坑参考连接

FreeRedis分布式锁实现以及使用的更多相关文章

  1. 使用redis构建可靠分布式锁

    关于分布式锁的概念,具体实现方式,直接参阅下面两个帖子,这里就不多介绍了. 分布式锁的多种实现方式 分布式锁总结 对于分布式锁的几种实现方式的优劣,这里再列举下 1. 数据库实现方式 优点:易理解 缺 ...

  2. 分布式锁1 Java常用技术方案

    前言:       由于在平时的工作中,线上服务器是分布式多台部署的,经常会面临解决分布式场景下数据一致性的问题,那么就要利用分布式锁来解决这些问题.所以自己结合实际工作中的一些经验和网上看到的一些资 ...

  3. .net 分布式架构之分布式锁实现

    分布式锁 经常用于在解决分布式环境下的业务一致性和协调分布式环境. 实际业务场景中,比如说解决并发一瞬间的重复下单,重复确认收货,重复发现金券等. 使用分布式锁的场景一般不能太多. 开源地址:http ...

  4. 分布式任务&分布式锁(li)

    目前系统中存在批量审批.批量授权等各个操作,批量操作中可能因为处理机器.线程不同,造成刷新缓存丢失授权等信息,如批量审批同一用户权限多个权限申请后,流程平台并发的发送多个http请求到acl不同服务器 ...

  5. jedisLock—redis分布式锁实现

    一.使用分布式锁要满足的几个条件: 系统是一个分布式系统(关键是分布式,单机的可以使用ReentrantLock或者synchronized代码块来实现) 共享资源(各个系统访问同一个资源,资源的载体 ...

  6. ZooKeeper 笔记(6) 分布式锁

    目前分布式锁,比较成熟.主流的方案有基于redis及基于zookeeper的二种方案. 大体来讲,基于redis的分布式锁核心指令为SETNX,即如果目标key存在,写入缓存失败返回0,反之如果目标k ...

  7. 一次基于etcd的分布式锁自动延时失败问题的排查

    今天在测试基于etcd的分布式锁过程中,在测试获取锁后,释放之前超出TTL时长的情况下自动延长TTL这部分功能,在延长指定key的TTL时总是返回404错误信息,在对目标KEY更新TTL时目标KEY已 ...

  8. 基于redis 实现分布式锁的方案

    在电商项目中,经常有秒杀这样的活动促销,在并发访问下,很容易出现上述问题.如果在库存操作上,加锁就可以避免库存卖超的问题.分布式锁使分布式系统之间同步访问共享资源的一种方式 基于redis实现分布式锁 ...

  9. 用Redis实现分布式锁

    Redis有一系列的命令,特点是以NX结尾,NX是Not eXists的缩写,如SETNX命令就应该理解为:SET if Not eXists.这系列的命令非常有用,这里讲使用SETNX来实现分布式锁 ...

随机推荐

  1. linux环境下使用jmeter进行分布式测试

    1.前言 熟练使用jmeter进行性能测试的工程师都知道,jmeter的客户端性能是有点差的.这会导致一个问题,其客户端的性能损耗会干扰到性能测试的结果,而且当线程数/并发大到一定程度时,客户端性能会 ...

  2. Redis 事务 & 消息队列

    Redis 消息队列介绍 什么是消息队列 消息队列(Message Queue)是一种应用间的通信方式,消息发送后可以立即返回,有消息系统来确保信息的可靠传递,消息生产者只管把消息发布到消息队列中而不 ...

  3. 交换机上禁止某个MAC地址通信

    当分析出网络中某台机器中毒时而有不知道它的具体位置,我们可以通过获取其MAC地址然后在交换机上禁止其MAC来达到隔离它的效果.通过ARP表查询IP地址对应的MAC地址,再将该MAC地址加入黑名单过滤. ...

  4. leetcode 1 两数之和 hashmap

    主要是hashmap.还有边插入边查找,提高效率和降低空间复杂度. 之前一直用map,结果发现还有hashmap,效率更高. 注意名称空间为 using namespace __gnu_cxx; 问题 ...

  5. μC/OS-III---I笔记4---软件定时器

    软件定时器是在硬件定时器的基础上开发的,通过将一个硬件定时器进行分频及管理就可以的到多个软件定时器.他和时间管理共同组成了系统的时间管理大部分的内容.系统一开始的系统初始化函数OSInit函数内调用了 ...

  6. 数理统计11:区间估计,t分布,F分布

    在之前的十篇文章中,我们用了九篇文章的篇幅讨论了点估计的相关知识,现在来稍作回顾. 首先,我们讨论了正态分布两个参数--均值.方差的点估计,给出了它们的分布信息,并指出它们是相互独立的:然后,我们讨论 ...

  7. 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 ...

  8. JSON-LD 结构化数据

    JSON-LD 结构化数据 SEO JSON-LD JSON for Linking Data JSON 链接数据 https://json-ld.org/ https://en.wikipedia. ...

  9. Flutter in DartPad

    Flutter in DartPad Gist Sharing https://github.com/dart-lang/dart-pad/wiki/Sharing-Guide https://gis ...

  10. js & bitwise operator

    js & bitwise operator bitwise operator https://github.com/Advanced-Frontend/Daily-Interview-Ques ...