Redis读写操作中那把锁,阻塞了双方的访问和效率问题
- 问答
- 2026-01-18 23:18:56
- 3
在讨论Redis的读写操作时,我们经常会遇到一个核心的矛盾点,这个矛盾点就像一把看不见的“锁”,这把“锁”并不是我们平常编程中显式声明的那个锁,而是由Redis自身的工作机制所决定的,它虽然保证了数据在多个客户端同时访问时不会错乱,但也在一定程度上限制了访问的流畅度和系统的整体效率,造成了所谓的“阻塞”现象。

要理解这把“锁”,我们首先要明白Redis处理任务的基本方式,根据Redis官方文档(《Redis持久化》)以及其设计理念的公开说明,Redis是采用单线程模型来处理客户端的命令请求的,这意味着,在任何一个瞬间,Redis服务器的主线程只能执行一个命令,所有的命令,无论是简单的读取一个键值(读操作),还是修改某个数据(写操作),都需要在这个唯一的线程中排队等候执行,这个“排队的机制”本身就是一种隐性的、全局的“锁”,它确保了线程安全,因为不存在两个命令同时修改同一份数据的情况。
这把“锁”是如何引发阻塞和效率问题的呢?我们可以从两个方面来看:一方面是写操作对读操作和其他写操作的“阻塞”;另一方面是某些特殊的、耗时的操作对整个服务的“大阻塞”。

是写操作带来的微观阻塞。 想象一下,有成千上万的用户同时在访问一个使用Redis做缓存的热门网站,大部分请求是读操作,比如获取文章内容、用户信息等,这些操作通常非常快,但如果此时,有一个需要更新热门文章数据的写操作命令到来(比如文章点赞数+1),它被放入队列,即使这个写操作本身只耗时1毫秒,但在它执行的那1毫秒里,队列中所有排在他后面的读操作都必须等待,对于海量并发的场景,这种微小的延迟累积起来,就会导致平均响应时间变长,用户可能会感觉到轻微的“卡顿”,这就好比一个只有一个收银台的超市,即便大部分人只是快速买瓶水,但只要前面有一个人办理复杂的退货,后面所有人的队伍就都停滞了,Antirez(Redis创始人)在早期的博客讨论中也承认,这种单线程模型在持续高负载下,延迟的方差(即不稳定性)会增大。
是某些特定操作导致的宏观阻塞,这才是更严重的问题。 这把“锁”的威力在以下场景中体现得尤为突出:
- 大数据量的键操作: 执行像
KEYS *这样的命令,或者删除一个包含数百万个字段的巨型Hash键(使用DEL命令),这些操作需要遍历整个数据库或巨大的数据结构,由于是单线程,这个遍历过程会持续占用线程很长一段时间(可能是几百毫秒甚至秒级),在这段时间内,整个Redis服务器无法响应任何其他客户端的任何请求,所有读写操作全部被挂起,服务完全停滞,这就像收银台不是在处理交易,而是在进行全场盘货,所有顾客只能干等着。 - 持久化操作时的fork阻塞: 为了保证数据安全,Redis需要将内存中的数据定期写入硬盘(持久化),在执行RDB快照或重写AOF文件时,主进程会调用fork操作创建一个子进程来负责写入硬盘,虽然子进程的工作不影响主进程,但fork这个动作本身,在Redis内存使用量非常大的时候,可能会是一个耗时的操作,因为在某些操作系统中,fork需要复制父进程的内存页表,如果内存数据庞大,复制页表的过程会导致主线程短暂停顿(阻塞),这段时间内,Redis同样无法处理请求。
这些阻塞问题直接影响了系统的效率和可用性,对于要求高并发、低延迟的应用(如实时排行榜、秒杀系统)即使是几十毫秒的阻塞也可能是不可接受的。
为了解决这把“锁”带来的问题,社区和使用者也想出了很多办法,禁止在生产环境使用KEYS命令,用SCAN命令分批渐进式遍历来代替;对于大Key的删除,采用异步渐进式的删除方案,从Redis 4.0版本开始,引入了惰性删除(Lazy Free)机制,可以将一些耗时的删除操作放到后台线程去慢慢处理,减轻对主线程的阻塞,而到了Redis 6.0,更是引入了多线程I/O,允许使用多个线程来处理网络的读写和数据解析,但执行命令的核心环节依然由单线程负责,这相当于给超市增加了多个引导员和打包员,但收银台仍然只有一个,它优化了排队前的流程,但核心的“交易”瓶颈依然存在。
Redis通过单线程这把隐形的“全局锁”换来了简单性和数据操作的原子性保证,但代价就是在高并发和特定场景下不可避免的阻塞风险,理解这把“锁”的存在和其工作原理,是高效使用和优化Redis的关键,开发者需要做的就是清晰地认识到这一限制,并通过合理的架构设计、命令规范和新特性运用,来尽可能地绕开或缓解它带来的效率问题。

本文由芮以莲于2026-01-18发表在笙亿网络策划,如有疑问,请联系我们。
本文链接:https://haoid.cn/wenda/83317.html
