Redis单节点的分布式锁只需要注意三点就可以了:

1.加锁并设置锁的过期时间必须是原子操作;

2.锁的value值必须要有唯一性;

3.释放锁的时候要验证其value值,不是自己加的锁不能释放.

但是单节点分布式锁最大的缺点就是,它只作用在一个Redis节点上,如果该节点挂了,那就挂了.

那可不可以通过哨兵机制来保证高可用呢?

答案是不行.

因为Redis在进行主从复制的时候是异步的.

假设 clientA 拿到锁后,在 master 还没同步到 slave 时,master 发生了故障,这时候 salve 升级为 master,导致锁丢失.

RedLock 的思想是:假设有5个Redis节点.这些节点完全相互独立,不存在主从或者集群机制,都是 master.并且这5个Redis实例运行在5台机器上,这样保证他们不会同时宕掉.

客户端应该按照以下操作来获取锁:

1.获取当前时间戳,假设是T1.

2.依次尝试从这5个Redis实例获取锁.当客户端向Redis请求获取锁时,客户端应该设置超时时间,并且这个超时时间应该小于锁的失效时间.比如你的锁自动失效时间为10秒,则超时时间应该在5-50毫秒之间.这样可以避免Redis已经挂掉的情况下,客户端还在等待响应结果.如果Redis没有在规定时间内响应,客户端应该尽快尝试去另外一个Redis实例请求获取锁.

3.请求完所有的Redis节点后,只有满足如下两点,才算真正的获取到锁:

  1)当前时间 - T1 的时间差小于锁的过期时间.比如T1=00:00:00,然后从5个Redis节点都拿到了锁,当前时间是 00:00:05,也就是说获取锁一共用了5秒钟.假设锁的过期时间是3秒,那么这次获取锁的操作就算失败了.

  2)从(N/2+1)个Redis节点都获取到锁.这个很好理解,5个节点,你拿2个,我拿2个,到底算谁的?

  总结一句话就是:从开始获取锁计时,只要在锁的过期时间内成功获取到一半以上的锁便算成功,否则算失败.

4.当客户端获取到了锁,锁的真正有效时间 = 锁的过期时间 - 获取锁所使用的时间(也就是第3步计算出来的时间).

5.如果客户端由于某些原因(比如获取锁的实例个数小于N/2+1,或者已经超过了有效时间),没有获取到锁,客户端便会在所有的Redis实例上进行解锁(即使某些Redis实例根本就没有加锁成功),因为可能已经获取了小于 N/2+1个锁,必须释放掉,否则会影响其他客户端获取锁.

关于是否启动AOF永久存储,需要有所取舍.

1.永久启动,由于Redis的过期机制是按照unix时间戳走的,所以当我们重启Redis后,依然会按照规定的时间过期.但是永久启动对性能有一定影响;

2.采用默认的1秒1次.如果在1秒内断电,会导致数据丢失,这时候如果立刻重启会导致锁的互斥性实效.

所以有效的解决方案是,采用AOF,1秒1次,不管什么原因宕机后,等待一定时间再重启.这个时间就是锁的过期时间.

Demo:

安装官方提供的 RedLock.net

Startup:

    public class Startup
{
private RedLockFactory _redLockFactory; public void ConfigureServices(IServiceCollection services)
{
services.AddControllers(); var endPoints = new List<RedLockEndPoint>
{
new DnsEndPoint("127.0.0.1", 6379),
new DnsEndPoint("127.0.0.1", 6380),
new DnsEndPoint("127.0.0.1", 6381)
};
_redLockFactory = RedLockFactory.Create(endPoints);
services.AddSingleton(typeof(IDistributedLockFactory), _redLockFactory);
} public void Configure(IApplicationBuilder app, IWebHostEnvironment env, IHostApplicationLifetime applicationLifetime)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
} //应用程序结束时释放,因为不是容器创建的对象
applicationLifetime.ApplicationStopping.Register(() =>
{
_redLockFactory.Dispose();
}); app.UseRouting();
app.UseEndpoints(endpoints =>
{
endpoints.MapControllers();
});
}
}

测试api:

    [ApiController]
public class ValuesController : ControllerBase
{
private static int _stock = 10; private readonly IDistributedLockFactory _distributedLockFactory; public ValuesController(IDistributedLockFactory distributedLockFactory)
{
_distributedLockFactory = distributedLockFactory;
} [Route("lockTest")]
[HttpGet]
public async Task<int> DistributedLockTest()
{
// resource 锁定的资源
var resource = "the-thing-we-are-locking-on"; // expiryTime 锁的过期时间
var expiry = TimeSpan.FromSeconds(5); // waitTime 等待时间
var wait = TimeSpan.FromSeconds(1); // retryTime 等待时间内,多久重试一次
var retry = TimeSpan.FromMilliseconds(250); using (var redLock = await _distributedLockFactory.CreateLockAsync(resource, expiry, wait, retry))
{
if (redLock.IsAcquired)
{
// 模拟执行业务逻辑
await Task.Delay(new Random().Next(100, 500));
if (stock > 0)
{
stock--;
return stock;
}
return stock;
}
Console.WriteLine($"{DateTime.Now} : 获取锁失败");
}
return -99;
}
}

测试控制台:

        static void Main(string[] args)
{
HttpClient client = new HttpClient();
var result = Parallel.For(0, 20, (i) =>
{
var stopwatch = new Stopwatch();
stopwatch.Start();
var response = client.GetAsync($"http://localhost:5000/locktest").Result;
stopwatch.Stop();
var data = response.Content.ReadAsStringAsync().Result;
Console.WriteLine($"ThreadId:{Thread.CurrentThread.ManagedThreadId}, Result:{data}, Time:{stopwatch.ElapsedMilliseconds}");
});
client.Dispose();
Console.ReadKey();
}

