Redis分布式锁和延时队列
Varsion

Redis的分布式锁和延时队列都是为了解决高并发场景下的问题。

分布式锁

在 Redis 中,如果要修改用户的状态,需要先读出用户状态,在内存中进行修改,改完之后在存进去。如果多个这样的操作同时对一个数据进行处理时,就会出现并发问题。因为 读取和保存着两个操作并不是原子性的。这个时候就需要分布式锁来进行限制程序的并发执行。

分布式锁本质上是实现一个占位机制,当其他进程来操作这个数据时,发现已经有其他进程正在使用,就只能放弃或者等待稍后再试。

加锁使用 setnx(set if not exists) 指令,只允许被一个客户端占有。先来先占, 用 完了,再调用 del 指令释放。

如果实际使用中逻辑执行到中间出现问题,可能会导致 del 命令没有被调用。这样就会陷入死锁。解决办法是使用 expire 给锁加一个过期时间,即使中间出现异常也可以在过期之后锁会自动释放。

(2.8版本之后,加入了 set 指令的拓展参数,使得 setnxexpire 可以一起执行

超时问题

Redis 分布式锁不能解决超时问题,如果在加锁和释放锁之间的逻辑执行的时间太长,以至于超出了锁的超时限制,就会出现问题。

因为这个时候锁已经过期了,第二个线程已经对这个资源进行了加锁,但是紧接着第一个线程执行完了业务逻辑,就把锁给释放了,第三个线程就会在第二个线程逻辑执行完之前拿到了锁。

避免这个问题的最好办法,就是不要用 Redis 执行这么较长时间的任务。

又一个解决办法就是为 set 命令的 value 参数设置为一个随机数,释放锁之前先匹配随机数是否一致,然后在删除这个 key。有个不必要的隐患是,匹配 key 和删除 key 并不是一个原子操作,也会引来一些不必要的问题。

可重入性

可重入性是指线程在持有锁的情况下再次请求加锁,如果一个锁支持同一个线程的多次加锁,那么这个锁就是可重入的。

但是因为其复杂性,并不推荐在实际开发中使用可重入锁。

延时队列

Redis 提供了更简便的方法搞定消息队列(对于那些只有一组消费者的消息队列),Redis 的消息队列并不是很专业的消息队列,并没有 ack 保证。

异步消息队列

Redis 的 list 数据结构常用来作为异步消息队列使用,使用 rpushlpushlpoprpop 来操作队列。

空队列问题

消费者是通过队列的 pop 操作然后来获取消息,然后进行处理。这是作为队列消费者的生命周期。

如果队列空了,消费者就会陷入 pop 死循环,不停的 pop 企图获取数据。这就是浪费资源的空轮询,空轮询回拉高客户端的 CPU 消耗,Redis 的 QPS 也会被拉高。

通常可以使用 sleep 来解决问题,让线程 sleep 1s 就可以显著的降低资源消耗。

延迟

通过 sleep 可以解决问题。但是带来的麻烦事,睡眠会导致消息的延迟增大。如果只有一个消费者,那么这个延迟就是 1s,如果有多个消费者,这个延迟会有所下降,因为每个消费者的 sleep 时间是岔开来的。

blpopbrpop 阻塞读可以解决这个问题,阻塞读在队列没有数据的时候,会立即进入休眠状态,一旦数据到来就立刻恢复。消息延迟几乎为0。

空闲连接管理

如果线程一直阻塞在哪里,Redis 的客户端连接就成了闲置连接,闲置过久,服务器一般 会主动断开连接,减少闲置资源占用。这个时候 blpopbrpop 会抛出异常,编写客户端的时候需要注意捕捉异常,以重新建立连接。

锁冲突

如果客户端在处理请求时加锁没加成功:

直接抛出异常

这种方式比较适合由用户直接发起的请求,用户看到错误对话框后,会先阅读对话框的内容,再点击重试,这样就可以起到人工延时的效果。如果考虑到用户体验,可以由前端的代码替代用户自己来进行延时重试控制。它本质上是对当前请求的放弃,由用户决定是否重新发起新的请求。

sleep

sleep 会阻塞当前的消息处理线程,会导致队列的后续消息处理出现延迟。如果碰撞的比较频繁或者队列里消息比较多,sleep 可能并不合适。如果因为个别死锁的 key 导致加锁不成功,线程会彻底堵死,导致后续消息永远得不到及时处理。

延时队列

这种方式比较适合异步消息处理,将当前冲突的请求扔到另一个队列延后处理以避开冲突。

  • Post title:Redis分布式锁和延时队列
  • Post author:Varsion
  • Create time:2021-05-11 16:45:22
  • Post link:https://blog.varsion.cn/post/c28b090e.html
  • Copyright Notice:All articles in this blog are licensed under BY-NC-SA unless stating additionally.
Comments