C# Redis分布式锁的应用

栏目: ASP.NET · 发布时间: 5年前

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

1、背景

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

2、演变

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

C# Redis分布式锁的应用

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

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

C# Redis分布式锁的应用

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

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");
            }

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


以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,也希望大家多多支持 码农网

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

数据挖掘导论

数据挖掘导论

(美)Pang-Ning Tan、Michael Steinbach、Vipin Kumar / 机械工业出版社 / 2010-9 / 59.00元

本书全面介绍了数据挖掘的理论和方法,着重介绍如何用数据挖掘知识解决各种实际问题,涉及学科领域众多,适用面广。 书中涵盖5个主题:数据、分类、关联分析、聚类和异常检测。除异常检测外,每个主题都包含两章:前面一章讲述基本概念、代表性算法和评估技术,后面一章较深入地讨论高级概念和算法。目的是使读者在透彻地理解数据挖掘基础的同时,还能了解更多重要的高级主题。 本书特色 ·包含大量的图表、......一起来看看 《数据挖掘导论》 这本书的介绍吧!

在线进制转换器
在线进制转换器

各进制数互转换器

HTML 编码/解码
HTML 编码/解码

HTML 编码/解码

XML、JSON 在线转换
XML、JSON 在线转换

在线XML、JSON转换工具