redis分布式锁

1.什么是分布式锁

分布式锁其实就是,控制分布式系统不同进程共同访问共享资源的一种锁的实现。如果不同的系统或同一个系统的不同主机之间共享了某个临界资源,往往需要互斥来防止彼此干扰,以保证一致性。

我们的分布式锁应具备

  • 在分布式系统环境下,一个方法在同一时间只能被一个机器的一个线程执行
  • 高可用的获取锁与释放锁
  • 高性能的获取锁与释放锁
  • 具备可重新进入性能
  • 具备锁失效机制,即自动解锁,防止死锁
  • 具备非阻塞锁特性,即没有获取到锁将直接返回获取锁失败

2.使用redis实现分布式锁

分案一,使用setnx,也就是基于set命令的分布式锁

setnx为set if not exists ,即当前key不存在时才能成功设值,所以我们可以通过使用setnx进行加锁,当该指令返回1时,说明成功获得锁,当得到锁的线程执行完任务之后,使用del命令释放锁,以便其他线程可以继续执行setnx命令来获得锁

但这个会存在个问题,就是当线程获取了锁之后,在执行任务的过程中挂掉,来不及显示地执行del命令释放锁,那么竞争该锁的线程都会执行不了,产生死锁的情况。

当然解决的方法也非常简单,我们给这个key加个锁超时时间就好了,可以使用expire命令设置锁超时时间

但setnx 和 expire 不是原子性的操作,假设某个线程执行setnx 命令,成功获得了锁,但是还没来得及执行expire 命令,服务器就挂掉了,这样一来,这把锁就没有设置过期时间了,变成了死锁,别的线程再也没有办法获得锁了。

所以我们要set命令支持在获取锁的同时设置key的过期时间

但假如线程A得到了锁,然后设置超时时间是 30 秒。如果某些原因线程 A 执行的很慢,过了 30 秒都没执行完,这时候锁过期自动释放,线程 B 得到了锁。 随后,线程A执行完任务,接着执行del指令来释放锁。但这时候线程 B 还没执行完,线程A实际上删除的是线程B加的锁。

所以我们要在 del 释放锁之前做一个判断,验证当前的锁是不是自己加的锁。在加锁的时候把当前的线程 ID 当做value,并在删除之前验证 key 对应的 value 是不是自己线程的 ID。但是,这样做其实隐含了一个新的问题,get操作、判断和释放锁是两个独立操作,不是原子性。对于非原子性的问题,我们可以使用Lua脚本来确保操作的原子性

我们也可以使用守护线程的方式解决上面的问题,假设线程A执行了29 秒后还没执行完,这时候守护线程会执行 expire 指令,为这把锁续期 20 秒。守护线程从第 29 秒开始执行,每 20 秒执行一次。当线程A执行完任务,会显式关掉守护线程。如果服务器忽然断电,由于线程 A 和守护线程在同一个进程,守护线程也会停下。这把锁到了超时的时候,没人给它续命,也就自动释放了。

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