原文:C# Redis分布式锁的应用 - 叶子栈 - SegmentFault 思否

C# Redis分布式锁的应用

 阅读约 7 分钟

1、背景

我们在开发很多业务场景会使用到锁,例如库存控制,抽奖等。一般我们会使用内存锁的方式来保证线性的执行。但现在大多站点都会使用分布式部署,那多台服务器间的就必须使用同一个目标来判断锁。分布式与单机情况下最大的不同在于其不是多线程而是多进程。

2、演变

[分布式站点使用内存锁方式如下图]

假设有3个用户同时购买一件商品,商品库存只剩下1,如果3个用户同时购买,负载均衡把3个用户分别指向站点1、2、3,那结果将会是3个用户都购买成功。下面我们使用分布式锁解决这个问题。

[分布式站点使用分布式锁如下图]

单台服务器由于可以共享堆内存,因此可以简单的采取内存作为标记存储位置。而多服务器之间都不在同一台物理机上,因此需要将标记存储在一个所有进程都能看到的地方。

3、实现

选型

想想我们实现分布式锁要满足哪些条件?
1、在分布式系统环境下,一个锁在同一时间只能被一个服务器获取;(这是所有分布式锁的基础)
2、高性能的获取锁和释放锁;(锁用完了,要及时释放,以供别人继续使用)
3、具备锁失效机制,防止死锁;(防止因为某些意外,锁没有得到释放,那别人将永远无法使用)
4、具备非阻塞锁特性,即没有获取到锁将直接返回获取锁失败。(满足等待锁的同时,也要满足非阻塞锁特性,便于多样性的业务场景使用)

分布式锁有很多的实现方式:数据库排他锁、Zookeeper、缓存(redis、memcached)等,本文选用Redis实现。原因如下:
1、Redis有很高的性能;
2、Redis命令对此支持较好,实现起来比较方便;
Redis官网上对C#的使用推荐有ServiceStack.Redis和StackExchange.Redis
由于StackExchange.Redis虽然有提供LockTake方法,很方便的使用锁,但是只支持.Net4.5以上。公司很多站点都是3.5和4.0的,所以选用ServiceStack.Redis,自己封装一下。

Redis使用命令介绍

1、SETNX
SETNX key val:当且仅当key不存在时,set一个key为val的字符串,返回1;若key存在,则什么都不做,返回0。
2、expire
expire key timeout:为key设置一个超时时间,单位为second,超过这个时间锁会自动释放,避免死锁。
3、delete
delete key:删除key
在使用Redis实现分布式锁的时候,主要就会使用到这三个命令。

C#代码

1、锁方法

        /// <summary>
/// 分布式锁
/// </summary>
/// <param name="key">锁key</param>
/// <param name="lockExpirySeconds">锁自动超时时间(秒)</param>
/// <param name="waitLockMs">等待锁时间(秒)</param>
/// <returns></returns>
public static bool Lock(string key, int lockExpirySeconds = 10, double waitLockSeconds = 0)
{
//间隔等待50毫秒
int waitIntervalMs = 50; RedisClient client = (RedisClient)RedisClientManager.GetClient(RedisProviderName.RedisCommon, 0); string lockKey = "LockForSetNX:" + key; DateTime begin = DateTime.Now;
using (client)
{
while (true)
{
//循环获取取锁
if (client.SetNX(lockKey, new byte[] { 1 }) == 1)
{
//设置锁的过期时间
client.Expire(lockKey, lockExpirySeconds);
return true;
} //不等待锁则返回
if (waitLockSeconds == 0) break; //超过等待时间,则不再等待
if ((DateTime.Now - begin).TotalSeconds >= waitLockSeconds) break; Thread.Sleep(waitIntervalMs);
}
return false;
}
}

2、释放锁

        /// <summary>
/// 删除锁 执行完代码以后调用释放锁
/// </summary>
/// <param name="key"></param>
public static void DelLock(string key)
{
RedisClient client = (RedisClient)RedisClientManager.GetClient(RedisProviderName.RedisCommon, 0);
string lockKey = "LockForSetNX:" + key;
using (client)
{
client.Del(lockKey);
}
}

3、业务应用代码

            try
{
//取锁,设置key10秒后失效,最大等待锁5秒
if (RedisHelper.Lock("LockKey", 10, 5))
{
//取到锁,执行具体业务
//例如商品购买,库存-1
}
}
finally
{
//释放锁
DelLock("LockKey");
}

【如果文章对你有所帮助,请在评论区留言点赞,以资鼓励。】

阅读 2.8k更新于 2月12日


赞 1

收藏 1


赞赏


分享

关注作者

