redis并发竞争key问题如何解决

1. 问题描述

并发竞争key这个问题简单讲就是:

同时有多个客户端去set一个key。

示例场景 1

例如有多个请求一起去对某个商品减库存,通常操作流程是:

  • 取出当前库存值

  • 计算新库存值

  • 写入新库存值

假设当前库存值为 20,现在有2个连接都要减 5,结果库存值应该是 10 才对,但存在下面这种情况:

redis并发竞争key问题如何解决

示例场景 2

比如有3个请求有序的修改某个key,按正常顺序的话,数据版本应该是 1->2->3,最后应该是 3

但如果第二个请求由于网络原因迟到了,数据版本就变为了 1->3->2,最后值为 2,出问题了。

2. 解决方案

2.1 乐观锁

乐观锁适用于大家一起抢着改同一个key,对修改顺序没有要求的场景。

watch 命令可以方便的实现乐观锁。

需要注意的是,如果你的 redis 使用了数据分片的方式,那么这个方法就不适用了。

watch 命令会监视给定的每一个key,当 exec 时如果监视的任一个key自从调用watch后发生过变化,则整个事务会回滚,不执行任何动作。

redis并发竞争key问题如何解决

2.2 分布式锁

适合分布式环境,不用关心 redis 是否为分片集群模式。

在业务层进行控制,操作 redis 之前,先去申请一个分布式锁,拿到锁的才能操作。

分布式锁的实现方式很多,比如 ZooKeeper、Redis 等。

2.3 时间戳

适合有序需求场景,例如 A 需要把 key 设置为 a,然后 B 设置为 bC 再设置为 c,最后的值应该是 c

这时就可以考虑使用时间戳的方式:

A => set key1 {a 11:01}

B => set key1 {b 11:02}

C => set key1 {c 11:03}

就是在写入时保存一个时间戳,写入前先比较自己的时间戳是不是早于现有记录的时间戳,如果早于,就不写入了。

假设 B 先执行了,key1 的值为 {b 11:02},当A执行时,发现自己的时间戳11:01早于现有值,就不执行 set 操作了。

2.4 消息队列

在并发量很大的情况下,可以通过消息队列进行串行化处理。这在高并发场景中是一种很常见的解决方案。

3. 小结

“Redis 并发竞争” 问题就是高并发写同一个key时导致的值错误。

常用的解决方法:

  • 乐观锁,注意不要在分片集群中使用

  • 分布式锁,适合分布式系统环境

  • 时间戳,适合有序场景

  • 消息队列,串行化处理