Redis性能抖动?可能是操作系统Page Cache被冲刷了

Redis性能抖动?可能是操作系统Page Cache被冲刷了 上周排查一个线上服务性能抖动的问题,花了大半天时间,从应用日志、数据库慢查询一直追到 Redis 监控,最后发现 Redis 集群的 CPU 和内存使用率都挺正常,但服务的响应时间就是会间歇性飙升。直到我把目光从应用层下移,看了一眼服务器的vmstat和sar命令输出,才恍然大悟——问题出在操作系统上。大量的磁盘 I/O 等待,并不是因为 Redis 没命中缓存,而是因为操作系统的 Page Cache(页面缓存)被其他进程频繁冲刷,导致本该在内存中命中的数据,被迫去磁盘上读取。这个经历让我重新审视了一个被我们长期忽视的“隐形玩家”:操作系统内核自带的缓存机制。我们花了大量精力去设计、调优 Redis 这样的应用层缓存,却常常忘了,操作系统在底层默默地为所有 I/O 操作提供了一层更基础、更普适的缓存。它不声不响,却无处不在;它没有复杂的淘汰策略配置,却直接影响着所有应用的性能基线。今天,我们就来聊聊这个“隐形缓存之王”。这不是要否定 Redis 的价值,Redis 在应用语义缓存、数据结构丰富性上无可替代。而是想探讨一个更本质的问题:当我们谈论缓存时,如果只盯着 Redis,是不是错过了性能优化棋盘上最重要的一块?理解操作系统缓存,不是替代 Redis,而是为了让我们在使用 Redis 时更清醒,在排查问题时更全面,在设计系统时更有底气。1. 重新认识缓存:从应用层到底层,不止 Redis 这一层当我们提到“缓存”,脑海里第一个跳出来的通常是 Redis、Memcached,或者本地内存缓存如 Caffeine。这没错,它们是应用层缓存,解决的是业务逻辑层面的数据快速存取问题。比如,把用户会话、热门商品信息、复杂的计算结果存起来,避免重复计算或访问后端数据库。但缓存是一个多层次的概念。在应用层之下,数据库自己就有缓存(如 InnoDB Buffer Pool),用来缓存表数据和索引。再往下,就到了文件系统层。当你读取一个文件时,数据并不会每次都从机械硬盘或 SSD 的物理块上读取。操作系统会把这些数据缓存在内存中,这就是Page Cache(页缓存)。同理,当你写入文件时,数据也常常是先写入 Page Cache,再由操作系统在后台异步刷到磁盘,这被称为Buffer Cache(缓冲缓存)的写缓冲机制。为什么操作系统要做这件事?因为内存和磁盘的速度差距是数量级的。一次内存访问大约是 100 纳秒,而一次磁盘寻道可能需要 10 毫秒,相差十万倍。操作系统用空闲内存来缓存磁盘数据,是提升 I/O 性能最直接、最有效的手段,没有之一。它对所有应用程序透明,无论你的程序是用 Java、Go 还是 Python 写的,只要进行文件读写,就在受益于这层缓存。所以,一个完整的数据读取路径可能是这样的:应用首先检查自己的本地内存缓存(如果有)。未命中,则请求 Redis(应用层缓存)。Redis 未命中,应用去查询数据库。数据库在其 Buffer Pool 中查找数据页。Buffer Pool 未命中,数据库向操作系统发起文件读取请求。操作系统检查 Page Cache。Page Cache 命中,数据直接返回给数据库;未命中,则触发真正的磁盘 I/O。可以看到,操作系统缓存是守护磁盘 I/O 的最后一道,也是最关键的一道防线。Redis 的未命中,可能只是导致一次数据库查询;而 Page Cache 的未命中,则必然导致一次昂贵的磁盘操作。在大量随机读或文件服务的场景下,Page Cache 的命中率直接决定了系统的吞吐量和延迟。2. 操作系统的缓存策略:简单、粗暴、有效与 Redis 提供了 LRU、LFU、TTL 等多种可配置的淘汰算法不同,操作系统的缓存管理策略相对“简单粗暴”,但正因如此,也极其高效和稳定。核心机制:页缓存(Page Cache)与回写(Writeback)读缓存(Page Cache):当进程读取文件数据时,内核会将磁盘上的数据块(block)加载到内存的页面(page)中。后续任何进程再次读取同一文件数据(即使是文件的不同位置),只要对应的页面还在内存中,就