给Redis选服务器的时候,很多人习惯性地盯着CPU看——几核、主频多少、有没有超线程。这个思路放在跑数据库或者Java应用上没问题,但放到Redis上,方向就偏了。Redis的性能瓶颈几乎从来不在CPU,内存容量和内存质量才是决定Redis能不能扛住的关键。
Redis为什么对CPU不敏感
Redis的读写操作全部在内存中完成。内存的读写速度比磁盘快几个数量级,一个简单的GET或SET操作,CPU只需要执行极少量的指令就能完成。这意味着Redis处理请求的速度极快,CPU大部分时间都在空闲状态。
有测试数据可以说明问题:一台6核Intel Xeon处理器、15MB三级缓存的机器,在数据集只有64MB时,Memcached的吞吐量可以达到410万OPS;但当数据集增大到60GB时,吞吐量下降到123万OPS。数据集从64MB到60GB,CPU没变、内存没变,性能却掉了3.3倍。原因只有一个:数据量增大后,CPU的缓存命中率从接近100%暴跌到20%左右,CPU不得不频繁地等待从内存中读取数据。
这个实验揭示了一个关键事实:Redis的瓶颈不是CPU的算力,而是CPU访问内存的效率。当数据量超过CPU缓存的承载能力时,CPU再快也没用,它得等内存把数据送过来。
Stack Overflow上有一个很精辟的总结:Redis本质上仍然是I/O密集型的——只不过这里的I/O指的是“内存I/O”。在Redis负载很高、达到最大请求速率的时候,它通常是在等待网络带宽或者内存带宽,CPU使用率反而不高。
内存容量:决定Redis能装多少数据
Redis作为缓存使用,最重要的功能就是把热数据放在内存里,让应用不用去查数据库。内存能装多少数据,直接决定了缓存命中率。
如果内存不够,Redis会触发淘汰策略(比如allkeys-lru),把一部分键踢出去。被淘汰的键意味着下一次请求要从数据库重新加载,不仅慢,还给数据库增加了压力。缓存命中率下降,整个系统的响应时间就会上升。
内存容量的选择逻辑:
先估算你的业务需要缓存多少数据。假设你的应用有100万条商品记录,每条记录序列化后平均2KB,那就是2GB的数据量。但Redis还有额外的内存开销——键名、过期时间、数据结构本身的开销,实际占用通常是原始数据的1.2到1.5倍。所以你需要准备至少2.4GB到3GB的内存。
一个实用的经验法则:单实例Redis的内存占用不要超过物理内存的70%-80%。剩下的20%-30%留给系统进程、Redis的持久化操作(fork子进程时会占用额外内存)、以及连接缓冲区。如果Redis把物理内存吃满了,系统会开始使用swap,而swap对Redis是灾难性的——Redis的设计前提就是“数据在内存里”,一旦发生swap,延迟会从微秒级变成毫秒级。
有实际运维经验的人提到:Redis使用内存超过60%后稳定性就下降了,超过70%时集群failover的频率会明显增加。这不是Redis本身的问题,而是宿主机内存压力增大后,各种连带效应开始显现。
内存质量:容易被忽略的第二个维度
“内存大”只是基础,“内存好”才是加分项。这里说的“好”,主要指两个方面:内存的频率/带宽,以及CPU缓存的大小。
前面提到的那篇VLDB论文里面做了一个实验:同样的数据集(64MB到60GB),同样的CPU,只是数据集大小不同,吞吐量就差了3.3倍。核心变量是CPU三级缓存的命中率。当数据集小到能塞进CPU的L3缓存时,数据访问几乎不经过内存,速度极快;一旦数据集超出缓存容量,每次访问都要去内存里取,延迟从几十个CPU周期变成几百个。
这个规律对Redis选型有直接指导意义:如果你的Redis数据集能控制在CPU缓存容量以内,性能会有质的飞跃。现代服务器CPU的L3缓存通常在20MB到100MB之间。如果你的业务热点数据能压缩到50MB以内,选一颗L3缓存较大的CPU(比如AMD的EPYC系列或者Intel的Xeon系列高缓存型号),性能会比单纯堆内存更好。
但对于绝大多数生产环境的Redis来说,数据集动辄几个GB,远超CPU缓存容量。这种情况下,内存带宽就成了关键指标。服务器内存的带宽决定了CPU从内存中拉取数据的速度。同样是DDR4,2666MHz和3200MHz的带宽差距大约20%。在数据集远大于CPU缓存的场景下,这个差距会体现在吞吐量上。
CPU在Redis场景下到底扮演什么角色
说了这么多“CPU不重要”,但CPU也不是完全没用。它的作用主要体现在三个地方:
第一,处理网络I/O。Redis 6.0之后引入了多线程I/O,网络数据的读写可以由多个线程并行处理,提高吞吐量。但这个多线程只处理网络I/O,实际的命令执行仍然是单线程的。所以增加CPU核心对提升Redis的QPS有一定帮助,但不是线性关系。
第二,处理复杂命令。如果业务里用了SORT、ZUNIONSTORE、EVAL(Lua脚本)这类复杂度较高的命令,CPU就会成为瓶颈。但这些命令在缓存场景里并不常见。
第三,持久化操作。Redis做RDB快照时会fork一个子进程,这个子进程需要占用CPU资源来写磁盘。如果快照频繁触发,CPU会有明显波动。
对于典型的缓存场景(GET/SET/EXPIRE),CPU的利用率通常很低。一台4核的服务器跑Redis,CPU使用率经常在5%以下,瓶颈完全在内存和网络。
具体配置推荐
根据上面的分析,给几个不同规模的配置建议:
小型应用(日活几千,缓存数据量<2GB):2核CPU、4GB内存的VPS就够用。CPU的核心数不是关键,单核性能只要不是太差就行。重点是把maxmemory设成3GB左右(预留1GB给系统),并配置maxmemory-policy allkeys-lru。
中型应用(日活几万,缓存数据量5-10GB):4核CPU、16GB内存。maxmemory设成12GB左右。这个规模下,内存带宽开始变得重要,如果预算允许,选择内存频率较高的机型。
大型应用(日活几十万以上,缓存数据量>20GB):这时候单实例已经不够了,需要用Redis Cluster做分片。每个分片节点的配置可以参考:8核CPU、32GB内存,maxmemory设成24GB左右。集群模式下,每个节点的maxmemory需要独立设置,并且要考虑分片策略和数据分布。
给Redis选服务器,先把内存容量算清楚,再考虑内存带宽和CPU缓存,最后才看CPU核心数。
Redis的性能上限由“数据能不能全部装进内存”决定,由“CPU访问内存的效率”决定,而不是由“CPU能算多快”决定。一台CPU很强但内存不够的服务器,跑Redis会频繁触发淘汰和swap,性能惨不忍睹;一台CPU普通但内存充裕的服务器,反而能稳稳地扛住高并发。
如果你的业务已经在用Redis,先去redis-cli里跑一下INFO memory,看看used_memory和maxmemory的差距。如果used_memory经常逼近maxmemory,那说明该加内存了,而不是该换CPU。
推荐文章