利用Redis实现分布式锁最佳解决方案

一、缘起

在分布式业务中,涉及到同一份数据的增删改时,很容易出现并发问题,导致最终结果出现异常,为了解决此类问题,我们引入了分布式锁的概念。

二、设计

由于网上教程实现逻辑冗余复杂(至今没看懂),这里我就按照自己的思路设计了一下,非常的简单实用,非常的银杏化。

1、采用Redis实现分布式锁存储

2、采用多线程多实例模拟分布式程序并发访问场景

3、采用Redis模拟分布式业务数据存储

三、代码

入口函数

class Program
    {
        static void Main(string[] args)
        {
            //利用多线程,多实例,模拟分布式并发请求
            for (var i = 0; i < 10; i++)
            {
                Task.Run(() =>
                {
                    new TestService().Run();
                });
            }

            Console.ReadKey();
        }
    }

业务函数

public class TestService
    {
        private readonly RedisCacheService _cacheService;
        private readonly RedisLockService _lockService;

        public TestService()
        {
            //创建Redis服务,这里的Redis用于存储业务数据
            _cacheService = new RedisCacheService(new RedisCacheOptions
            {
                Configuration = "Redis连接字符串"
            });
            //创建Redis锁服务,用于锁创建
            _lockService = new RedisLockService();
        }

        public void Run()
        {
            //创建锁
            _lockService.Create("test");

            //设置数量
            SetCount();
            //显示数量
            ShowCount();

            //释放锁
            _lockService.Clear("test");
        }

        public void SetCount()
        {
            _cacheService.Set("count", _cacheService.Get<int>("count") + 1);
        }

        public void ShowCount()
        {
            Console.WriteLine($"当前数量:{_cacheService.Get<int>("count")}");
        }
    }

锁函数

public class RedisLockService
    {
        private readonly RedisCacheService _cacheService;

        public RedisLockService()
        {
            //创建Redis服务,这里的Redis用于存储锁数据
            _cacheService = new RedisCacheService(new RedisCacheOptions
            {
                Configuration = "Redis连接字符串"
            });
        }

        /// <summary>
        /// 创建锁
        /// </summary>
        /// <param name="name">锁名</param>
        public void Create(string name)
        {
            //锁标识,证明是当前程序创建的锁
            var flag = CommonUtil.NewGuid();

            while (!Get(name, flag)) //锁验证,若未获取到自己创建的锁,则进入循环,反之跳出循环执行后续代码
            {
                //锁检测,若发现锁已被占用,则进入循环等待50ms,反之跳出循环执行后续代码
                while (Check(name))
                {
                    Thread.Sleep(50);
                }

                //锁设置,设置属于自己的锁,由于存在并发问题,锁有可能被其他程序覆盖,因此才需要循环进行锁验证
                Set(name, flag);
            }

        }

        /// <summary>
        /// 释放锁
        /// </summary>
        /// <param name="name">锁名</param>
        public void Clear(string name)
        {
            _cacheService.Remove($"lock-{name}");
        }
        private bool Check(string name)
        {
            return _cacheService.IsExist($"lock-{name}");
        }

        private bool Get(string name, string flag)
        {
            return _cacheService.Get<string>($"lock-{name}") == flag;
        }

        private void Set(string name, string flag)
        {
            //这里设置的Redis过期时间为30秒,防止出现死锁,具体业务具体设置
            _cacheService.Set($"lock-{name}", flag, 30);
        }
    }

留下评论