C# 基于 StackExchange.Redis 实现 分布式锁工具类

 

RedisLockService(完整实现)

using StackExchange.Redis;
using System;
using System.Threading.Tasks;

public class RedisLockService
{
    private readonly IDatabase _redis;
    private readonly TimeSpan _lockLeaseTime;   // 锁过期时间
    private readonly TimeSpan _lockTimeout;     // 获取锁的等待时间

    public RedisLockService(IConnectionMultiplexer connectionMultiplexer, 
                            TimeSpan? lockLeaseTime = null, 
                            TimeSpan? lockTimeout = null)
    {
        _redis = connectionMultiplexer.GetDatabase();
        _lockLeaseTime = lockLeaseTime ?? TimeSpan.FromSeconds(10); // 默认锁 10 秒过期
        _lockTimeout = lockTimeout ?? TimeSpan.FromSeconds(3);      // 默认等待 3 秒
    }

    /// <summary>
    /// 尝试获取分布式锁
    /// </summary>
    public async Task<string?> LockAsync(string key)
    {
        key = $"Mall_lock:{key}";
        var value = Guid.NewGuid().ToString(); // 锁的唯一标识

        var start = DateTime.UtcNow;

        while (DateTime.UtcNow - start < _lockTimeout)
        {
            // NX:不存在时设置,PX:过期时间
            bool acquired = await _redis.StringSetAsync(
                key,
                value,
                _lockLeaseTime,
                when: When.NotExists
            );

            if (acquired)
            {
                return value; // 返回唯一标识,用于解锁
            }

            await Task.Delay(100); // 等待重试
        }

        return null; // 超时未获取到锁
    }

    /// <summary>
    /// 解锁,只有持有锁的客户端才能解锁
    /// </summary>
    public async Task<bool> UnlockAsync(string key, string value)
    {
        key = $"Mall_lock:{key}";

        string script = @"
            if redis.call('get', KEYS[1]) == ARGV[1] then
                return redis.call('del', KEYS[1])
            else
                return 0
            end";

        var result = (int)(long)await _redis.ScriptEvaluateAsync(
            script,
            keys: new RedisKey[] { key },
            values: new RedisValue[] { value }
        );

        return result == 1;
    }
}

使用示例

var muxer = await ConnectionMultiplexer.ConnectAsync("localhost:6379");
var redisLockService = new RedisLockService(muxer);

// 获取锁
string? lockValue = await redisLockService.LockAsync("order:123");

if (lockValue != null)
{
    try
    {
        Console.WriteLine("加锁成功,执行业务逻辑...");

        // TODO: 处理订单逻辑
    }
    finally
    {
        // 解锁(必须传入获取锁时的 value,保证安全)
        bool unlocked = await redisLockService.UnlockAsync("order:123", lockValue);
        Console.WriteLine(unlocked ? "解锁成功" : "解锁失败");
    }
}
else
{
    Console.WriteLine("获取锁失败,请勿重复提交");
}

 

  • 自动加前缀 Mall_lock:

  • 加锁时返回唯一标识(解锁必须带上,防止误删别人锁)

  • 锁有过期时间,防止死锁

  • 带超时等待逻辑

 

posted @ 2025-09-19 17:56  点终将连成线  阅读(7)  评论(0)    收藏  举报