Redis SETNX實現分布式鎖


1、某進程1執行 SETNX lock 以嘗試獲取鎖

2、由於某進程2已獲得了鎖,所以進程1執行 SETNX lock 返回0,即獲取鎖失敗

3、進程1執行 GET lock 來檢測鎖是否已超時,如果沒超時,則線程等待一段時間,再次檢測

4、如果進程1檢測到鎖已超時,即當前的時間大於鍵 lock 的值,進程1會執行以下操作

GETSET lock <current Unix timestamp + lock timeout + 1>

5、由於 GETSET 操作在設置鍵的值的同時,還會返回鍵的舊值,通過比較鍵 lock 的舊值是否小於當前時間,可以判斷進程是否已獲得鎖

6、假如另一個進程3也檢測到鎖已超時,並在進程1之前執行了 GETSET 操作,那么進程1的 GETSET 操作返回的是一個大於當前時間的時間戳,這樣進程1就不會獲得鎖而繼續等待。注意到,即使進程1接下來將鍵 lock 的值設置了比進程3設置的更大的值也沒影響。

另外,值得注意的是,在進程釋放鎖,即執行 DEL lock 操作前,需要先判斷鎖是否已超時。如果鎖已超時,那么鎖可能已由其他進程獲得,這時直接執行 DEL lock 操作會導致把其他進程已獲得的鎖釋放掉。

C# Code

using System;
using System.Threading;
using System.Threading.Tasks;
using CSRedis;

namespace RedisLockDemo
{
    public class CsRedisLock
    {
        private static readonly int _lock_timeout = 40;
        private static readonly string _lock_key = "lock";
        public static void Test()
        {
            var rds = new CSRedisClient("127.0.0.1:6379,password=123456,defaultDatabase=13,poolsize=50,ssl=false");
            RedisHelper.Initialization(rds);

            Parallel.For(0, 13, x =>
            {
                if (GetLock(_lock_key))
                {
                    Console.WriteLine($"person:{x},線程ID:{Thread.CurrentThread.ManagedThreadId},獲得鎖 woking");

                    if (DateTimeOffset.Now.ToUnixTimeMilliseconds() < RedisHelper.Get<long>(_lock_key))
                    {
                        //釋放鎖
                        RedisHelper.Del(_lock_key);
                    }
                }
                else
                {
                    Console.WriteLine($"person:{x},線程ID:{Thread.CurrentThread.ManagedThreadId},獲取鎖異常");
                }
            });
            Console.WriteLine();
        }

        private static bool GetLock(string key)
        {
            bool getLocked = false;
            try
            {
                while (!getLocked)
                {
                    var now = DateTimeOffset.Now.ToUnixTimeMilliseconds();
                    var lock_time = now + _lock_timeout + 1;
                    getLocked = RedisHelper.SetNx(key, lock_time);
                    //判斷是否獲取鎖,
                    if (getLocked || now > RedisHelper.Get<long>(key) && now > RedisHelper.GetSet<long>(key, lock_time))
                    {
                        getLocked = true;
                    }
                    else
                    {
                        Thread.Sleep(30);
                    }
                }
            }
            catch (Exception ex)
            {
                Console.WriteLine(ex.Message);
            }
            return getLocked;
        }
    }
}

相關文檔:https://redis.io/commands/setnx


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM