在分布式系统中,存在多进程多主机(每个服务器都是独立的进程),在此种情况下,多进程的运行情况是随机的,我们无法使用sychronized这类锁去控制多进程
因此我们就需要引入"分布式锁"

分布式锁的实现
在上面这张图中,当客户端进行买票操作时,会先查询余票,然后进行扣除,当没有加锁时,在第一个客户端进行操作查询到余票 还未扣除时,第二个客户端也进行买票操作,那么此时就会出现超卖情况
所谓的分布式锁 就是一个/一组单独的服务器程序,给其他的服务器提供"加锁"这样的服务
买票服务器在进行买票时,操作的过程中就需要先加锁
往Redis上设置一个特殊的key-value
完成上述买票操作,再把这个key-value删除掉
如果其他服务器要进行买票操作,就会设置这个key-value 如果已经存在 就会加锁失败,至于是阻塞还是放弃 看具体策略
引入setnx
setnx这个指令的语义是不存在就设置,存在就出错
使用setnx确实可以得到加锁效果
针对解锁,就可以使用del命令来完成
但这样当某个服务器进行setnx 还未执行del 服务器挂了 那此时锁无法释放
设置过期时间
可以给sey的key设置过期时间 一旦时间到了就会自动删除
set ex nx 这样的命令来设置
校验id
因为我们此处分布式锁只是redis上的一个键值对,那么很有可能被误删,服务器1执行加锁,而服务器2执行了解锁
正常来说是不会的 但是代码总有bug
为了解决上述问题,就引入一点校验机制
1.给服务器编号,每个服务器有一个自己的身份标识
2.进行加锁的时候,设置key-value,key对应着要针对哪个资源加锁(比如车次),value就可以存储刚才服务器编号,标识当前锁是哪个服务器加上的
解锁时 就先查询所对应服务器编号,判定这个编号是否就是当前执行解锁的服务器编号,如果是执行del,如果不是 失败
引入lua脚本
在执行解锁过程中 由于是两步操作,先获取,后解锁
服务器很可能是多线程的 就会出现问题

使用事务可以解决上述问题 防止插队,但实践中往往使用的是lua脚本
可以使用lua编写一些逻辑,把这个脚本上传到redis服务器上,然后就可以让客户端来控制redis执行上述脚本
redis执行lua脚本的过程,也是原子的,相当于执行一条命令一样(实际上lua中可以 写对个命令)

引入看门狗
过期时间的续约问题
过期时间设置多少合适
*如果设置的短,可能业务执行逻辑还没执行完就释放锁了
*如果设置的太长,就也会导致锁释放不及时的问题
更好的方式是动态续约 往往需要服务器这边有个专门的线程 来进行 叫"看门狗"
初始时设置一个时间 动态续约 如果服务器挂掉 也不会一直持有锁 会随后释放
redlock算法
使用redis作为分布式锁,redis本身有没有可能挂呢,很有可能
哨兵模式中 当主节点加锁后挂了 从节点成为新的主节点 获取的有主节点的数据也会被加锁
但当主节点挂了时 从节点同步主节点数据有延迟,此时呢就会可能加锁失败
为了提高可用性,就使用了redlock算法

这样呢就提高了可用性
关于redis 分布式锁呢 我们就探讨到这里
至此Redis 相关底层原理 我们就介绍到这里 具体运用我们敬请期待