Redis分布式锁

是什么?

对于分布式场景,我们可以使用分布式锁,它是控制分布式系统之间互斥访问共享资源的一种方式。

比如说在一个分布式系统中,多台机器上部署了多个服务,当客户端一个用户发起一个数据插入请求时,如果没有分布式锁机制保证,那么那多台机器上的多个服务可能进行并发插入操作,导致数据重复插入,对于某些不允许有多余数据的业务来说,这就会造成问题。而分布式锁机制就是为了解决类似这类问题,保证多个服务之间互斥的访问共享资源,如果一个服务抢占了分布式锁,其他服务没获取到锁,就不进行后续操作。

怎么做?

先设置锁,在完成业务操作后,删除锁。其他请求先获取锁,未获取锁的时候,则等待。

setnx lock v100 //设置锁
if  设置成功 {
	1.完成业务操作
	2.释放锁
	del lock
}

问题:系统报错中断或者无限延迟,锁无法释放,其他请求一直等待,系统阻塞

解决方案:设置过期时间

setnx lock v100
expire lock 20

问题:这两操作不是原子性,存在设置过期时间前,系统中断,锁无法释放
解决方案:

set lock v100 nx ex 20

仍有问题:请求A拿到锁,进行业务操作时请求阻塞,A锁过期后,请求B拿到锁后,A业务操作完成,A将B的锁释放掉了
解决方案:A只能释放A设置的锁,B只能释放B设置的锁

生成一个随机数 uuid
set lock uuid nx ex 20
//释放锁时比较uuid 和 lock的value值
if uuid == get lock {
	//锁释放
}

问题: 虽然这样做可以避免大部分A释放B的可能性,但依然可能存在,uuid比较成功后,A锁过期,请求B拿到锁后,A业务操作完成,A将B的锁释放掉了,因为比较操作和删除操作不是原子性
解决方案:lua语言

if redis.call('get',KEYS[1]) == ARGV[1] then " +
            "return redis.call('del',KEYS[1]) else return 0 end"
//get keys[1] 获取lock的value
//ARGV[1] uuid值

llua语言保证了 比较操作 和删除操作的原子性

为什么

为了确保分布式锁可用,我们至少要确保锁的实现同时满足一下四个条件

  • 互斥性 :在任意时刻,只有一个客户端能持有锁
  • 不会发生死锁:即使有一个客户端在持有锁的期间崩溃而没有主动释放锁,也能保证后续其他客户端能加锁----设置过期时间
  • 解铃还须系铃人:加锁和解锁必须是同一个客户端,客户端自己不能把别人加的锁给解了。–设置随机数uuid
  • 加锁和解锁必须具有原子性—set lock value nx ex 10 和 lua语言

实例:PHP Redis基于TP5.1框架实现互斥锁

你可能感兴趣的:(redis,redis,分布式,数据库)