测试结果:

C# Redis分布式锁(RedLock) - 多节点的更多相关文章

  1. 七种方案!探讨Redis分布式锁的正确使用姿势

    前言 日常开发中,秒杀下单.抢红包等等业务场景,都需要用到分布式锁.而Redis非常适合作为分布式锁使用.本文将分七个方案展开,跟大家探讨Redis分布式锁的正确使用方式.如果有不正确的地方,欢迎大家 ...

  2. Redlock(redis分布式锁)原理分析

    Redlock:全名叫做 Redis Distributed Lock;即使用redis实现的分布式锁: 使用场景:多个服务间保证同一时刻同一时间段内同一用户只能有一个请求(防止关键业务出现并发攻击) ...

  3. Redlock:Redis分布式锁最牛逼的实现

    普通实现 说道Redis分布式锁大部分人都会想到:setnx+lua,或者知道set key value px milliseconds nx.后一种方式的核心实现命令如下: - 获取锁(unique ...

  4. 【Redis】分布式锁RedLock

    普通实现 说道Redis分布式锁大部分人都会想到: 1.setnx+lua, 2.setkey value px milliseconds nx. - 获取锁(unique_value可以是UUID等 ...

  5. C# Redis分布式锁 - 单节点

    为什么要用分布式锁? 先上一张截图,这是在浏览别人的博客时看到的. 在了解为什么要用分布式锁之前,我们应该知道到底什么是分布式锁. 锁按照不同的维度,有多种分类.比如 1.悲观锁,乐观锁; 2.公平锁 ...

  6. Redis分布式锁升级版RedLock及SpringBoot实现

    分布式锁概览 在多线程的环境下,为了保证一个代码块在同一时间只能由一个线程访问,Java中我们一般可以使用synchronized语法和ReetrantLock去保证,这实际上是本地锁的方式.但是现在 ...

  7. RedLock.Net - 基于Redis分布式锁的开源实现

    工作中,经常会遇到分布式环境中资源访问冲突问题,比如商城的库存数量处理,或者某个事件的原子性操作,都需要确保某个时间段内只有一个线程在访问或处理资源. 因此现在网上也有很多的分布式锁的解决方案,有数据 ...

  8. Redis分布式锁

    Redis分布式锁 分布式锁是许多环境中非常有用的原语,其中不同的进程必须以相互排斥的方式与共享资源一起运行. 有许多图书馆和博客文章描述了如何使用Redis实现DLM(分布式锁管理器),但是每个库都 ...

  9. Lua脚本在redis分布式锁场景的运用

    目录 锁和分布式锁 锁是什么? 为什么需要锁? Java中的锁 分布式锁 redis 如何实现加锁 锁超时 retry redis 如何释放锁 不该释放的锁 通过Lua脚本实现锁释放 用redis做分 ...

随机推荐

  1. .Net Task 异步执行不等待结果返回

    该文章适合有一定异步编程基础的童鞋 开始之前先看.NET官网的一张图: 异步编程中最需弄清的是控制流是如何从方法移动到方法的. 没有理解的话可以去看一下 https://docs.microsoft. ...

  2. Flink从入门到入土(详细教程)

    和其他所有的计算框架一样,flink也有一些基础的开发步骤以及基础,核心的API,从开发步骤的角度来讲,主要分为四大部分 1.Environment Flink Job在提交执行计算时,需要首先建立和 ...

  3. Python爬虫实战 批量下载高清美女图片

    彼岸图网站里有大量的高清图片素材和壁纸,并且可以免费下载,读者也可以根据自己需要爬取其他类型图片,方法是类似的,本文通过python爬虫批量下载网站里的高清美女图片,熟悉python写爬虫的基本方法: ...

  4. 痞子衡嵌入式:导致串行NOR Flash在i.MXRT下无法正常下载/启动的常见因素之SFDP

    大家好,我是痞子衡,是正经搞技术的痞子.今天痞子衡给大家介绍的是导致串行NOR Flash在i.MXRT下无法正常下载/启动的常见因素之SFDP. i.MXRT系列MCU发布已两年多了,基于i.MXR ...

  5. 目标识别AI资料

    朋友推荐的, 还有自己搜的. 入门可以看看. 网上资料应该不少, 一搜一大把, 简单记下地址. Review of Deep Learning Algorithms for Object Detect ...

  6. Selenium学习整理(Python)

    1 准备软件 Selenium IDE firebug-2.0.19.xpi firepath-0.9.7-fx.xpi Firefox_46.0.1.5966_setup.exe 由于火狐浏览器高版 ...

  7. Canalv1.1.4版本搭建HA集群

    前提 Canal上一个正式版是于2019-9-2发布的v1.1.4,笔者几个月前把这个版本的Canal推上了生产环境,部署了HA集群.过程中虽然遇到不少的坑,但是在不出问题的前提下,Canal的作用还 ...

  8. Codeforces 1337D Xenia and Colorful Gems

    题意 给你3个数组\(a, b\)和\(c\),最小化\((x-y)^2+(y-z)^2+(z-x)^2\),其中\(x \in a, y \in b, z \in c\). 解题思路 这题其实第一眼 ...

  9. JS中的数组,添加删除元素,判断是否存在一个值的方法总结

    一.添加元素: 1:在最后添加,返回数组长度:arr.push(...); 2:在最前面添加,返回数组长度:arr.unshift(...); 3:在指定位置添加,没有返回值:arr[i] = xxx ...

  10. PHP的七个数组指针函数

    1. PHP的七个数组指针函数 函数 描述 reset() 将一个数组的内部指针重置到首位,并返回第一个元素的值 end() 将一个数组的内部指针移动到数组的最后一个元素所在的位置,并返回最后一个元素 ...