Redis 并发控制

背景

开发活动报名业务,涉及到活动人数限制的问题,当并发量上来的时候,多人同时提交报名信息,将会导致活动已报名人数的不准确,对业务造成影响,如下图:

分析出现问题的原因是,设置操作发生的时候,并没有确保当前人数的准确性,即没有确保当前查询出来的已报名人数与数据库的一致性,导致客户端并发的两次操作有被覆盖的情况发生

传统数据库 VS NoSql

mysql

针对如上场景,若报名人数字段保存在mysql数据库中,可以使用一种常见的降低读写锁冲突,保证数据一致性的乐观锁机制(Compare and Set CAS),实现方案如下

将原来的操作sql代码

update act set num=#{numNew} where actId=#{actId}

改为

update act set num=#{numNew} where actId=#{actId} and num=#{numOld}

即只有当查询出来的数据与当前数据库的数据一致时,才可以进行赋值操作,否则失败

redis

若使用redis,则活动报名人数以键值对的形式存在内存中,业务代码将会对内存中的人数进行操作,相比mysql,redis的效率更高,不会造成很大的延迟(若当并发量很大时,使用mysql进行报名人数记录,CAS的方案将会导致很多客户端操作失败,用户体验不好),但使用redis,其没有很好的事务支持,以上mysql的解决方案不能很好的运用在redis上,因此如何设计redis锁,进行共享资源(已报名活动人数)的操作,是需要解决的问题

使用到的命令说明

设计Redis锁之前,需要介绍下即将用到的几个命令

SETNX

将key设置值为value,如果key不存在,这种情况下等同SET命令,返回值1。 当key存在时,什么也不做,返回值0。

watch && MULTI

watch:标记所有指定的key 被监视起来,在事务中有条件的执行(乐观锁)

MULTI:标记一个事务块的开始。 随后的指令将在执行EXEC时作为一个原子执行

当两者一起使用的时候,首先key被watch监视,若在调用 EXEC 命令执行事务时, 如果任意一个被监视的键被其他客户端修改了, 那么整个事务不再执行, 直接返回失败。如下表:

时间 客户端A 客户端B
T1 WATCH name
T2 MULTI
T3 SET name owen
T4 SET name tom
T5 EXEC

在时间 T4 ,客户端 B 修改了 name 键的值, 当客户端 A 在 T5 执行 EXEC 时,Redis 会发现 name 这个被监视的键已经被修改, 因此客户端 A 的事务不会被执行,而是直接返回失败。

GETSET

GETSET key value 返回之前的旧值value,之后设置key的新值

1 2 
下一页