2022年11月 .NET CORE工具案例-StackExchange.Redis代码变量方式实现商品秒杀

Posted 微软MVP Eleven

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了2022年11月 .NET CORE工具案例-StackExchange.Redis代码变量方式实现商品秒杀相关的知识,希望对你有一定的参考价值。

文章目录


前言

下面是Redis分布式锁常用的概念说明:设置、获取、过期时间、删除。

1、 Setnx

  • 命令:SETNX key value
  • 说明:将 key 的值设为 value ,当且仅当 key 不存在。若给定的 key 已经存在,则 SETNX 不做任何动作。SETNX 是『SET if Not eXists』(如果不存在,则 SET)的简写。 时间复杂度:O(1)
  • 返回值:设置成功,返回1 ; 设置失败,返回 0

2、Getset

  • 命令:GETSET key value
  • 说明:将给定 key 的值设为 value ,并返回 key 的旧值(old value)。当 key 存在但不是字符串类型时,返回一个错误。 时间复杂度:O(1)
  • 返回值:返回给定 key 的旧值; 当 key 没有旧值时,也即是, key 不存在时,返回 nil 。

3、Expire

  • 命令:EXPIRE key seconds
  • 说明:为给定 key 设置生存时间,当 key 过期时(生存时间为 0 ),它会被自动删除。 时间复杂度:O(1)
  • 返回值:设置成功返回 1 ;当 key 不存在或者不能为 key 设置生存时间时(比如在低于 2.1.3 版本的 Redis 中你尝试更新 key 的生存时间),返回 0 。

4、Del

  • 命令:DEL key [key …]
  • 说明:删除给定的一个或多个 key 。不存在的 key 会被忽略。 时间复杂度:O(N); N 为被删除的 key 的数量。删除单个字符串类型的 key ,时间复杂度为O(1)。 删除单个列表、集合、有序集合或哈希表类型的 key ,时间复杂度为O(M), M为以上数据结构内的元素数量。

锁的分类说明:

相对方相对方
悲观锁乐观锁
公平锁非公平锁
独享锁共享锁
线程锁进程锁

一、StackExchange.Redis执行Lua脚本实现商品秒杀

以下以.NET 7控制台为实例测试

1.StackExchange.Redis封装

using StackExchange.Redis;

