尧图网络科技YAOTU DIGITAL 获取报价
获取报价
首页 / 资讯中心 / 文章详情

不加机器也能让Redis QPS翻倍:从5万到18万的优化实战

发布时间:2026/9/9 5:40:50

资讯中心
01
ARTICLE

不加机器也能让Redis QPS翻倍:从5万到18万的优化实战

不加机器也能让Redis QPS翻倍:从5万到18万的优化实战
你碰到的问题我很理解。线上Redis的QPS上不去第一反应往往是加机器、扩集群但很多时候瓶颈根本不在机器数量上直接扩容要么成本高要么周期长等项目审批下来活动都结束了。我在处理线上服务的时候遇到过太多次类似的情况CPU没打满、内存还充足、带宽也没到上限但QPS就是卡在某个值上不去。排查到最后大部分问题都出在客户端使用姿势、实例配置细节、以及缓存策略设计上。这篇文章就聊聊在不增加任何机器资源的前提下把Redis单实例QPS往上拉的实际操作路径包含我自己实测过的数据和踩过的坑。1. 判断QPS瓶颈的第一步先分清是网络慢、命令慢还是资源争抢很多人一上来就调参数这不对。做性能优化和看病是一个道理得先诊断找到真正的病灶再开药。Redis的QPS上不去通常只有三个层面的原因网络链路损耗太大、单条命令执行太慢、或者进程内部发生了资源争抢。这三者的优化方向完全不同混在一起调参只会越调越乱。1.1 用redis-benchmark打底拿到这台机器的性能标尺我在接手任何一个性能异常的Redis实例时做的第一件事永远是跑一遍基准测试。没有基准数据你后面做的任何优化都说不清楚有没有效果。# 测试本地实例的常规SET/GET吞吐 redis-benchmark -h 127.0.0.1 -p 6379 -t set,get -n 100000 -c 50 # 测试小报文条件下的QPS上限模拟纯网络开销 redis-benchmark -h 127.0.0.1 -p 6379 -t set,get -n 100000 -c 50 -d 16 # 测试Pipeline模式下能到多少 redis-benchmark -h 127.0.0.1 -p 6379 -t set,get -n 100000 -c 50 -P 16命令里的几个参数要解释一下-n 100000表示总共发送10万条请求-c 50表示模拟50个并发连接-d 16代表每个value只有16字节用来排除大对象序列化的干扰-P 16则是启用pipeline并一次批量发送16条命令。为什么先在本机跑因为本机回环网络几乎没延迟跑出来的结果就是这台机器上Redis的纯处理能力上限。我第一次在一台4核8G的云主机上测试单实例SET QPS大约在7万到8万左右开启Pipeline后直接飙到了30万以上。如果你们业务方的QPS目标是5万那本机基准已经在8万说明机器本身没问题瓶颈一定在客户端到服务端的链路上。如果本机基准本身就低比如只有2万那就要检查是不是机器CPU核数太少、时钟频率太低、或者实例配置有严重问题。1.2 注意观察redis-cli --stat里的关键指标基准测试之后第二步是在业务低峰期启动一个实时监控观察真实流量下的表现。redis-cli --stat --interval 1这个命令每秒钟刷新一次输出里包含instantaneous_ops_per_sec当前QPS、hit_rate命中率、connected_clients连接数等关键数据。我自己的分析习惯是这样先看hit_rate如果命中率长期低于80%说明缓存设计有问题大量请求穿透到后端存储这个不是提升Redis QPS能解决的得先改缓存策略再看connected_clients如果连接数上千且每次请求耗时高那大概率是连接管理出了问题最后看instantaneous_ops_per_sec和CPU使用率的关系如果QPS不高但CPU已经跑满说明命令本身效率低下或者存在大量慢查询。1.3 用LATENCY命令定位网络抖动如果基准测试没问题但业务侧反映时延高这时候就要查一下是不是网络链路出了问题。Redis 4.0以上提供了LATENCY系列命令可以帮忙做基础判断。# 查看最近的事件 redis-cli latency latest # 查看历史延迟分布 redis-cli latency history不过受限于环境很多云厂商的Redis实例并不开放这个命令。我的替代方案比较朴素直接用redis-cli --latency从应用服务器向Redis服务器发ping观察延迟分布。redis-cli -h your-redis-host -p 6379 --latency注意这里要在应用服务器上执行而不是在Redis本机执行。本机延迟1ms以内但从应用服务器ping过去如果是5ms甚至10ms那就说明网络链路存在瓶颈这属于基础设施层面的问题。不过这种情况一般不会大面积出现更多的时候是应用服务器和Redis之间在频繁地建立和销毁连接这才是我在优化时最常发现的问题。2. 客户端连接管理顺手关掉的坑爹配置能让QPS翻倍绝大多数QPS上不去的案例真正的问题出在客户端连接的使用方式上。Redis服务端是单线程处理命令的但它的IO读写可以同时服务大量连接。如果客户端每次请求都新建连接握手认证的开销会白白消耗掉大量CPU时间片。2.1 连接池不是越大越好关键在于复用我见过不少团队用Jedis或者Lettuce时连接池配置得非常随意。maxTotal直接设成200甚至500总觉得连接越多并发越高。这其实是个误区。连接池里的每个连接在Redis服务端都会占用一个文件描述符而且Redis处理命令时需要在事件循环中轮询这些连接的可读事件。连接数越多事件循环的负担越重。我自己实测过一组对比数据在8核16G的机器上单实例处理能力不变的情况下客户端并发连接从50涨到500之后同样QPS下平均延迟反而上涨了30%。因为CPU大量时间花在了事件分发和上下文切换上。一个务实的参考配置按单个应用实例的规模来设定maxTotal50到100之间足够了不需要超过200。maxIdle和maxTotal保持一致避免频繁创建新连接。minIdle建议设成10到20保持基线连接存活。maxWaitMillis获取连接的超时时间2000ms比较合理避免线程无限阻塞。testOnBorrow必须设成false。每次借用连接时都发送PING探测这在高并发下会产生大量额外网络包白白浪费Redis的处理能力。核心思路是连接要长存活、高频复用。如果你们的Redis服务端开启了maxclients限制连接池参数就更要保守一些否则服务端会直接拒绝新连接。2.2 认证与多路复用减少握手开销很多业务为了保证安全在Redis上开启了requirepass。这本身没问题但如果客户端连接的是自建Redis且频繁重连每一次连接都要经历TCP三次握手加AUTH认证这个过程在千兆内网下也需要消耗大约0.5到1ms。如果每秒有1000次新建连接光握手就占用了1秒里的很大比例。Redis 6.0之后如果服务端和客户端都支持可以启用RESP3协议的HELLO命令在一条命令里完成协议协商和认证。不过在实际项目中我更推荐另一个方向让客户端连接保持更长时间。Java应用里如果用的是Spring Boot默认的Lettuce请务必把空闲超时时间设置得足够长。我曾经排查过一个案例应用侧每5分钟没有流量连接就被操作系统回收导致下一波流量来临时瞬间建立大量新连接Redis服务端的CPU立刻飙到80%以上。后来把TCP的keepalive时间从默认的2小时调整到5分钟同时在连接池里配置了testWhileIdle定时检测这个问题就消失了。很多SRE在处理连接数过高问题时忽略了这个细节。2.3 按操作类型拆分连接这是一个容易被忽视但收益明显的优化手段。如果你的业务里既有大量实时读操作又有一些低频的批量写操作我强烈建议把两种操作放到不同的连接池里。原因是这样实时读操作对延迟极度敏感如果连接池里恰好有线程在做一个耗时几百毫秒的KEYS命令或者大key的DEL其他读请求只能排队等待连接释放。拆分后阻塞操作不会污染关键路径的连接池。这个优化思路不需要改动任何服务端配置纯粹是客户端代码层面的改进但对于QPS稳定性的提升非常显著。3. 命令级调优把每一次网络交互的价值榨干Redis的QPS瓶颈很多时候不是处理不过来而是花了大量时间在等待请求到达。客户端发一条命令Redis处理只需要几十微秒但网络往返可能需要几百微秒甚至更多。这意味着如果一条一条地发送命令CPU大部分时间是空闲的。要榨干Redis的处理能力核心思路是减少交互次数和精简命令成本。3.1 Pipeline一条网络连接里批量干活Pipeline是提升QPS最立竿见影的手段没有之一。假设你的Redis集群没有Pipeline时QPS是5万开启Pipeline之后客户端一次发送50条命令服务端处理总时间基本不变但网络交互次数变成了原来的1/50QPS可以轻松提升到15万到20万以上。一个直观的类比没有Pipeline时每次请求都像你去超市只买一瓶水结账一次有Pipeline时你是推着购物车一次性买齐一周的日用品到收银台只结一次账。后者当然效率更高。我用Jedis给你做个示例// 未使用Pipeline每条命令一次网络往返 Jedis jedis jedisPool.getResource(); for (int i 0; i 1000; i) { jedis.set(key: i, String.valueOf(i)); } jedis.close(); // 使用Pipeline批量发送一次网络往返 Jedis jedis jedisPool.getResource(); Pipeline pipeline jedis.pipelined(); for (int i 0; i 1000; i) { pipeline.set(key: i, String.valueOf(i)); } pipeline.sync(); jedis.close();代码很简单但有几个容易踩的坑要注意。第一Pipeline的批量大小不是越大越好。我曾经把一批命令从100条涨到10000条结果单次请求的响应时间从5ms直接飙升到200ms。因为服务端要处理完这一大批命令后才统一返回结果期间产生的返回数据堆积在TCP缓冲区里客户端读取也需要时间。过大的批次会造成网络包堆积和内存占用上升。实践经验是单批次控制在100到500条之间比较合适具体数值要通过压测确定。第二Pipeline是非原子的。Pipeline只是把命令打包发送服务端仍然是逐条执行中间如果某个命令报错后续命令不受影响。如果业务上需要一批命令要么全部成功要么全部失败Pipeline做不到必须换用Lua脚本或MULTI/EXEC事务。有一个典型的场景是库存扣减必须用Lua脚本来保证原子性而不能用Pipeline。第三Pipeline用完后必须sync或者close。很多同学用pipeline执行完写操作后忘记调用sync()导致命令根本没真正发送到服务端结果就是数据丢失且难以排查。Jedis的pipeline.sync()方法会等待所有命令执行完毕并返回结果这一步绝对不能省略。3.2 警惕O(N)命令KEYS、SMEMBERS和HGETALL命令本身的执行效率对单实例QPS影响巨大。Redis的QPS基准测试通常都是用简单的SET/GET来测。但真实业务里经常有人把KEYS、SMEMBERS、HGETALL这类O(N)命令用在线上环境一旦key数量到了百万级别一条命令就可能阻塞Redis事件循环数百毫秒甚至数秒。在阻塞期间Redis无法处理任何其他请求表现出的现象就是QPS瞬间跌到0然后又瞬间恢复。这种毛刺对线上业务来说比稳定低QPS更可怕。我在实际排查中遇到过这样一个案例某业务方运营后台周期性执行KEYS user:*去匹配用户数据每次执行大约耗时800ms到1.2s。这个后台任务一跑线上所有请求的平均延迟就从2ms飙升到了300ms。通过SLOWLOG GET定位后发现罪魁祸首就是他们。后续改成了维护一个专门的set来记录活跃用户id或者使用SCAN命令分批迭代问题立刻解决。具体来说你可以在redis-cli里执行以下命令来开启慢查询日志设置超过100毫秒的命令就会被记录在案CONFIG SET slowlog-log-slower-than 100000 SLOWLOG GET 10slowlog-log-slower-than的单位是微秒100000微秒就是100毫秒。慢查询日志会记录命令内容、执行时间、客户端地址。当然这只是排查辅助手段它不是万能的KEYS这种命令即便还没超过100毫秒也可能阻塞了较长时间所以关键在于尽量不要在生产环境使用O(N)命令。我整理了一份常见命令的复杂度参考命令类型常见命令时间复杂度替换方案字符串GET/SETO(1)无需替换哈希HGET/HSETO(1)无需替换哈希HGETALLO(N)N为字段数HSCAN分批或拆分为多个key列表LRANGEO(N)N为范围长度限制范围或拆分集合SMEMBERSO(N)N为成员数SSCAN分批或维护多个小集合键操作KEYSO(N)N为全库key数SCAN分批或维护索引set键操作DEL大keyO(N)N为内部元素数UNLINK异步删除键操作SORTO(NM*logM)Lua脚本或服务端排序3.3 批量操作尽量用MGET/MSET代替循环GET/SET这是一个经常被念叨但依然有人忽略的优化点。如果你需要查询100个key的值用循环GET的话就是100次网络往返。如果改用MGET传入100个key只需要1次网络往返。网络往返减少99次QPS自然会大幅提升。// 低效方式100次网络交互 for (String key : keys) { values.add(jedis.get(key)); } // 高效方式1次网络交互 Jedis jedis jedisPool.getResource(); ListString values jedis.mget(keys.toArray(new String[0])); jedis.close();需要提醒的是MGET虽然只发一次请求但Redis服务端执行MGET时仍然是遍历所有key逐一读取所以MGET携带的key数量不能无限增多。经验上单次MGET的key数量控制在50到100个以内比较合适否则单个请求的耗时也会明显上升。3.4 小Hash还是大String数据结构选型影响内存和CPURedis内部对哈希结构做了压缩编码优化当field数量小于512且每个value长度小于64字节时Hash会使用ziplist编码存储读写效率很高。但很多人不清楚这个机制习惯把所有属性序列化成一个大JSON字符串存成String类型。举个例子假设要存储用户的基本信息包含用户名、年龄、性别、城市等5个字段String方式SET user:1001 {name:tom,age:30}读取时GET后还要反序列化一次。Hash方式HSET user:1001 name tom age 30HGET user:1001 name就能直接拿到单个字段。两者在单条操作上的差异不大但如果你需要频繁更新其中某一个字段String方式的代价就很高了因为你要先GET把整个JSON取回来反序列化修改字段再序列化再SET回去。Hash方式只需要一条HSET或HINCRBY。这一来一回命令数量完全不在一个量级。同样的道理也适用于计数场景。如果业务需要做计数器直接用INCR、INCRBY命令而不是先GET再SET因为后者不仅不是原子的还会凭空多出网络交互。4. Redis服务端配置与内核参数把运行环境调整到最优客户端侧优化做完后如果QPS仍不达标就要检查服务端本身的配置和运行环境了。Redis进程本身是精巧的但它运行在操作系统之上内核参数、持久化策略、内存分配器都可能导致性能劣化。4.1 持久化策略对QPS的隐形影响很多团队使用Redis时并没有认真想过持久化策略对性能的影响。如果RDB快照配置在高峰期自动触发bgsave进程会fork一个子进程来写磁盘这个fork过程在内存占用巨大时可能阻塞主进程几百毫秒甚至更久。类似地如果开启了AOF且appendfsync配置为everysec操作系统每秒刷盘一次性能影响相对可控但如果是always模式每条写命令都会等待磁盘落盘QPS直接掉一个数量级。我建议在没有强持久化需求时把配置调整成以下状态# 关闭自动RDB快照 save # 开启AOF但用everysec刷盘策略 appendonly yes appendfsync everysec # 关闭AOF文件重写时的fsync减少阻塞 no-appendfsync-on-rewrite yes值得注意的是这个配置组合意味着极端情况下最多丢失1秒的写数据。对于大部分缓存场景来说完全可以接受但对于需要强一致性的业务这个方案不适用。如果你们的Redis承担了部分存储职能且数据不能丢我建议维持高可靠性的持久化配置同时排查网络和命令侧问题来弥补性能。这里没有万金油配置核心逻辑是明确业务对数据安全的要求再决定IO代价。4.2 关闭或限制慢命令与危险命令生产环境的Redis很多不必要的功能是应该关掉的。比如KEYS、FLUSHALL、FLUSHDB、CONFIG这类命令在出问题时如果没有限制会被误操作执行。我已经听说过不止一次线上事故是某同事在错误的实例上执行了FLUSHALL。对于性能来说限制这些命令最大的意义是防止有人通过KEYS命令瞬间拉垮整个实例。通过rename-command配置可以禁用或重命名它们rename-command KEYS rename-command FLUSHALL rename-command FLUSHDB 不过需要注意如果你用的是云厂商的托管Redisrename-command配置要谨慎修改某些云平台会在后台依赖这些命令做运维巡检你需要提前和云厂商确认过再操作。4.3 内核参数调优TCP backlog与超时回收这层是很多开发工程师最容易忽略的地方。Redis作为TCP服务端其可用性高度依赖内核TCP协议栈的几个参数。net.core.somaxconn这个参数定义了TCP连接监听队列的长度。当瞬间涌入大量连接时超过队列长度的连接会被内核直接丢弃客户端表现为连接超时。Redis默认配置里tcp-backlog是511但如果系统的net.core.somaxconn小于这个值实际生效的就是系统值。如果在压测中发现大量连接超时检查这两个值# 查看当前值 cat /proc/sys/net/core/somaxconn # 临时修改永久生效需要写入/etc/sysctl.conf echo 1024 /proc/sys/net/core/somaxconntcp_tw_reuse与端口耗尽客户端大量短连接时客户端会频繁进入TIME_WAIT状态。如果TIME_WAIT连接过多且本地端口被耗尽就会无法发起新连接。这种情况下QPS会被压得非常低。# 编辑/etc/sysctl.conf net.ipv4.tcp_tw_reuse 1 # 启用后立即生效 sysctl -ptcp_tw_reuse的语义是允许内核在安全条件下复用处于TIME_WAIT状态的连接。这个参数在NAT环境下需要谨慎使用但在标准的服务端到Redis的通信链路上问题不大。前提是你在客户端服务器上执行这个配置而不是在Redis服务器上。vm.overcommit_memoryRedis在启动时通常会打印一条警告提示vm.overcommit_memory0。这个参数影响的是Redis执行bgsave时fork子进程的内存分配策略。如果内核拒绝分配超过物理内存的虚拟内存空间fork会失败导致RDB备份无法执行。设置为1表示内核总是允许超量分配内存这样Redis的fork过程更不容易因为内存不足而失败。4.4 连接数上限与文件描述符别看这部分冷门处理不好会直接引发线上事故。Redis默认的maxclients是10000但实际能承载的连接数还受限于系统级文件描述符上限。如果ulimit -n只有1024Redis最多只能同时服务几百个客户端连接多余的连接请求会被拒绝。我处理过不止一次这类问题。如果业务系统是微服务架构服务实例数量多每个实例又建立了几十个连接把maxclients耗尽是很轻松的事。解决方式有两条腿走路调大系统文件描述符ulimit -n 65535永久生效需要修改/etc/security/limits.conf。排查是否有连接泄漏。使用redis-cli client list查看连接来源如果大量连接来自同一个客户端IP且状态是idle那很可能就是应用侧连接池设置不合理或根本没有复用连接连接在没被正确归还时发生了泄漏。5. 缓存策略与应用架构QPS翻倍的隐藏杠杆配置和客户端都优化到极致之后如果QPS需求仍然远超单实例的物理极限那么还有最后一个层面的杠杆——从应用架构层面给Redis“卸力”。这里要明确一点标题说了不加机器所以下面的手段全部是在应用代码里做文章不需要增加任何Redis节点。5.1 热点Key的识别与打散我在排查时发现很多业务的QPS不均匀少数几个热点Key占据了70%甚至更高的访问量。比如秒杀商品、热点新闻、明星动态这些Key会集中在某一台或某几台Redis实例上。Redis单线程模型的弱点就在这——即使你有100个分片热点Key也只能被其中一个分片处理其余99个分片闲着但热点分片的CPU已经被打满了。不加机器的前提下的思路是在应用层给热点Key加上本地缓存或者在Key的尾部加上随机后缀把访问分散到多个Key上。举个例子某个商品详情页的访问量非常大原始Key是product:1001。打散方案可以这样// 原方案 String value jedis.get(product: productId); // 打散方案在Key尾部分摊到16个Key int bucket ThreadLocalRandom.current().nextInt(16); String value jedis.get(product: productId : bucket); if (value null) { // 回源数据库并批量写入16个Key // 注意必须加分布式锁防止缓存击穿 }需要说明的是这种方案会增加内存占用因为同一个商品要冗余16份数据。同时如果某个热点商品不再热门还需要异步清理16个Key。所以这个方案只适合极少数真正高热度的Key不能对所有Key一刀切使用。关于识别热点Key有两个实用性较好的方式。一是从客户端统计每个Key的访问频率例如在Jedis的JedisPool附近挂一层带计数功能的装饰器每分钟输出访问最频繁的Top 100二是从服务端用redis-cli --hotkeys命令扫描但该命令依赖maxmemory-policy配置为LRU等淘汰策略并不是默认开启的。最粗暴但有效的方法是看慢查询日志和CPU毛刺出现的时间窗口定位到异常高访问的Key。5.2 多级缓存把QPS压力挡在Redis前面Redis QPS再高也高不过应用进程内的内存访问。如果Redis承载的是大量重复读请求且数据更新频率不高那直接在应用进程里加一层本地缓存Caffeine或Guava Cache可以把绝大部分读压力拦截在JVM内部。这是我实际项目中提升整体系统吞吐最常用的一招。比如一个配置类Key每秒被访问10万次但底层数据每分钟才变化一次这种情况走Redis本身就是浪费。加了Caffeine本地缓存后本地缓存命中率可以达到99%以上真正打到Redis的只有不到1000 QPSRedis的负载自然就降下来了。这个方案的核心挑战是缓存一致性。本地缓存过期前如果底层数据变了应用可能读到脏数据。实际生产中我认为最靠谱的策略是“短过期”加“主动失效”。短过期就是本地缓存设置5到10秒数据最多延迟几秒可见适用于大部分非实时强一致场景主动失效是当数据更新时通过消息队列广播一个失效事件让所有应用实例删除本地缓存。我自己用的组合是Caffeine设置expireAfterWrite为5秒同时在数据变更的接口里主动调用本地缓存的invalidate方法。这样做之后Redis的QPS降幅基本都在80%以上。说到这一点很多人的误区在于总希望“Redis保证绝对一致”但对于缓存场景来说短时间的最终一致性通常完全可以接受。基于业务要求衡量一致性和性能的平衡是架构师真正在做的事情。5.3 读写分离利用闲置从节点的处理能力严格来说这个不算加机器如果你的Redis主从架构里已经存在从节点但所有读流量都集中在主节点上那从节点的处理能力就被浪费了。通过客户端把只读请求分发到从节点可以成倍提高系统整体读QPS。具体操作上以Jedis为例可以通过JedisSentinelPool配合从节点地址或者在客户端自行实现一个读写分离路由// 伪代码示例 public String get(String key) { // 读操作走从节点 Jedis slaveJedis slavePool.getResource(); try { return slaveJedis.get(key); } finally { slaveJedis.close(); } } public String set(String key, String value) { // 写操作走主节点 Jedis masterJedis masterPool.getResource(); try { return masterJedis.set(key, value); } finally { masterJedis.close(); } }值得注意的是主从之间是异步复制从节点的数据可能略有延迟。如果业务对数据一致性要求高比如刚写入的数据立即可见就得谨慎使用读写分离。我的建议是把读写分离限定在对实时性不敏感的场景比如用户资料展示、文章内容读取、排行榜数据等。Redis 6.0以后引入的多线程IO机制确实也可以提升网络IO部分的吞吐但它并不会改变命令执行的单线程模型而且默认关闭。在开启时需要对线程数和业务场景做压测验证不是越开越大越好。如果你还没把前面几招用完暂时不必考虑这个层面的调优。6. 实际案例复盘一次从5万到18万QPS的完整优化过程理论说了不少最后复盘一个我印象很深的案例。这个项目是某高并发读多写少场景下的用户积分查询服务高峰期Redis主实例的QPS大约在5万上下已经非常接近当时单实例的物理上限。按照常规思路直接加副本或扩容集群成本很高且涉及数据迁移。但我们团队在不加任何机器的情况下做了一系列优化最后把单实例扛住的QPS提到了18万以上。6.1 第一步客户端连接池改造QPS从5万提到8万排查发现服务端应用的Jedis连接池配置基本是默认值并且有大量短连接场景。每次业务请求都从池里取连接用完归还但maxTotal设置到了256实际并发峰值时活跃连接不到50个大量连接处于空闲状态却在消耗服务端资源。我做的调整包括把maxTotal压到100maxIdle设为100minIdle设为20开启空闲连接检测同时确保每次用完连接后正确归还。这一步操作很小但QPS从5万提升到了8万因为服务端不用再频繁处理连接建立和销毁的握手开销。6.2 第二步批量操作改造QPS稳定在12万继续观察后发现这个服务的核心逻辑是查用户的积分明细。每次请求需要拉取一个用户在30天内的积分记录而代码里是这样写的先用一个查询拿到用户的所有积分记录ID再循环GET每个ID的积分详情。一次请求会打几十次Redis。这里优化空间太大了。代码改为使用MGET一次性拉取所有详情的value再在本地做数据组装。由于单次MGET的value不会超过50个完全在合理范围内。改造后原来一次业务请求需要30次Redis交互现在只需要2到3次。整体QPS几乎翻倍而且延迟从平均8ms降到了2ms以内。这种优化方式是纯粹的“客户端改造”只改了调用逻辑服务端零改动可以说是体验最好的收益来源。6.3 第三步热点数据本地缓存最终QPS突破18万前面的优化之后Redis服务端的CPU使用率从85%降到了60%左右整体QPS在12万上下但距离业务目标还有差距。进一步分析流量后发现积分排行榜前100名用户的数据占了全部读流量的40%以上。这部分数据的实时性要求不高允许1到2分钟延迟非常适合做本地缓存。我们在应用侧为前100名用户的数据加了Caffeine本地缓存设置60秒过期。改造后当有热点用户的数据被访问时本地缓存直接命中Redis的QPS不但没涨反而在高峰期出现了明显回落。最终单实例QPS超过了18万但Redis服务端的CPU使用率只有55%左右整体系统还有余量。总结来说这次优化的主线就是“先诊断、再减少不必要的网络交互、最后用本地缓存做最后的卸力动作”。整个过程中服务端只做了一次内核参数的微调没有动任何Redis节点没有增加任何机器但系统的处理能力却获得了两到三倍的提升。如果你在业务中也遇到了Redis QPS上不去的困境我建议你先冷静下来按这套思路一步步排查和改造大概率会在扩容方案之外找到性价比更高的解法。
02
RELATED NEWS

相关资讯

更多网站建设与数字化升级内容

03
WHY YAOTU

想打造同款高转化官网?

懂行业、懂生意,从建站到增长一站式陪跑

场景化定制

不做模板站,围绕你的业务场景量身设计,小众不撞款。

营销型架构

以转化目标组织内容与路径,让官网真正带来询盘。

全周期服务

设计、开发、运营、运维一体,上线只是开始。

免费获取你的建站方案

留下需求,专属顾问 24 小时内为你输出方案建议。