高并发数据访问场景下的并发控制与缓存优化(缓存优化)

发布时间:2026/8/21 11:34:52
高并发数据访问场景下的并发控制与缓存优化(缓存优化) 一、缓存分层体系标准三层架构高并发必备1. L1 本地进程缓存Caffeine替代 Guava Cache核心定位离应用最近无网络 IO单机百万 QPS拦截 90% 常规读请求大幅减轻 Redis 压力。适用数据极少变更基础静态数据商品基础信息、字典、配置、标签短期热点数据允许 1~3s 数据不一致Caffeine 核心优势基于 Window TinyLFU 淘汰算法比 LRU 命中率高 30%支持同步 / 异步加载、自动过期、写入刷新、最大容量限制无锁设计并发读写性能远超 Guava基础配置示例// 初始化本地缓存最大10万条写入后5秒过期 LoadingCacheLong, GoodsDTO localCache Caffeine.newBuilder() .maximumSize(100000) .expireAfterWrite(5, TimeUnit.SECONDS) .recordStats() // 统计命中率、失效、加载耗时 .build(id - loadFromRedis(id));短板单机隔离多实例数据不一致重启丢失不适合强一致场景内存占用可控。2. L2 分布式缓存Redis Cluster核心存储层定位跨实例共享数据支撑集群全局缓存承载所有热点查询统一数据视图。适用所有需要多服务共享的数据库存、用户会话、订单快照、活动数据、排行榜集群架构选型Redis Cluster分片集群无中心水平扩容生产主流主从 哨兵读多写少、数据量小场景扩容受限读写分离优化主节点负责写入从节点分担读请求降低主节点 CPU / 网卡负载。3. L3 数据库最终兜底仅缓存全部失效、穿透、熔断降级时访问正常流量下 DB 请求量极低。完整读取流程客户端请求 → 查 L1 本地缓存 命中 → 直接返回 未命中 → 查询 L2 Redis Redis 命中 → 回填本地缓存返回数据 Redis 未命中 → 查询 DB → 回填 Redis 本地缓存 → 返回二、缓存三大致命问题根因 全套解决方案2.1 缓存穿透查询不存在的数据请求直达 DB根因恶意请求传入非法 ID负数、超大 ID、随机乱 ID业务空数据未缓存每次查询都访问数据库爬虫批量遍历不存在主键压垮 DB解决方案按推荐优先级排序方案 1布隆过滤器最优拦截无效 ID原理预加载所有有效业务主键到过滤器查询前先判断过滤器判定不存在直接返回空不访问 Redis/DB。实现RedissonBloomFilter、Google Guava 布隆过滤器生产落地项目启动 / 定时任务全量同步有效商品 ID、用户 ID 到过滤器新增数据同步写入布隆过滤器过滤器存在误判率可控调整容量降低误判缺点数据删除困难适合只增不减数据重启需重新加载。方案 2缓存空值 / 占位符查询 DB 无数据时存入 Redis 特殊标识如、NULL_PLACEHOLDER设置短过期时间3~10s短期内拦截重复穿透。// DB无数据处理 if(goods null){ redisTemplate.opsForValue().set(goods:10086, NULL, 5, TimeUnit.SECONDS); return null; }缺点恶意随机 ID 会产生大量无效 key占用 Redis 内存。方案 3参数校验 网关限流网关层拦截非法参数ID 小于 0、超长字符串、特殊符号对单 IP 高频空查询进行限流。2.2 缓存击穿热点 Key 过期瞬间并发流量全部打 DB根因爆款商品、首页活动等百万 QPS 热点 Key 统一过期过期瞬间大量请求绕过缓存访问数据库。4 套落地方案方案 1逻辑永不过期首选无锁无等待不设置 Redis 过期时间key 永久存在value 中增加expireTime逻辑过期字段请求时先判断逻辑时间未过期直接返回过期则异步线程刷新缓存当前请求直接返回旧数据 优势无锁、无阻塞、吞吐量最高适合允许短暂旧数据的读多写少场景。方案 2互斥锁强实时场景保证数据最新热点 key 过期后仅允许 1 个线程查询 DB 更新缓存其余线程等待重试。 使用 Redisson 分布式锁实现避免大量并发击穿 DB。 伪流程查询 Redis发现逻辑过期尝试获取刷新锁获取成功查 DB 更新缓存释放锁获取失败短暂 sleep 后重试读取缓存方案 3定时主动预热刷新后台定时任务如每 30 分钟主动刷新全量热点 Key从根源避免 key 过期真空期。 适合活动商品、首页固定资源。方案 4过期时间随机偏移基础过期时间 随机浮动值如基础 30min随机 ±300s避免大量 key 同一时刻失效仅作为辅助方案无法解决单热点 key 击穿。2.3 缓存雪崩大批量 key 同时过期DB 瞬间流量洪峰根因批量缓存设置相同过期时间零点 / 整点大批量失效Redis 集群宕机、主从切换、节点故障整体缓存不可用分两类问题分开解决第一类批量 key 同时过期雪崩过期时间随机偏移expire 30*60 new Random().nextInt(300)分层过期基础配置 12h商品数据 30min活动数据 10min错开生命周期逻辑永不过期策略替代物理过期第二类Redis 整体宕机雪崩高可用兜底Redis Cluster 集群 主从复制每个 slot 至少一主一从故障自动切换多级本地缓存 L1 兜底Redis 挂掉后流量全部走本地缓存保护 DB熔断降级Sentinel 监控 Redis 异常触发降级返回静态兜底数据限流网关 / 应用层限制 DB 最大并发查询数防止压垮数据库三、缓存更新一致性策略读写并发脏数据根治生产环境 99% 业务使用Cache Aside 旁路缓存分为读流程、写流程。3.1 标准读流程查询缓存命中直接返回未命中 → 查询 DB将 DB 数据写入 Redis 缓存返回结果3.2 写流程 3 种方案对比方案 1先更新 DB再删除缓存基础版大部分场景够用流程更新数据库 → 删除对应 Redis 缓存 问题极低概率读写并发脏数据 场景线程 A 更新 DB未删缓存线程 B 此时读缓存拿到旧数据写入缓存导致缓存长期脏数据。方案 2延迟双删工业级解决并发脏读推荐完整流程更新数据库立即删除 Redis 缓存延迟 1~3s 再次删除缓存通过线程池 / MQ 异步执行 原理延迟删除覆盖并发读写入旧缓存的窗口彻底解决脏数据。 延迟时间设置大于业务单次查询 DB 回填缓存最大耗时。方案 3先删缓存再更新 DB不推荐并发场景极易出现脏数据禁止使用。3.3 强一致性场景补充金融、订单账务不建议依赖缓存做数据计算缓存仅作查询加速核心计算逻辑走 DB 事务。 若要求缓存与 DB 实时一致使用 Canal 监听数据库 binlog自动同步更新缓存解耦业务代码。其他缓存模式极少使用Read/Write Through读写全部经过缓存缓存同步写 DB代码侵入高性能差Write Back 写回缓存异步刷 DB数据丢失风险高仅大数据离线场景使用四、热点 Key 大 Key 深度优化超高并发核心瓶颈4.1 热点 Key 问题单 key 承受数万百万 QPS绑定 Redis 单一节点CPU / 网卡打满集群其他节点空闲。优化方案缓存副本分片最优同一个热点商品拆分多份缓存 keygoods:100_0、goods:100_1、goods:100_2查询时随机选取其中一个 key 读取分摊流量到不同 slot 节点。 更新时同步删除所有副本 key。L1 本地缓存拦截 热点数据存入 Caffeine 本地缓存90% 流量不经过 Redis从源头减少 Redis 压力。Redis 集群热点 slot 迁移 手动迁移热点 slot 到多台物理机器分散节点压力。4.2 大 Key 优化value 超过 10KB 即为大 key危害网络传输耗时高单次 rt 暴涨删除大 key 阻塞 Redis 主线程造成服务卡顿集群迁移 slot 超时失败拆分方案对象 Hash 拆分商品详情大 JSON 拆分为 hash 结构hget 单个字段不用一次性读取全部数据列表分页拆分长 list 拆分多个短 list分页存储压缩序列化JSON 使用 Snappy 压缩后存入 Redis降低体积异步分片删除不直接 del 大 key使用 hdel/lpop 分批删除不阻塞主线程