C# Redis分布式锁的应用 - 叶子栈 - SegmentFault 思否的更多相关文章

  1. Redis 分布式锁的正确打开方式

    前言 分布式锁一般有三种实现方式:1. 数据库乐观锁:2. 基于Redis的分布式锁:3. 基于ZooKeeper的分布式锁.本篇博客将介绍第二种方式,基于Redis实现分布式锁.虽然网上已经有各种介 ...

  2. Redis 分布式锁|从青铜到钻石的五种演进方案

    缓存系列文章: 缓存实战(一):20 图 |6 千字|缓存实战(上篇) 缓存实战(二):Redis 分布式锁|从青铜到钻石的五种演进方案 缓存实战(三):分布式锁中的王者方案 - Redisson 上 ...

  3. 利用redis分布式锁的功能来实现定时器的分布式

    文章来源于我的 iteye blog http://ak478288.iteye.com/blog/1898190 以前为部门内部开发过一个定时器程序,这个定时器很简单,就是配置quartz,来实现定 ...

  4. Redis分布式锁

    Redis分布式锁 分布式锁是许多环境中非常有用的原语,其中不同的进程必须以相互排斥的方式与共享资源一起运行. 有许多图书馆和博客文章描述了如何使用Redis实现DLM(分布式锁管理器),但是每个库都 ...

  5. redis分布式锁和消息队列

    最近博主在看redis的时候发现了两种redis使用方式,与之前redis作为缓存不同,利用的是redis可设置key的有效时间和redis的BRPOP命令. 分布式锁 由于目前一些编程语言,如PHP ...

  6. redis咋么实现分布式锁,redis分布式锁的实现方式,redis做分布式锁 积极正义的少年

    前言 分布式锁一般有三种实现方式:1. 数据库乐观锁:2. 基于Redis的分布式锁:3. 基于ZooKeeper的分布式锁.本篇博客将介绍第二种方式,基于Redis实现分布式锁.虽然网上已经有各种介 ...

  7. spring boot redis分布式锁

    随着现在分布式架构越来越盛行,在很多场景下需要使用到分布式锁.分布式锁的实现有很多种,比如基于数据库. zookeeper 等,本文主要介绍使用 Redis 做分布式锁的方式,并封装成spring b ...

  8. Redis分布式锁的正确实现方式

    前言 分布式锁一般有三种实现方式:1. 数据库乐观锁:2. 基于Redis的分布式锁:3. 基于ZooKeeper的分布式锁.本篇博客将介绍第二种方式,基于Redis实现分布式锁.虽然网上已经有各种介 ...

  9. Redis分布式锁---完美实现

    这几天在做项目缓存时候,因为是分布式的所以需要加锁,就用到了Redis锁,正好从网上发现两篇非常棒的文章,来和大家分享一下. 第一篇是简单完美的实现,第二篇是用到的Redisson. Redis分布式 ...

随机推荐

  1. Linux设备驱动程序 之 tasklet

    多数情况下,为了控制一个寻常的硬件设备,tasklet机制都是实现自己下半部的最佳选择:tasklet可以动态创建,使用方便,执行起来还算快: 声明tasklet tasklet既可以静态的创建,也可 ...

  2. chrome console控制台引入jquery库

    var jqueryJs=document.createElement('script');jqueryJs.setAttribute("type","text/Java ...

  3. WIN7+Qt5.2.0连接oracle数据库的oci驱动的编译

    一.前提安装 1.需要安装QT5.2.0,本介绍安装的是qt-windows-opensource-5.2.0-mingw48_opengl-x86-offline.exe: 本文安装目录:c:\Qt ...

  4. CDH构建大数据平台-配置集群的Kerberos认证安全

     CDH构建大数据平台-配置集群的Kerberos认证安全 作者:尹正杰 版权声明:原创作品,谢绝转载!否则将追究法律责任. 当平台用户使用量少的时候我们可能不会在一集群安全功能的缺失,因为用户少,团 ...

  5. 开发软件-IntelliJ IDEA:百科

    ylbtech-开发软件-IntelliJ IDEA:百科 IDEA 全称 IntelliJ IDEA,是java语言开发的集成环境,IntelliJ在业界被公认为最好的java开发工具之一,尤其在智 ...

  6. iOS获取应用当前Caches目录路径以及当前日期

    NSArray *paths = NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES); NSSt ...

  7. React开发环境配置

    本文以上一篇文章继续配置:npm安装及环境配置<https://www.cnblogs.com/hzb462606/p/11565275.html> 使用 create-react-app ...

  8. OpenLDAP管理命令详解

    一.OpenLDAP命令汇总 ldapsearch:搜索 OpenLDAP 目录树条目. ldapadd:通过 LDIF 格式,添加目录树条目. ldapdelete:删除 OpenLDAP 目录树条 ...

  9. 使用apache commons csv解析del(类似csv)格式文件

    del格式类似csv,我这里的测试文件test.del是从DB2数据库中导出来的一组数据. apache commons csv的maven坐标为: <!-- https://mvnreposi ...

  10. linux 清除/var/spool/mail/root日志存储

    检查出是/var/spool/mail下的root文件过大,然后进到服务器里面一查看,确实是这个问题,就是用cat /dev/null > /var/spool/mail/root,把这个文件清 ...