namespace Redis

    public class RedisHelper
    
        #region Fileds
        private static string? _redisConnection= "127.0.0.1:6379,defaultDatabase=1";
        private static int _db = 0;
        private static ConnectionMultiplexer? connection;
        #endregion

        #region Constructors
        public RedisHelper()
        

        

        public static ConnectionMultiplexer CacheConnection
        
            get
            
                try
                
                    if (connection == null || !connection.IsConnected)
                    
                        connection = new Lazy<ConnectionMultiplexer>(() => ConnectionMultiplexer.Connect(_redisConnection!)).Value;
                    
                
                catch (Exception ex)
                
                    return null;
                
                return connection;
            
        
        #endregion


        #region Methons

        /// <summary>
        /// 缓存当前数据库
        /// </summary>
        public static IDatabase CacheRedis => CacheConnection.GetDatabase(_db);

        /// <summary>
        /// 新增单条值
        /// </summary>
        /// <param name="values"></param>
        /// <returns></returns>
        public static bool StringSet(string key, string values)
        
            if (string.IsNullOrEmpty(key) && string.IsNullOrEmpty(values))
                throw new AggregateException("values or is null");
            return CacheRedis.StringSet(key, values);
        

        /// <summary>
        /// 查询单个key值
        /// </summary>
        /// <param name="key"></param>
        /// <returns></returns>
        public static RedisValue GetStringKey(string key)
        
            return CacheRedis.StringGet(key);
        

        /// <summary>
        /// 判断key是否存储
        /// </summary>
        /// <param name="key">redis key</param>
        /// <returns></returns>
        public bool KeyExists(string key)
        
            return CacheRedis.KeyExists(key);
        

        /// <summary>
        /// 删除单个key
        /// </summary>
        /// <param name="key">redis key</param>
        /// <returns>是否删除成功</returns>
        public bool KeyDelete(string key)
        
            return CacheRedis.KeyDelete(key);
        

        /// <summary>
        /// redis加锁
        /// </summary>
        /// <param name="key">需要加锁的锁名</param>
        /// <param name="values">需要加锁的值</param>
        /// <param name="expireTimeSeconds">该锁自动到期时间,如果没其他要求可设置为最大时常,该方式一定要手动解锁</param>
        /// <exception cref="Exception"></exception>
        #region 分布式锁
        public static bool LockByRedis(string key, string values)
        
            try
            
                //expireTimeSeconds = expireTimeSeconds > 20 ? 10 : expireTimeSeconds;
                //var data = TimeSpan.FromSeconds(expireTimeSeconds);
                //var token = Environment.MachineName;              
                //bool lockflag = CacheRedis.LockTake(key, Thread.CurrentThread.ManagedThreadId, TimeSpan.FromSeconds(expireTimeSeconds));

                bool lockflag = CacheRedis.LockTake(key, values, TimeSpan.MaxValue);
                if (!lockflag)
                
                    return false;
                
                return true;
            
            catch (Exception ex)
            
                throw new Exception($"Redis加锁异常:原因ex.Message");
            
        

        /// <summary>
        /// 解锁
        /// </summary>
        /// <param name="key">需要解锁的锁名</param>
        /// <param name="values">需要解锁的值</param>
        /// <returns></returns>
        /// <exception cref="Exception"></exception>
        public static bool UnLockByRedis(string key, string valuse)
        
            try
            
                // Thread.CurrentThread.ManagedThreadId
                //Environment.MachineName
                return CacheRedis.LockRelease(key, valuse);
            
            catch (Exception ex)
            
                throw new Exception($"Redis加锁异常:原因ex.Message");
            
        

        #endregion
        #endregion
    

2.秒杀代码

using Redis;

int count=11;

Console.WriteLine($"进入秒杀时刻,进入时间DateTime.Now");

Parallel.For(0, 1000, i =>

    //锁键-值
    var lockKey = "lockKey";
    var lockValue = Guid.NewGuid().ToString("N");
    //加锁
    bool result = RedisHelper.LockByRedis(lockKey, lockValue);

    if (!result)
    
        Console.WriteLine("没有抢到锁,这次请求停止");
        return;
    
   

    string data = RedisHelper.GetStringKey("Sum").ToString();
    int sum = int.Parse(string.IsNullOrEmpty(data) ? "0" : data);


    if (sum <= 0)
    
        RedisHelper.StringSet("Sum", count.ToString());
    
    else
    
        if (sum <=count)
        
            count--;
            Console.WriteLine("商品还剩:" + sum.ToString());
            
            RedisHelper.StringSet("Sum", count.ToString());
        
    
    
    //释放锁
    var unlock = RedisHelper.UnLockByRedis(lockKey, lockValue);
);

Console.WriteLine($"结束秒杀时刻,结束时间DateTime.Now");

3.效果

以上是关于2022年11月 .NET CORE工具案例-StackExchange.Redis代码变量方式实现商品秒杀的主要内容,如果未能解决你的问题,请参考以下文章

2022年11月 .NET CORE工具案例-.NET 7中的Quic通信

2022年11月 .NET CORE工具案例-StackExchange.Redis代码变量方式实现商品秒杀

2022年11月 .NET CORE工具案例-StackExchange.Redis代码变量方式实现商品秒杀

愚公系列2022年12月 .NET CORE工具案例-.NET Core使用PaddleOCRSharp进行身份证和车牌识别

2022年10月 .NET CORE工具案例-DiffPlex文本差异组件

2022年12月 .NET CORE工具案例-CSRedis执行Lua脚本实现商品秒杀