为什么需要使用分布式锁?
因为在一个分布式系统中,存在多个进程,多个进程之间也存在访问同一个公共资源的情况,多个进程之间的执行顺序是随机性的,此时就需要通过"锁"来达到互斥控制。
比如买票的时候:

此时我们就可以引入redis作为分布式锁

在redis中设置一个特殊的key-value,完成上述买票操作,再把key-value删除,其他服务器在也想买票的时候,就需要尝试设置key-value,如果发现key-value已经存在,就认定为加锁失败(放弃/等待)。执行完之后再删除对应的key,完成解锁操作。
什么是分布式锁
所谓的分布式锁,也是一个/一组单独的服务器程序,提供"加锁"这样的服务。(redis是一种典型的可以用来实现分布式锁的方案,但不是唯一一种,业界可能也会使用mysql/zookeeper这样的组件来实现分布式锁的效果)。
刚才买票的场景,使用mysql的事务也可以批量执行 查询+修改操作,但是分布式系统中,要访问的共享资源不一定是mysql...也可能是执行一段特定的操作,是通过统一的服务器完成执行动作。
引入过期时间
对于上述的设置key-value的方法,如果服务器在执行del突然服务器宕机了,就会出现无法皆所得情况。此时就可以对用来加锁的key设置过期时间,防止服务器宕机无法解锁。
比如使用 set ex nx 命令
引入校验id
上面设置的key-value,服务器1设置了key之后,其他服务器也可以进行删除,为了解决这个问题,我们引入校验id,比如把value设置为服务器的编号,这样其他服务器在获取到value之后,就可以根据value进行判断是不是自己设置的key。
引出lua脚本

此时虽然看上去没有什么问题,但是此时如果有另外一个服务器,在线程1删除之后设置了key,此时线程2再进行删除,就会导致这个另外的服务器的加锁被错误的解锁。
归根到底还是GET和DEL操作不原子的所引起的。
使用事务,能解决上述的问题(redis的事务虽然弱,但是能保证避免插队)
但是实践中往往使用更好的方案,lua脚本
lua是一个编程语言,作为redis内嵌语言,特别轻量,可以使用lua编写一些逻辑,把这个脚本上传到redis服务器上,然后让客户端来控制redi执行上述脚本,也是原子的,相当于一条命令一样
引出看门狗
在加锁的时候给key设置多久的过期时间比较合适呢?
如果设置的短,就可能在你的业务逻辑还没执行完,就释放锁了。
如果设置的太长,就会导致"锁释放的不及时"的问题
更好的办法是"动态续约"。(往往也需要一个服务器单独的线程负责续约这个事情)
比如:初始条件下,设置一个过期时间(比如设置1s)就提前在还剩下300ms(还剩下一段时间,不一定是300ms)的时候,如果当前任务还没执行完,就把过期时间再续上1s.等到时间又快到了,如果没执行完,再续....
如果服务器中途崩溃了,自然就没人负责续约了,此时,锁也就能够在较短的时间里释放了。
引出Redlock算法
使用redis作为分布式锁,redis自身也是有可能挂的,所以必需要保证redis本身的高可用性
redis可以使用集群(主要是解决内存问题)或者哨兵的方式保证高可用性
但是主节点和从节点之间的数据同步是存在延时的,可能主节点收到set请求之后,还没来得及给从节点同步,主节点就挂了,就会导致这次set的数据丢失。
redlock算法(作者给出的一个解决方案) 通过存放冗余的数据来保证数据不丢失

此处加锁,就是按照一定的顺序,针对这些组redis都进行加锁,如果某个节点挂了,继续给下一个节点加锁即可,如果写入key成功的节点个数超过总数的一半,就视为加锁成功。同时解锁也是相同的操作
上述介绍的只是一个简单的"互斥锁"
redis也是可以实现以下这些锁的
1.读写锁
2.公平锁
3.可重入锁