帮客户做了一次从本地缓存到Redis的分布式迁移实战踩坑与经验总结前不久帮一个做电商零售的客户重构了他们的库存中心。客户是个中型品牌日均订单量大概在5万单左右但在大促期间峰值能冲到20万。项目初期为了赶进度我们直接用了应用本地的Caffeine缓存来扛QPS。说实话刚上线那两周风平浪静性能测试看着也漂亮我以为这就稳了。直到上个月双11预热流量稍微上来一点几个节点的服务日志开始疯狂报错库存扣减出现超卖现象用户投诉电话都快被打爆了。当时我觉得这样就行结果发现错了。本地缓存最大的问题就是数据一致性太差。我在A节点更新了库存B节点还是旧的旧数据因为它们是各自维护一份内存拷贝。一旦某个节点宕机重启或者发生服务漂移缓存穿透和雪崩的风险就直接暴露出来了。客户急得要命让我立刻出方案。我仔细复盘了一下决定把这套复杂的本地缓存逻辑彻底砍掉迁移到Redis集群。这不仅仅是换个组件的事而是整个架构层面的重构。方案选型与核心代码改造迁移前我和团队开了个短会讨论了两个方案。方案A是使用Spring Cache注解配合Redis作为后端存储简单粗暴改几行代码就能搞定方案B则是完全手写Redis操作逻辑引入Redlock分布式锁来保证原子性。我选了方案B。虽然方案A开发快但在高并发库存扣减这种对一致性要求极高的场景下Spring Cache默认的异步刷新机制很容易导致竞态条件。我们需要更细粒度的控制特别是在处理“超卖”这个致命Bug时。java// 核心扣减库存逻辑使用Lua脚本保证原子性public boolean deductStock(String itemId, int quantity) {String key stock: itemId;// Lua脚本确保读取和扣减是原子的String script if redis.call(get, KEYS[1]) false then return -1 end local stock tonumber(redis.call(get, KEYS[1])) if stock tonumber(ARGV[1]) then return 0 end redis.call(decrby, KEYS[1], ARGV[1]) return 1;Long result redisTemplate.execute(new DefaultRedisScript(script, Long.class),Collections.singletonList(key), quantity);if (result null || result 0) {return false; // 库存不足}return true;}有意思的是在迁移过程中我发现了一个隐藏很深的性能瓶颈。原本本地缓存时我们为了节省内存把很多不常用的商品详情也塞进了缓存里。切换到Redis后如果不做区分巨大的Key空间会导致Redis内存碎片率飙升。我特意让DBA同学帮忙监控了一下果然碎片率一度达到了40%以上。为了解决这个问题我给缓存加了分层策略。热点商品数据Top 1000 SKU放入Redis Cluster的主节点并且设置较短的TTL5分钟强制高频刷新以换取数据新鲜度。非热点数据则降级到MySQL数据库查询并开启数据库层面的连接池优化。这样既保证了核心链路的极速响应又避免了Redis资源被冷数据挤占。应对雪崩与穿透的实战踩坑迁移完成后的第一次压力测试就给了我一个下马威。模拟大促流量时Redis CPU瞬间飙升至95%随后大量请求超时。当时我有点懵明明QPS才2万怎么会这么卡排查日志后发现原来是“缓存穿透”引发的连锁反应。有些恶意请求或者前端参数错误查询了一些根本不存在的商品ID。在本地缓存时代查不到直接走DB因为并发低没感觉。现在虽然加了Redis但我们在代码层面没有做空值缓存导致每次查询不存在的数据都直接打到MySQL瞬间打穿了数据库连接池。坑死了这种低级错误居然发生在生产环境重构时。我立刻调整了策略引入了布隆过滤器Bloom Filter作为第一道防线拦截非法的Key请求。同时对于查询结果为null的情况我也写入Redis但设置一个极短的过期时间比如30秒防止短时间内重复查询相同的不存在的数据。java// 空值缓存处理if (result null) {// 设置很短的过期时间防止缓存穿透redisTemplate.opsForValue().set(key, , 30, TimeUnit.SECONDS);return null;}另外关于“缓存雪崩”的问题我也做了针对性优化。之前所有缓存的过期时间都是硬编码的5分钟一旦这批缓存同时失效所有流量会瞬间涌向数据库。这次我把过期时间改成了“固定时间 随机抖动”。比如基础TTL是300秒我在此基础上加上0-60秒的随机值。这样可以让缓存失效的时间分散开避免同一时刻大量Key过期。说实话这次迁移花了我整整两周时间除了改代码大部分精力都花在调优Redis配置和监控报警规则上。客户最后跑完大促库存准确率100%系统零宕机算是圆满交差。回顾整个过程从本地缓存到分布式Redis的迁移看似只是技术栈的升级实则是对系统一致性和可用性的一次深刻反思。别小看那几个配置项在生产环境里它们就是决定系统是稳如泰山还是瞬间崩塌的关键。本文基于实际项目经验整理如有技术问题欢迎私信交流。