【Redis分布式缓存实战】第18章 Redis全方位性能调优
网络参数、内核参数、操作系统层级优化Redis 是内存型、高并发、低延迟的中间件优化核心围绕减少网络延迟、提升连接承载、避免内存阻塞、禁用 swap、降低 IO 开销。本文分3 大核心层级提供生产环境可直接落地的配置方案覆盖 Redis 自身网络配置、Linux 内核参数、操作系统底层优化。一、Redis 自身网络参数优化redis.conf直接修改 Redis 配置文件针对TCP 连接、IO 模型、客户端缓冲区、并发连接做核心优化是最基础且见效最快的优化。核心配置清单# 1. TCP监听队列配合内核somaxconn高并发必调 tcp-backlog 65535 # 2. 客户端空闲超时关闭无效连接释放资源timeout 300 tcp-keepalive 300 # 3. 最大客户端连接数配合系统ulimit承载高并发 maxclients 100000 # 4. Redis 6.0 开启多线程IO网络性能提升50% io-threads 4 # 设为CPU核心数的 1/2 ~ 1如8核设4 io-threads-do-reads yes # 5. TCP快速打开减少握手延迟Linux3.7支持 tcp-fastopen 3 # 6. 客户端输出缓冲区限制防止大流量占满内存主从必调 # 普通客户端硬限制512M软限制256M/60s client-output-buffer-limit normal 536870912 268435456 60 # 从节点客户端调大缓冲区避免主从同步断连 client-output-buffer-limit replica 1073741824 536870912 60 # 订阅客户端 client-output-buffer-limit pubsub 33554432 8388608 60关键参数说明tcp-backlogTCP 全连接队列长度高并发下必须大于默认值避免连接丢失io-threadsRedis 6.0 网络多线程仅处理网络 IO命令仍单线程是网络瓶颈核心优化项client-output-buffer-limit主从架构必须调大从节点缓冲区否则 RDB 大文件同步时会触发断连。二、Linux 内核参数优化sysctl.conf修改 /etc/sysctl.conf解决TCP 连接复用、队列溢出、内存分配、网络缓冲区问题是高并发 Redis 的核心优化。生效命令sysctl -p临时生效配置写入文件永久生效。网络内核参数最关键# -------------------------- TCP队列优化 -------------------------- net.core.somaxconn 65535 # 与redis tcp-backlog一致全连接队列上限 net.core.netdev_max_backlog 100000 # 网卡接收队列防止丢包 net.ipv4.tcp_max_syn_backlog 65535 # SYN半连接队列防SYN洪水 net.ipv4.tcp_syncookies 1 # 开启SYN Cookie防御攻击# -------------------------- TIME_WAIT优化高并发短连接必备 -------------------------- net.ipv4.tcp_tw_reuse 1 # 复用TIME_WAIT连接必开 net.ipv4.tcp_tw_recycle 0 # 【严禁开启】NAT环境会导致连接异常 net.ipv4.tcp_fin_timeout 30 # 缩短FIN等待时间默认60s# -------------------------- TCP保活参数 -------------------------- net.ipv4.tcp_keepalive_time 300 net.ipv4.tcp_keepalive_intvl 30 net.ipv4.tcp_keepalive_probes 5 # -------------------------- TCP缓冲区优化提升网络吞吐量 -------------------------- net.core.rmem_max 16777216 # 最大读缓冲区16M net.core.wmem_max 16777216 # 最大写缓冲区16M net.ipv4.tcp_rmem 4096 16384 16777216 # 自动调节缓冲区min default max net.ipv4.tcp_wmem 4096 16384 16777216 net.ipv4.tcp_timestamps 1 # 开启时间戳防止序列号回绕# -------------------------- TCP快速打开 -------------------------- net.ipv4.tcp_fastopen 3内存内核参数Redis 强制要求# 【Redis官方强制】内存过量分配避免fork(RDB/AOF重写)失败 vm.overcommit_memory 1 # 【禁用swap】尽量不使用交换分区0禁用1最低使用 vm.swappiness 0 # 降低脏页刷盘阻塞减少持久化时的性能波动 vm.dirty_background_ratio 5 vm.dirty_ratio 10文件 / 进程内核参数# 系统最大文件句柄配合Redis maxclients fs.file-max 1000000 # 最大进程数 kernel.pid_max 655350三、操作系统层级优化系统底层内核参数之上的系统级配置解决文件句柄、CPU、内存、磁盘、网卡的底层瓶颈Redis 官方强制要求部分配置。文件句柄限制高并发必备Redis 每个客户端连接占用 1 个文件描述符fd高并发下必须解除系统默认的 fd 限制。永久配置推荐修改 /etc/security/limits.conf# redis用户 软限制/硬限制 最大打开文件数 redis soft nofile 655350 redis hard nofile 655350 * soft nofile 655350 * hard nofile 655350Systemd 托管 Redis 额外配置如果用 systemd 管理 Redis修改 /usr/lib/systemd/system/redis-server.service[Service]LimitNOFILE655350生效systemctl daemon-reload systemctl restart redis-serverCPU 优化降低上下文切换2.1 CPU 核心绑定Redis 主流程是单线程绑定独占 CPU 核心减少上下文切换taskset -c 0 redis-server /etc/redis/redis.conf2.2 关闭 CPU 节能模式切换为性能模式提升 CPU 主频cpupower frequency-set --governor performance2.3 禁用透明大页THP【Redis 官方强制】THP 会导致 Redis fork() 延迟飙升秒级必须关闭# 临时生效 echo never /sys/kernel/mm/transparent_hugepage/enabled echo never /sys/kernel/mm/transparent_hugepage/defrag # 永久生效写入rc.local echo echo never /sys/kernel/mm/transparent_hugepage/enabled /etc/rc.local内存优化禁用 SwapRedis 完全依赖内存使用 swap 会导致性能断崖式下跌必须彻底禁用# 临时禁用swap swapoff -a # 永久禁用注释/etc/fstab中的swap行 sed -i /swap/s/^/#/ /etc/fstab磁盘优化仅针对持久化Redis 持久化RDB/AOF依赖磁盘无持久化需求可忽略使用 SSD机械盘无法满足 Redis 持久化 IO 需求关闭文件访问时间挂载磁盘时添加noatime,nodiratime参数减少磁盘 IOAOF 优化生产用appendfsync everysec性能 安全平衡禁用always文件系统推荐ext4/xfs禁用日志冗余功能。网卡优化开启网卡多队列RSS提升网络并发处理能力关闭网卡节能模式避免网络延迟波动增大网卡硬件缓冲区。其他系统优化关闭防火墙 / SELinux内网生产环境减少网络开销禁用无用服务释放 CPU / 内存资源时间同步主从集群必须开启 NTP避免时间不一致导致同步异常。四、生产环境核心禁忌必看严禁开启tcp_tw_recycleNAT 环境下会导致大量连接失败必须关闭 THP否则 RDB/AOF 重写时 Redis 会阻塞必须禁用 swapswap 会让 Redis 延迟从微秒级变成毫秒级必须开启 Redis 多线程 IORedis 6.0 网络瓶颈的最优解主从架构必须调大 replica 缓冲区防止大文件同步断连。总结Redis 层调大连接队列、开启多线程 IO、优化客户端缓冲区内核层优化 TCP 队列 / 复用、强制内存过量分配、禁用 swap系统层解除文件句柄限制、绑定 CPU、关闭 THP、禁用 swap、SSD 磁盘。按照以上配置Redis 可轻松支撑10 万 并发连接延迟保持在亚毫秒级。客户端优化连接池配置、命令批量操作、管道Pipeline实战Redis 服务端单线程能轻松支撑10w QPS性能瓶颈几乎都在客户端频繁创建销毁 TCP 连接、多次网络往返RTT、单命令循环调用是最常见的问题。本文从三大核心优化点出发讲透原理 生产级配置 实战代码覆盖 JavaJedis/Lettuce、Python 主流客户端连接池复用 TCP 连接消除建连销毁开销原生批量命令服务端单命令执行多操作减少 RTTPipeline 管道客户端打包多命令一次性发送极致降低 RTT一、连接池配置必做优化为什么必须用连接池Redis 基于 TCP 通信频繁创建 / 关闭连接会产生大量三次握手、四次挥手开销高并发下还会导致端口耗尽、服务超时。连接池作用预先初始化一批连接客户端复用连接执行命令执行完归还连接避免重复建连。核心配置参数生产通用所有客户端连接池的核心参数一致直接抄作业参数含义生产推荐配置maxTotal连接池最大总连接数CPU 核心数 ×2 磁盘数或按 QPS1w QPS≈10~20 连接maxIdle最大空闲连接数等于 maxTotal避免频繁缩容minIdle最小空闲连接数5~10核心连接预热保持可用connectTimeout连接超时时间200msmaxWaitMillis获取连接的最大等待时间500ms超时抛异常防止阻塞testOnBorrow获取连接时是否校验false开启会严重损耗性能实战配置1Java Jedis 连接池传统同步客户端!-- 依赖 -- dependency groupIdredis.clients/groupId artifactIdjedis/artifactId version4.4.3/version /dependencyimport redis.clients.jedis.Jedis; import redis.clients.jedis.JedisPool; import redis.clients.jedis.JedisPoolConfig; public class RedisPoolUtil { // 声明连接池单例全局复用 private static final JedisPool JEDIS_POOL; static { // 1. 连接池配置 JedisPoolConfig poolConfig new JedisPoolConfig(); poolConfig.setMaxTotal(20); // 最大连接数 poolConfig.setMaxIdle(20); // 最大空闲连接 poolConfig.setMinIdle(5); // 最小空闲连接 poolConfig.setMaxWaitMillis(500); // 获取连接超时 poolConfig.setTestOnBorrow(false); // 关闭校验 // 2. 初始化连接池 JEDIS_POOL new JedisPool(poolConfig, 127.0.0.1, 6379, 200, 123456); } // 获取连接 public static Jedis getJedis() { return JEDIS_POOL.getResource(); } // 归还连接必须 public static void close(Jedis jedis) { if (jedis ! null) { jedis.close(); // 连接池中的close是归还不是关闭 } } }2SpringBoot 默认 Lettuce 连接池推荐Lettuce 基于 Netty 异步非阻塞性能比 Jedis 更强SpringBoot 2.x 默认集成!-- 依赖 -- dependency groupIdorg.springframework.boot/groupId artifactIdspring-boot-starter-data-redis/artifactId /dependency !-- 连接池依赖 -- dependency groupIdorg.apache.commons/groupId artifactIdcommons-pool2/artifactId /dependency# application.yml 生产配置 spring:redis:host: 127.0.0.1 port: 6379 password: 123456timeout: 200ms lettuce: pool: max-active: 20 # 最大连接数 max-idle: 20 # 最大空闲 min-idle: 5 # 最小空闲 max-wait: 500ms # 获取连接超时二、命令批量操作优先使用原理Redis 提供原生批量命令一个网络请求执行多个操作比循环单命令快 10~100 倍。✅ 优点服务端原生支持、原子性、无代码复杂度❌ 局限仅支持同类型命令如批量 String、批量 Hash不能混合命令常用批量命令表格数据类型批量写批量读StringMSETMGETHashHMSETHMGETListLPUSH/RPUSH 多值-SetSADD 多值-实战代码1Java 批量操作Jedis jedis RedisPoolUtil.getJedis(); // 1. 批量写String jedis.mset(k1, v1, k2, v2, k3, v3); // 2. 批量读 StringListString values jedis.mget(k1, k2, k3); // 3. 批量Hash操作 jedis.hmset(user:1, Map.of(name, 张三, age, 20)); ListString hashValues jedis.hmget(user:1, name, age); RedisPoolUtil.close(jedis);三、Pipeline 管道极致优化核心原理RTT往返时间是 Redis 性能的最大杀手循环单命令N 个命令 N 次 RTTPipelineN 个命令 1 次 RTT客户端缓冲所有命令一次性发送给服务端服务端执行完一次性返回结果Pipeline vs 原生批量命令表格特性原生批量命令Pipeline命令类型仅支持同类型命令支持任意混合命令sethgetlpush原子性原子性默认非原子性可结合事务实现原子性实现服务端单命令客户端打包命令适用场景简单批量读写复杂多命令组合、大批量数据导入实战代码1Java Jedis PipelineJedis jedis RedisPoolUtil.getJedis(); // 1. 创建Pipeline对象 Pipeline pipeline jedis.pipelined(); // 2. 批量添加命令混合命令 pipeline.set(p1, v1); pipeline.hset(user:2, name, 李四); pipeline.lpush(list, a, b, c); pipeline.get(p1); // 3. 同步执行并获取结果按命令顺序返回 ListObject results pipeline.syncAndReturnAll(); System.out.println(results); // [OK, 1, 3, v1] // 4. 归还连接关闭Pipeline pipeline.close();RedisPoolUtil.close(jedis);2SpringBoot Lettuce PipelineAutowired private StringRedisTemplate redisTemplate; public void pipelineTest() { // 批量执行命令 ListObject results redisTemplate.executePipelined((RedisCallbackObject) connection - { connection.set(p1.getBytes(), v1.getBytes()); connection.hGetAll(user:1.getBytes());return null; // 必须返回null }); System.out.println(results); }Pipeline 生产注意事项分批执行不要一次性缓冲 1w 命令会导致客户端内存溢出、服务端缓冲阻塞建议每 500~1000 条命令分一批非原子性Pipeline 中的命令不是原子执行的中间可能被其他客户端命令插入需要原子性则结合MULTI/EXEC事务不支持依赖命令Pipeline 中后一个命令不能依赖前一个命令的结果命令是批量发送的执行前拿不到结果四、综合最佳实践生产必看永远用连接池禁止每次操作新建连接连接池必须单例全局复用优先原生批量命令简单批量读写MSET/MGET用原生命令性能最优、原子安全复杂场景用 Pipeline大批量数据导入、混合命令组合用 Pipeline 并分批执行杜绝循环单命令这是 Redis 客户端最严重的反模式比如 for 循环里调 jedis.set ()超时控制连接池、命令都要加超时防止阻塞业务线程总结连接池解决 TCP 连接复用问题是所有优化的基础原生批量命令简单高效、原子性优先使用Pipeline打包多命令降低 RTT适合大批量 / 混合命令场景三者结合使用能让 Redis 客户端性能提升10~100 倍完美支撑高并发业务。事务、Lua脚本原子性实战与性能优化在 Redis 中原生事务和Lua 脚本是实现原子操作的两种核心方式但二者的原子性本质、适用场景、性能天差地别。本文从原理、原子性真相、实战案例、性能优化、选型五个维度带你彻底吃透两者。核心结论前置特性Redis 原生事务MULTI/EXECRedis Lua 脚本原子性伪原子命令批量执行无回滚真原子单线程独占执行不可中断逻辑支持无仅命令入队无法判断支持判断 / 循环 / 运算网络开销高多次 RTT 往返低一次请求执行所有逻辑高并发场景不推荐WATCH 冲突严重首选秒杀 / 限流 / 转账核心方案性能差优一、Redis 原生事务伪原子的批量执行Redis 原生事务基于 MULTI/EXEC/DISCARD/WATCH 实现并非关系型数据库的 ACID 事务核心是命令队列化、批量执行。核心命令MULTI开启事务后续命令入队缓存不立即执行EXEC执行事务队列中所有命令DISCARD清空事务队列放弃执行WATCH key乐观锁监听 Key 变化若 Key 在事务执行前被修改事务直接失败原子性真相必踩坑Redis 事务没有回滚机制原子性仅保证命令按入队顺序执行执行过程中不被其他命令打断若命令语法错误如命令写错整个队列全部不执行若命令运行时错误如对字符串执行INCR其他命令正常执行无任何回滚反例运行时错误不回滚MULTI SET user:1 100 # 正确入队 INCR user:1 # 正确入队 INCR user:2 abc # 运行时错误参数非法 SET user:3 200 # 正确入队 EXEC执行结果user:1101、user:3200错误命令不影响其他命令执行 →这不是真正的原子性实战WATCH 乐观锁实现转账场景用户 A 给用户 B 转账需保证余额扣减 / 增加的一致性用 WATCH 防并发修改# 1. 监听两个余额 Key WATCH balance:user1 balance:user2 # 2. 开启事务 MULTI # 3. 扣减 A 余额增加 B 余额 DECRBY balance:user1 100 INCRBY balance:user2 100 # 4. 执行事务若监听的 Key 被修改EXEC 返回 nil EXEC局限性高并发下 WATCH 冲突率极高事务频繁失败性能极差。原生事务的致命缺陷无真正原子性运行时错误无法回滚不支持逻辑判断无法判断库存、余额是否足够高并发场景下WATCH性能拉胯网络开销大多次命令往返。二、Redis Lua 脚本真原子的终极方案Redis 2.6 原生支持 Lua 脚本核心原理Redis 是单线程模型Lua 脚本会独占 Redis 线程执行执行过程中绝对不会被其他命令打断→ 这是真正的原子性核心命令# 执行脚本直接传脚本内容 EVAL 脚本内容 key个数 key1 key2 ... arg1 arg2 ... # 优化预加载脚本返回SHA哈希后续用哈希执行减少网络传输 SCRIPT LOAD 脚本内容 # 执行预加载的脚本 EVALSHA SHA哈希 key个数 key1 key2 ... arg1 arg2 ...原子性实战秒杀防超卖秒杀场景必须满足判断库存 0 扣减库存原子执行否则会超卖。原生事务无法实现无法做逻辑判断Lua 脚本完美解决步骤 1编写秒杀 Lua 脚本lua-- KEYS[1]库存Key如 stock:iphone16 -- ARGV[1]扣减数量 local stock tonumber(redis.call(GET, KEYS[1])) local num tonumber(ARGV[1]) -- 逻辑判断库存不足返回0 if stock num thenreturn 0 end -- 库存充足扣减库存返回1 redis.call(DECRBY, KEYS[1], num) return 1步骤 2Redis CLI 执行redis# 初始化库存 SET stock:iphone16 10 # 执行秒杀脚本1个Key库存Key扣减1个 EVAL local stocktonumber(redis.call(GET,KEYS[1])); local numtonumber(ARGV[1]); if stocknum then return 0 end; redis.call(DECRBY,KEYS[1],num); return 1 1 stock:iphone16 1结果要么扣减成功返回 1要么库存不足返回 0绝对不会超卖。Java 实战Jedis 客户端import redis.clients.jedis.Jedis; public class RedisLuaDemo { // 秒杀Lua脚本 private static final String SECKILL_LUA local stocktonumber(redis.call(GET,KEYS[1])); local numtonumber(ARGV[1]); if stock num then return 0 end; redis.call(DECRBY,KEYS[1],num);return 1; public static void main(String[] args) { try (Jedis jedis new Jedis(localhost, 6379)) { // 预加载脚本生产环境推荐仅加载一次 String sha jedis.scriptLoad(SECKILL_LUA); // 执行脚本key列表参数列表 Object result jedis.evalsha(sha, 1, stock:iphone16, 1); System.out.println(执行结果 result); // 1成功0失败 } } }Lua 脚本核心优势真原子性单线程独占执行无并发竞争支持复杂逻辑判断、循环、运算全覆盖低网络开销一次请求执行所有逻辑减少 RTT往返时间高并发友好秒杀、限流、分布式锁的核心方案。三、性能优化实战Lua 脚本是 Redis 高并发场景的首选优化核心围绕减少阻塞、降低网络开销、简化逻辑展开。Lua 脚本极致优化生产必做① 用 EVALSHA 替代 EVALEVAL每次传输完整脚本网络开销大EVALSHA预加载脚本到 Redis仅传输 SHA 哈希网络开销减少 90%。生产环境必须预加载脚本服务启动时加载一次即可。② 禁止编写长脚本 / 耗时脚本Redis 单线程执行 Lua 脚本长脚本会阻塞所有客户端请求✅ 规则脚本执行时间必须 1ms仅做简单判断 命令执行。❌ 禁止循环遍历大集合、sleep、复杂运算。③ 使用 Lua 局部变量Lua 中 local 局部变量比全局变量快 30%所有变量必须加 locallua-- 优 local stock tonumber(redis.call(GET, KEYS[1])) -- 劣 stock tonumber(redis.call(GET, KEYS[1]))④ 避免操作大 Key脚本中禁止批量操作大 Key如 HGETALL 大哈希会导致脚本耗时剧增阻塞 Redis。⑤ 复用命令结果多次使用的 Redis 命令结果用局部变量缓存避免重复执行lua-- 优缓存结果 local user redis.call(HGETALL, KEYS[1]) local name user[1]local age user[2] -- 劣重复执行命令 local name redis.call(HGET, KEYS[1], name) local age redis.call(HGET, KEYS[1], age)原生事务优化不推荐使用放弃WATCH高并发下冲突率极高直接改用 Lua 脚本缩短事务长度仅批量执行无依赖的简单命令无逻辑判断禁止长事务事务开启后尽快执行EXEC减少 Key 监听时间。性能对比1000 次并发请求方案执行耗时网络请求数并发安全性原生事务WATCH800ms4 次 / 请求差冲突Lua 脚本EVALSHA50ms1 次 / 请求优原子四、选型最佳实践必须用 Lua 脚本秒杀、库存扣减、转账、限流、分布式锁需要原子逻辑判断的场景可用原生事务无依赖的简单命令批量执行如批量设置 Key极少用绝对禁止用原生事务处理高并发核心业务脚本规范短小精悍、预加载、局部变量、无阻塞逻辑。总结Redis 原生事务是伪原子无回滚、无逻辑判断高并发场景禁用Lua 脚本是真原子单线程独占执行是高并发业务的终极方案性能优化核心EVALSHA预加载、短脚本、局部变量、避免大 Key生产环境所有需要原子性的业务优先选择 Lua 脚本。热点数据分片、本地缓存分布式缓存多级架构优化在高并发场景秒杀、爆款商品、热搜、推送下单个热点 Key会压垮 Redis 单节点CPU / 带宽瓶颈甚至引发缓存雪崩。核心优化思路分两层分布式缓存层Redis 热点数据分片 → 打散热点请求分摊节点压力应用架构层本地缓存 Redis 多级缓存 → 用 JVM 本地缓存挡住绝大部分流量彻底减少 Redis 压力。一、先明确Redis 热点数据的核心痛点Redis Cluster 采用哈希槽分片默认将一个 Key 映射到一个节点。若单个 Key如goods:10086爆款QPS 达到 10 万 会导致单 Redis 节点 CPU / 网络带宽打满其他节点空闲集群资源利用率极低请求阻塞引发级联故障。普通集群分片无法解决热点 Key 问题必须针对性优化。二、方案一Redis 热点数据分片优化核心原理人为将单个热点 Key 拆分为多个子 Key分散到 Redis 集群的不同节点让请求均匀分摊到多台机器横向扛住高 QPS。实现方式手动热点分片最常用原理给热点 Key 加随机后缀将一个 Key 变成 N 个分片 Keygoods:10086 → goods:10086:0、goods:10086:1、goods:10086:2读请求随机选取一个分片 Key读取写请求批量更新所有分片 Key。代码示例Java// 热点Key分片数根据集群节点数设置如3/5/10 private static final int HOT_KEY_SLOT 3; private final StringRedisTemplate redisTemplate; // 热点Key读随机取分片 public String getHotKey(String key) { // 拼接分片Keygoods:10086 → goods:10086:1 String slotKey key : ThreadLocalRandom.current().nextInt(HOT_KEY_SLOT); return redisTemplate.opsForValue().get(slotKey); } // 热点Key写批量更新所有分片 public void setHotKey(String key, String value) { for (int i 0; i HOT_KEY_SLOT; i) { String slotKey key : i; redisTemplate.opsForValue().set(slotKey, value, 5, TimeUnit.MINUTES); } }自动热点分片进阶云厂商 Redis阿里云 / 腾讯云 Redis 自带热点 Key 自动拆分功能无需手动编码代理方案Codis/Twemproxy 支持热点 Key 自动迁移、分片开源组件使用 Redis Sentinel 自定义分片中间件。优缺点✅ 优点纯 Redis 层优化不改动应用架构横向扩容扛 QPS❌ 缺点写操作成本高批量更新仅适合读多写极少的热点数据如商品详情。三、方案二本地缓存 分布式缓存 多级架构最优解核心思想用 JVM 本地缓存挡住 99% 的热点请求Redis 只作为兜底彻底降低 Redis 压力。这是工业界高并发场景的标准优化方案。多级缓存层级从外到内用户请求 → CDN静态资源 → 应用本地缓存L1JVM级 → Redis分布式缓存L2 → 数据库DBL1 本地缓存Caffeine/GuavaJVM 内存读写速度微秒级比 Redis 快 100 倍L2 分布式缓存Redis集中式存储保证数据一致性兜底数据库。核心读写流程读流程核心先查本地再查 Redis请求先查本地缓存命中直接返回本地未命中查Redis命中则写入本地缓存并返回Redis 未命中查数据库写入 Redis 本地缓存返回。写流程核心先更新 DB再删缓存保证一致性更新数据库删除本地缓存所有应用节点删除 Redis 缓存禁止「更新缓存」用「删除缓存」避免并发脏数据。本地缓存选型组件性能淘汰算法推荐度Caffeine最快LRULFUW-TinyLFU⭐⭐⭐⭐⭐Guava Cache快LRU⭐⭐⭐Ehcache中等支持磁盘持久化⭐⭐首选 CaffeineJava 生态性能天花板SpringBoot 默认集成。SpringBoot 实战代码Caffeine Redis1依赖!-- Caffeine 本地缓存 -- dependency groupIdcom.github.benmanes.caffeine/groupId artifactIdcaffeine/artifactId /dependency !-- Redis -- dependency groupIdorg.springframework.boot/groupId artifactIdspring-boot-starter-data-redis/artifactId /dependency2配置本地缓存Configuration public class CacheConfig { Bean public CacheString, Object caffeineCache() { return Caffeine.newBuilder().maximumSize(10000) .expireAfterWrite(5, TimeUnit.MINUTES).build(); } }3多级缓存工具类Service public class MultiLevelCacheService { private final CacheString, Object caffeineCache; private final StringRedisTemplate redisTemplate; // 读多级缓存逻辑 public Object get(String key) { // 1. 查本地缓存 Object localVal caffeineCache.getIfPresent(key); if (localVal ! null) { return localVal; } // 2. 查 RedisString redisVal redisTemplate.opsForValue().get(key); if (redisVal ! null) { caffeineCache.put(key, redisVal); // 写入本地缓存 return redisVal; } // 3. 查数据库模拟 Object dbVal getDataFromDB(key); // 4. 写入两级缓存 redisTemplate.opsForValue().set(key, dbVal.toString(), 10, TimeUnit.MINUTES); caffeineCache.put(key, dbVal); return dbVal; } // 写更新DB 删除两级缓存 public void update(String key, Object value) { // 1. 更新数据库updateDB(key, value); // 2. 删除两级缓存先本地再Redis caffeineCache.invalidate(key); redisTemplate.delete(key); } }关键问题解决1本地缓存一致性核心难点本地缓存是 JVM 私有多应用节点无法同步解决方案短过期时间5~10 分钟自动过期牺牲强一致性换性能消息通知删缓存用 Redis Pub/Sub、MQKafka/RocketMQ广播删缓存消息所有节点收到后删除本地缓存不适用强一致场景本地缓存只放允许短暂不一致的热点数据商品详情、热搜。2防 OOM严格限制本地缓存最大容量如 1 万条使用自动淘汰算法。3热点 Key 自动识别业务预判秒杀、活动提前标记热点主动加载到本地缓存监控统计客户端统计请求频率自动将高频 Key 加入本地缓存Redis 原生redis-cli --hotkeys查看热点 Key。四、双方案结合最佳实践高并发场景下多级缓存 热点分片组合使用最外层本地缓存Caffeine挡住 99% 请求中间层Redis 热点分片分摊剩余 1% 请求兜底数据库。适用场景读多写少商品详情、热搜、用户信息超高并发秒杀、双十一爆款、春晚推送。五、避坑总结热点分片仅适合读多写少写操作会批量更新禁止用于高频写数据本地缓存不存大对象、不存强一致数据必须限制内存大小防止 OOM缓存一致性写操作一定用「先更新 DB再删缓存」兜底方案多级缓存都失效时必须限流、降级保护数据库。总结Redis 热点分片打散热点 Key 到多节点解决 Redis 单节点瓶颈本地缓存 Redis 多级架构用 JVM 缓存挡流量是高并发优化的核心最优组合本地缓存L1 Redis 热点分片L2兼顾性能与一致性。