面试题答案
一键面试利用Redis特性保障并发处理原子性的方法
- Redis单线程特性:Redis是单线程模型,这意味着同一时间只有一个命令在执行,所以在执行限流逻辑时,只要将相关操作作为一个整体交给Redis执行,就能保证原子性。
- 使用事务(Multi - Exec):通过
MULTI
命令开启事务,将一系列限流操作(如计数、判断、更新等)放入事务中,最后用EXEC
命令提交事务。在事务执行期间,Redis会将这些命令按顺序依次执行,不会被其他客户端的命令打断,从而保证了原子性。 - 使用Lua脚本:Redis支持执行Lua脚本,Lua脚本在Redis中也是原子执行的。将限流逻辑写成Lua脚本,然后使用
EVAL
或EVALSHA
命令执行脚本。因为脚本内的所有操作在Redis内部是作为一个整体执行的,所以可以确保原子性。
可能用到的命令示例
- INCR命令:用于对指定键的值进行原子性递增。在限流场景中,可以用来记录当前窗口内的请求次数。例如,每次请求到达时,执行
INCR key
,key
代表当前限流窗口的计数键。 - EXPIRE命令:用于设置键的过期时间。在限流场景中,结合窗口时间设置计数键的过期时间,以实现固定窗口限流。例如,设置计数键
key
在60秒后过期,执行EXPIRE key 60
。 - MULTI、EXEC命令:如前面所述,开启事务和提交事务。示例:
MULTI
INCR key
GET key
EXEC
这里先使用INCR
递增计数,然后用GET
获取当前计数,整个过程是原子的。
4. EVAL命令:执行Lua脚本。假设Lua脚本实现了限流逻辑(检查计数、判断是否超过限制、更新计数等),示例:
EVAL "local current = redis.call('INCR', KEYS[1]); if current == 1 then redis.call('EXPIRE', KEYS[1], ARGV[1]); end; if current > tonumber(ARGV[2]) then return 0; else return 1; end" 1 key 60 10
其中KEYS[1]
是计数键key
,ARGV[1]
是窗口时间60秒,ARGV[2]
是限流阈值10。该脚本实现了限流逻辑,并保证了原子性。