当前位置: 首页 > 科技观察

基于Redis的分布式锁及Redlock算法_1

时间:2023-03-19 01:36:48 科技观察

本文转载自微信公众号《UP科技控》,作者conan5566。转载本文请联系UP技控公众号。在单进程系统中,当有多个线程可以同时更改一个变量(变量共享变量)时,需要对变量或代码块进行同步,使其线性执行,在修改这个时消除并发修改多变的。多变的。同步的本质是通过锁来实现的。为了实现多个线程同一时间只能有一个线程在同一个代码块中执行,需要在某处做标记。这个标记必须对每个线程可见。当标记不存在时,可以设置标记,其余后续线程发现已经有标记,然后等待有标记的线程结束同步代码块取消标记,再尝试设置标记标记。这个标记可以理解为一把锁。锁在不同的地方有不同的实现方式,只要所有线程都能看到标记即可。比如在Java中,synchronize就是在对象头设置一个标记,而Lock接口的实现类基本上只是一个volatile修饰的int变量,保证了每个线程都能对int进行可见性和原子修饰,就像Linux内核中的情况一样。用互斥量或信号量等内存数据进行标记。除了使用内存数据作为锁外,其实任何互斥锁都可以作为锁使用(只考虑互斥的情况)。例如,用于幂等验证的水表中的序号和时间的组合,可以看作是一个不会释放的锁,或者使用某个文件是否存在作为锁等。只需要满足原子性即可修改标记时可以保证内存可见性。1什么是分布式?分布式CAP理论告诉我们,没有一个分布式系统可以同时满足Consistency、Availability和Partitiontolerance,只能同时满足两者。目前很多大型网站和应用都是分布式部署的,分布式场景下的数据一致性问题一直是一个重要的话题。基于CAP理论,很多系统在设计之初就不得不在三者之间做出权衡。在互联网领域的大部分场景下,需要牺牲强一致性来换取系统的高可用性,而系统往往只需要保证最终一致性即可。这里的分布式场景主要是指集群模式,同时启动多个相同的服务。在很多场景下,为了保证数据的最终一致性,我们需要很多技术方案来支持,比如分布式事务、分布式锁等。很多时候我们需要保证一个方法在同一时刻只能被同一个线程执行。在单机环境下,我们可以通过Java提供的并发API来解决,但是在分布式环境下,就没那么简单了。分布式和单机最大的区别是不是多线程而是多进程。因为多线程可以共享堆内存,所以可以简单的把内存作为标签的存放位置。而且进程甚至可能不在同一台物理机上,所以tag需要存放在所有进程都能看到的地方。什么是分布式锁?当分布式模型中只有一个(或有限的)数据副本时,就需要使用锁技术来控制某个时刻修改数据的进程数。单机模式下的锁不仅需要保证进程可见,还需要考虑进程与锁之间的网络问题。(我觉得之所以问题在分布式情况下变得复杂,主要是因为需要考虑网络的延迟和不可靠。。。一个大坑)分布式锁还是可以在内存中存储标签,但是内存不是process分配的内存是Redis、Memcache等公有内存。至于使用数据库、文件等作为锁,和单机实现是一样的,只要标签互斥即可。2我们需要什么样的分布式锁?可以保证在分布式应用集群中,同一个方法在同一时间只能由一台机器上的一个线程执行。如果这个锁是可重入锁(避免死锁)这个锁最好是阻塞锁(根据业务需求考虑是否要这个)这个锁最好是公平锁(根据业务需求考虑是否要这个)highly可用的锁获取和释放锁的性能?代码实现ILockResultLock(stringresourceKey,TimeSpanexpiryTime,TimeSpanwaitTime,TimeSpanretryTime,CancellationTokencancellationToken);TaskLockAsync(stringresourceKey);TaskLockAsync(stringresourceKey,TimeSpanexpiryTime);TaskLockAsync(stringresourceKey,TimeSpanexpiryTime,TimeSpaniryTime);任务LockAsync(stringresourceKey,TimeSpanexpiry;TimeSpaniryTime)ILockResult>LockAsync(stringresourceKey,TimeSpanexpiryTime,TimeSpanwaitTime,TimeSpanretryTime,CancellationTokencancellationToken);}publicinterfaceILockResult:IDisposable{stringLockId{get;}boolIsAcquired{get;}intExtendCount{get;}}classEndPoint:RedLockEndPoint.Redis私有的ereadonlystring_connectionString;publicEndPoint(stringconnectionString){_connectionString=connectionString;//139.196.40.252,password=xstudio,defaultDatabase=9varconnection=connectionString.Split(',');vardict=newDictionary();foreach(variteminconnection){varkeypar=item.Split('=');if(keypar.Length>1){dict[keypar[0]]=keypar[1];}}this.EndPoint=newSystem.Net.DnsEndPoint(connection[0],6379);if(dict.TryGetValue("password",outstringpassword)){this.Password=password;}if(dict.TryGetValue("defaultDatabase",outstringdefaultDatabase)&&int.TryParse(defaultDatabase,outintdatabase)){RedisDatabase=database;}}}[Export(typeof(IDistributedLock))]classInnerLock:IDistributedLock{privatestaticLazy_factory;staticInnerLock(){_factory=newLazy(()=>newRedisLockFactory(newEndPoint(ConfigurationManager.AppSettings["Redis"])),System.Threading.LazyThreadSafetyMode.ExecutionAndPublication);}publicILockResultLock(stringresourceKey){returnnewLockResult(_factory.Value.Create(resourceKey,TimeSpan.FromDays(1)));}publicILockResultLock(stringresourceKey,TimeSpanexpiryTime){returnnewLockResult(_factory.Value.Create(resourceKey,expiryTime));}publicILockResultLock(stringresourceKey,TimeSpanexpiryTime,TimeSpanwaitTime,TimeSpanretryTime){returnnewLockResult(_factory.Value.Create(resourceKey,expiryTime,waitTime,retryTime));}publicILockResultLock(stringresourceKey,TimeSpanexpiryTime,TimeSpanwaitTime,TimeSpanretryTime,CancellationTokencancellationToken){returnnewLockFactory(_Value.Create(resourceKey,expiryTime,waitTime,retryTime,cancellationToken));}publicasyncTaskLockAsync(stringresourceKey){varresult=await_factory.Value.CreateAsync(resourceKey,TimeSpan.FromDays(1));returnnewLockResult(结果);}publicasyncTaskLockAsync(stringresourceKey,TimeSpanexpiryTime){varresult=await_factory.Value.CreateAsync(resourceKey,expiryTime);returnnewLockResult(结果);}publicasyncTaskLockAsync(stringresourceKey,TimeSpanexpiryTime,TimeSpanwaitTime,TimeSpanretryTime){varresult=await_factory.Value.CreateAsync(resourceKey,expiryTime,waitTime,retryTime);returnnewLockResult(结果);}publicasyncTaskLockAsync(stringresourceKey,TimeSpanexpiryTime,TimeSpanwaitTime,TimeSpanretryTime,CancellationTokencancellationToken){varresult=await_factory.Value.CreateAsync(resourceKey,expiryTime,waitTime,retryTime,cancellationToken);returnnewLockResult(结果);}}classLockResult:ILockResult{privateIRedisLock_lock;publicLockResult(IRedisLockredisLock){_lock=redisLock;}publicstringLockId=>_lock.LockId;publicboolIsAcquired=>_lock.IsAcquired;publicintExtendCount=>_lock.ExtendCount;publicvoidDispose(){_lock.Dispose();}}