Redis 核心教学: 数据结构, 缓存设计, 分布式锁

Redis 核心教学

说明:默认读者已经掌握 MQ、MySQL;重点聚焦工程落地、并发问题、选型边界;通用抽象描述 + 业务举例,穿插 mermaid、Java 代码示例;实验环境:JDK8,Spring‑Boot,spring‑data‑redis / Redisson。

1 Redis|数据结构

Redis 的业务落地本质就是选对数据结构,不纠结底层实现,聚焦场景匹配。

数据结构 核心特性 典型业务场景
String 字符串,支持增减计数,二进制安全 计数器、库存、Token、普通KV缓存
Hash key‑field‑value,支持局部更新 对象缓存:用户信息、商品属性
List 双向链表,有序可重复 简单队列、时序列表
Set 无序,元素唯一,集合运算 去重、黑名单、交集计算
ZSet 元素唯一,score分数排序 排行榜、权重排序
Bitmap 按bit位存储,极大节省内存 用户签到、状态标记、布尔批量标记
HyperLogLog 基数统计,低内存,有误差 页面UV、大数据量去重统计
Geo 经纬度存储,距离计算 附近门店、地理位置检索
Stream 持久化消息队列,支持消费组 Redis实现消息队列,消息回溯

1.1 String 示例

java 复制代码
// String:库存计数
redisTemplate.opsForValue().set("coupon:stock:1001", "1000");
// 原子递增递减
Long stock = redisTemplate.opsForValue().decrement("coupon:stock:1001");

1.2 Hash 示例,局部更新对象,不要序列化大对象

java 复制代码
// Hash 存储用户信息
String userKey = "user:info:10086";
redisTemplate.opsForHash().put(userKey,"name","zhangsan");
redisTemplate.opsForHash().put(userKey,"age","22");
// 只更新age字段,不需要更新整个对象
redisTemplate.opsForHash().put(userKey,"age","23");
Object age = redisTemplate.opsForHash().get(userKey,"age");

1.3 List 示例,简单时序队列

java 复制代码
// 左进右出模拟队列
redisTemplate.opsForList().leftPush("msg:queue","msg1");
String msg = redisTemplate.opsForList().rightPop("msg:queue");

1.4 Set 示例,黑名单、集合去重

java 复制代码
redisTemplate.opsForSet().add("black:uid","1001","1002","1003");
Boolean isBlack = redisTemplate.opsForSet().isMember("black:uid","1001");

1.5 ZSet 排行榜示例

java 复制代码
// score 分数,member 用户ID
redisTemplate.opsForZSet().add("rank:score","uid:1001",950);
redisTemplate.opsForZSet().add("rank:score","uid:1002",880);
// 降序分页 top10
Set<Object> top10 = redisTemplate.opsForZSet().reverseRange("rank:score",0,9);

1.6 Bitmap 位图

原理:把每一个用户映射到一个 bit 位,bit=1代表签到,bit=0未签到;1亿用户仅占用约12MB内存。

java 复制代码
// key:sign:20260916  offset 用户id
// 用户1000签到
redisTemplate.opsForValue().setBit("sign:20260916",1000,true);
// 判断用户是否签到
Boolean signFlag = redisTemplate.opsForValue().getBit("sign:20260916",1000);
// 统计当日签到总人数
Long signCount = redisTemplate.execute((RedisCallback<Long>) con -> con.bitCount("sign:20260916".getBytes(StandardCharsets.UTF_8)));

适用:签到、开关状态;局限:用户ID不能离散过大,会造成大量空bit占用。

1.7 HyperLogLog 基数统计

统计不重复元素数量,不存储原始数据,存在0.81%标准误差;内存极小,百万UV只占几KB;不能取出具体元素,只能得到去重数量。

java 复制代码
// 访问页面,添加访问用户id
redisTemplate.opsForHyperLogLog().add("uv:page:home","uid:1001","uid:1002","uid:1001");
// 获取UV,自动去重
Long uv = redisTemplate.opsForHyperLogLog().size("uv:page:home");

坑点:只适合统计数量;不能获取明细;允许误差才使用。

1.8 Geo 地理位置

存储经纬度,计算两点距离,查询附近点位。

java 复制代码
// 添加门店经纬度:经度,纬度,member
redisTemplate.opsForGeo().add("store:geo",new Point(122.27,30.05),"store_001");
redisTemplate.opsForGeo().add("store:geo",new Point(122.28,30.06),"store_002");
// 查询两店距离
Distance distance = redisTemplate.opsForGeo().distance("store:geo","store_001","store_002",Metrics.KILOMETERS);
// 获取附近5km门店
Circle circle = new Circle(new Point(122.27,30.05),5,Metrics.KILOMETERS);
GeoResults<GeoLocation<Object>> nearList = redisTemplate.opsForGeo().radius("store:geo",circle);

1.9 Stream Redis内置消息队列

持久化消息,支持消费组、消息ACK、消息回溯;弥补List丢失消息问题;替代部分MQ轻量场景。

java 复制代码
// 发送消息
redis.xadd("stream:order", Map.of("orderId", "O10001"));

// 创建消费组(只需一次)
redis.xgroupCreate("stream:order", "g1", "0");

// 消费组读取消息
List<Map<String, String>> msgs = redis.xreadGroup(
    "g1", "consumer-1",
    Map.of("stream:order", ">")  // ">" 表示只读新消息
);

for (Map<String, String> msg : msgs) {
    handle(msg);  // 业务处理

    // 处理成功才 ACK,消息不会丢
    redis.xack("stream:order", "g1", msg.get("id"));
}

// 不 ACK:留在 pending,可重试
// xreadGroup 传 "0" 而非 ">":从头读,可回溯

选型边界:简单业务可用;可靠性、事务、死信队列不如专业MQ(RocketMQ/Kafka)。
落地小提示

  1. 对象缓存优先 Hash,不要把整个对象序列化塞进 String,局部更新代价很高。
  2. 排行榜一定用 ZSet,不要在业务代码内存排序。
  3. 计数、库存优先 String。
  4. Bitmap/HyperLogLog 是空间换性能,接受业务本身限制再使用。

2 Redis|缓存架构:读缓存、写缓存、读写缓存(搭配 MQ + MySQL)

整体架构思想:Redis承接高并发,MySQL持久化落数据,MQ做异步削峰解耦

2.1 读缓存模式(读多写少场景)

抽象流程:请求先访问缓存;命中直接返回;未命中查数据库,回写缓存再返回。目的减轻数据库读压力。
#mermaid-svg-rngSpzUGaCHob8sT{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-rngSpzUGaCHob8sT .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-rngSpzUGaCHob8sT .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-rngSpzUGaCHob8sT .error-icon{fill:#552222;}#mermaid-svg-rngSpzUGaCHob8sT .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-rngSpzUGaCHob8sT .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-rngSpzUGaCHob8sT .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-rngSpzUGaCHob8sT .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-rngSpzUGaCHob8sT .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-rngSpzUGaCHob8sT .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-rngSpzUGaCHob8sT .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-rngSpzUGaCHob8sT .marker{fill:#333333;stroke:#333333;}#mermaid-svg-rngSpzUGaCHob8sT .marker.cross{stroke:#333333;}#mermaid-svg-rngSpzUGaCHob8sT svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-rngSpzUGaCHob8sT p{margin:0;}#mermaid-svg-rngSpzUGaCHob8sT .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-rngSpzUGaCHob8sT .cluster-label text{fill:#333;}#mermaid-svg-rngSpzUGaCHob8sT .cluster-label span{color:#333;}#mermaid-svg-rngSpzUGaCHob8sT .cluster-label span p{background-color:transparent;}#mermaid-svg-rngSpzUGaCHob8sT .label text,#mermaid-svg-rngSpzUGaCHob8sT span{fill:#333;color:#333;}#mermaid-svg-rngSpzUGaCHob8sT .node rect,#mermaid-svg-rngSpzUGaCHob8sT .node circle,#mermaid-svg-rngSpzUGaCHob8sT .node ellipse,#mermaid-svg-rngSpzUGaCHob8sT .node polygon,#mermaid-svg-rngSpzUGaCHob8sT .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-rngSpzUGaCHob8sT .rough-node .label text,#mermaid-svg-rngSpzUGaCHob8sT .node .label text,#mermaid-svg-rngSpzUGaCHob8sT .image-shape .label,#mermaid-svg-rngSpzUGaCHob8sT .icon-shape .label{text-anchor:middle;}#mermaid-svg-rngSpzUGaCHob8sT .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-rngSpzUGaCHob8sT .rough-node .label,#mermaid-svg-rngSpzUGaCHob8sT .node .label,#mermaid-svg-rngSpzUGaCHob8sT .image-shape .label,#mermaid-svg-rngSpzUGaCHob8sT .icon-shape .label{text-align:center;}#mermaid-svg-rngSpzUGaCHob8sT .node.clickable{cursor:pointer;}#mermaid-svg-rngSpzUGaCHob8sT .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-rngSpzUGaCHob8sT .arrowheadPath{fill:#333333;}#mermaid-svg-rngSpzUGaCHob8sT .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-rngSpzUGaCHob8sT .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-rngSpzUGaCHob8sT .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-rngSpzUGaCHob8sT .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-rngSpzUGaCHob8sT .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-rngSpzUGaCHob8sT .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-rngSpzUGaCHob8sT .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-rngSpzUGaCHob8sT .cluster text{fill:#333;}#mermaid-svg-rngSpzUGaCHob8sT .cluster span{color:#333;}#mermaid-svg-rngSpzUGaCHob8sT div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-rngSpzUGaCHob8sT .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-rngSpzUGaCHob8sT rect.text{fill:none;stroke-width:0;}#mermaid-svg-rngSpzUGaCHob8sT .icon-shape,#mermaid-svg-rngSpzUGaCHob8sT .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-rngSpzUGaCHob8sT .icon-shape p,#mermaid-svg-rngSpzUGaCHob8sT .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-rngSpzUGaCHob8sT .icon-shape .label rect,#mermaid-svg-rngSpzUGaCHob8sT .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-rngSpzUGaCHob8sT .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-rngSpzUGaCHob8sT .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-rngSpzUGaCHob8sT :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 命中
未命中
业务请求
Redis缓存命中?
直接返回
查询MySQL
结果回写Redis
返回数据

Java伪代码

java 复制代码
public ProductDTO getProduct(Long productId) {
    String key = "product:" + productId;
    String cache = redisTemplate.opsForValue().get(key);

    if (NULL_PLACEHOLDER.equals(cache)) return null;
    if (cache != null) return JSON.parseObject(cache, ProductDTO.class);

    // 只让一个线程去查DB
    String lockKey = "lock:product:" + productId;
    Boolean locked = redisTemplate.opsForValue()
            .setIfAbsent(lockKey, "1", Duration.ofSeconds(10));

    if (Boolean.TRUE.equals(locked)) {
        try {
            // 再查一次缓存,防止重复查(双重检查)
            cache = redisTemplate.opsForValue().get(key);
            if (cache != null) {
                return NULL_PLACEHOLDER.equals(cache) ? null
                        : JSON.parseObject(cache, ProductDTO.class);
            }

            ProductDTO dbData = productMapper.selectById(productId);
            if (dbData == null) {
                redisTemplate.opsForValue().set(key, NULL_PLACEHOLDER, Duration.ofMinutes(2));
                return null;
            }
            redisTemplate.opsForValue().set(key, JSON.toJSONString(dbData), Duration.ofMinutes(30));
            return dbData;
        } finally {
            redisTemplate.delete(lockKey);
        }
    } else {
        // 没抢到锁,等一会再读缓存(或自旋)
        try { Thread.sleep(50); } catch (InterruptedException e) { }
        return getProduct(productId);  // 重试
    }
}

业务举例:商品详情、活动配置查询。

2.2 写缓存模式(高并发写,库存扣减类)

抽象流程:Redis做预扣减与流量拦截;库存充足发送消息到 MQ;MQ消费者真正操作 MySQL;库存不足直接拒绝请求。
核心:高并发写请求不直接打 MySQL,Redis拦截大部分流量。
#mermaid-svg-BhOj88ptFeEONQMU{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-BhOj88ptFeEONQMU .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-BhOj88ptFeEONQMU .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-BhOj88ptFeEONQMU .error-icon{fill:#552222;}#mermaid-svg-BhOj88ptFeEONQMU .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-BhOj88ptFeEONQMU .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-BhOj88ptFeEONQMU .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-BhOj88ptFeEONQMU .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-BhOj88ptFeEONQMU .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-BhOj88ptFeEONQMU .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-BhOj88ptFeEONQMU .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-BhOj88ptFeEONQMU .marker{fill:#333333;stroke:#333333;}#mermaid-svg-BhOj88ptFeEONQMU .marker.cross{stroke:#333333;}#mermaid-svg-BhOj88ptFeEONQMU svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-BhOj88ptFeEONQMU p{margin:0;}#mermaid-svg-BhOj88ptFeEONQMU .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-BhOj88ptFeEONQMU .cluster-label text{fill:#333;}#mermaid-svg-BhOj88ptFeEONQMU .cluster-label span{color:#333;}#mermaid-svg-BhOj88ptFeEONQMU .cluster-label span p{background-color:transparent;}#mermaid-svg-BhOj88ptFeEONQMU .label text,#mermaid-svg-BhOj88ptFeEONQMU span{fill:#333;color:#333;}#mermaid-svg-BhOj88ptFeEONQMU .node rect,#mermaid-svg-BhOj88ptFeEONQMU .node circle,#mermaid-svg-BhOj88ptFeEONQMU .node ellipse,#mermaid-svg-BhOj88ptFeEONQMU .node polygon,#mermaid-svg-BhOj88ptFeEONQMU .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-BhOj88ptFeEONQMU .rough-node .label text,#mermaid-svg-BhOj88ptFeEONQMU .node .label text,#mermaid-svg-BhOj88ptFeEONQMU .image-shape .label,#mermaid-svg-BhOj88ptFeEONQMU .icon-shape .label{text-anchor:middle;}#mermaid-svg-BhOj88ptFeEONQMU .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-BhOj88ptFeEONQMU .rough-node .label,#mermaid-svg-BhOj88ptFeEONQMU .node .label,#mermaid-svg-BhOj88ptFeEONQMU .image-shape .label,#mermaid-svg-BhOj88ptFeEONQMU .icon-shape .label{text-align:center;}#mermaid-svg-BhOj88ptFeEONQMU .node.clickable{cursor:pointer;}#mermaid-svg-BhOj88ptFeEONQMU .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-BhOj88ptFeEONQMU .arrowheadPath{fill:#333333;}#mermaid-svg-BhOj88ptFeEONQMU .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-BhOj88ptFeEONQMU .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-BhOj88ptFeEONQMU .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-BhOj88ptFeEONQMU .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-BhOj88ptFeEONQMU .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-BhOj88ptFeEONQMU .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-BhOj88ptFeEONQMU .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-BhOj88ptFeEONQMU .cluster text{fill:#333;}#mermaid-svg-BhOj88ptFeEONQMU .cluster span{color:#333;}#mermaid-svg-BhOj88ptFeEONQMU div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-BhOj88ptFeEONQMU .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-BhOj88ptFeEONQMU rect.text{fill:none;stroke-width:0;}#mermaid-svg-BhOj88ptFeEONQMU .icon-shape,#mermaid-svg-BhOj88ptFeEONQMU .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-BhOj88ptFeEONQMU .icon-shape p,#mermaid-svg-BhOj88ptFeEONQMU .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-BhOj88ptFeEONQMU .icon-shape .label rect,#mermaid-svg-BhOj88ptFeEONQMU .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-BhOj88ptFeEONQMU .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-BhOj88ptFeEONQMU .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-BhOj88ptFeEONQMU :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 库存充足
库存不足
并发写请求
Redis校验+预扣库存
发送消息至MQ
返回失败
MQ消费
MySQL执行真实扣减落库

Java伪代码

java 复制代码
private static final DefaultRedisScript<Long> RECEIVE_SCRIPT =
    new DefaultRedisScript<>(
        "local stock = tonumber(redis.call('get', KEYS[1]))\n" +
        "if not stock or stock <= 0 then return 0 end\n" +
        "if redis.call('sismember', KEYS[2], ARGV[1]) == 1 then return -1 end\n" + // 已领过
        "redis.call('decr', KEYS[1])\n" +
        "redis.call('sadd', KEYS[2], ARGV[1])\n" +
        "return 1",
        Long.class);

public boolean receiveCoupon(Long couponId, Long userId) {
    String stockKey = "coupon:stock:" + couponId;
    String userKey  = "coupon:users:" + couponId;

    Long r = redisTemplate.execute(RECEIVE_SCRIPT,
            Arrays.asList(stockKey, userKey), userId.toString());

    if (r == null || r == 0) return false;   // 无库存
    if (r == -1) return false;               // 已领过

    // 先落本地消息表(状态=待发送),再发MQ
    try {
        mqProducer.send("coupon_consume_topic", couponId, userId);
        // 标记消息已发送
    } catch (Exception e) {
        redisTemplate.opsForValue().increment(stockKey);        // 补库存
        redisTemplate.opsForSet().remove(userKey, userId);      // 补去重
        throw e;
    }
    return true;
}

业务举例:大促领券、秒杀库存扣减。

2.3 读写缓存模式(排行榜类)

抽象流程:业务产生分数变更,通过 MQ 异步更新 Redis ZSet;查询直接读 ZSet;定时任务把排行榜数据同步 MySQL 持久化备份。
#mermaid-svg-3ITlU1gHTx80vbDK{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-3ITlU1gHTx80vbDK .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-3ITlU1gHTx80vbDK .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-3ITlU1gHTx80vbDK .error-icon{fill:#552222;}#mermaid-svg-3ITlU1gHTx80vbDK .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-3ITlU1gHTx80vbDK .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-3ITlU1gHTx80vbDK .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-3ITlU1gHTx80vbDK .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-3ITlU1gHTx80vbDK .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-3ITlU1gHTx80vbDK .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-3ITlU1gHTx80vbDK .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-3ITlU1gHTx80vbDK .marker{fill:#333333;stroke:#333333;}#mermaid-svg-3ITlU1gHTx80vbDK .marker.cross{stroke:#333333;}#mermaid-svg-3ITlU1gHTx80vbDK svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-3ITlU1gHTx80vbDK p{margin:0;}#mermaid-svg-3ITlU1gHTx80vbDK .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-3ITlU1gHTx80vbDK .cluster-label text{fill:#333;}#mermaid-svg-3ITlU1gHTx80vbDK .cluster-label span{color:#333;}#mermaid-svg-3ITlU1gHTx80vbDK .cluster-label span p{background-color:transparent;}#mermaid-svg-3ITlU1gHTx80vbDK .label text,#mermaid-svg-3ITlU1gHTx80vbDK span{fill:#333;color:#333;}#mermaid-svg-3ITlU1gHTx80vbDK .node rect,#mermaid-svg-3ITlU1gHTx80vbDK .node circle,#mermaid-svg-3ITlU1gHTx80vbDK .node ellipse,#mermaid-svg-3ITlU1gHTx80vbDK .node polygon,#mermaid-svg-3ITlU1gHTx80vbDK .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-3ITlU1gHTx80vbDK .rough-node .label text,#mermaid-svg-3ITlU1gHTx80vbDK .node .label text,#mermaid-svg-3ITlU1gHTx80vbDK .image-shape .label,#mermaid-svg-3ITlU1gHTx80vbDK .icon-shape .label{text-anchor:middle;}#mermaid-svg-3ITlU1gHTx80vbDK .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-3ITlU1gHTx80vbDK .rough-node .label,#mermaid-svg-3ITlU1gHTx80vbDK .node .label,#mermaid-svg-3ITlU1gHTx80vbDK .image-shape .label,#mermaid-svg-3ITlU1gHTx80vbDK .icon-shape .label{text-align:center;}#mermaid-svg-3ITlU1gHTx80vbDK .node.clickable{cursor:pointer;}#mermaid-svg-3ITlU1gHTx80vbDK .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-3ITlU1gHTx80vbDK .arrowheadPath{fill:#333333;}#mermaid-svg-3ITlU1gHTx80vbDK .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-3ITlU1gHTx80vbDK .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-3ITlU1gHTx80vbDK .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-3ITlU1gHTx80vbDK .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-3ITlU1gHTx80vbDK .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-3ITlU1gHTx80vbDK .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-3ITlU1gHTx80vbDK .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-3ITlU1gHTx80vbDK .cluster text{fill:#333;}#mermaid-svg-3ITlU1gHTx80vbDK .cluster span{color:#333;}#mermaid-svg-3ITlU1gHTx80vbDK div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-3ITlU1gHTx80vbDK .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-3ITlU1gHTx80vbDK rect.text{fill:none;stroke-width:0;}#mermaid-svg-3ITlU1gHTx80vbDK .icon-shape,#mermaid-svg-3ITlU1gHTx80vbDK .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-3ITlU1gHTx80vbDK .icon-shape p,#mermaid-svg-3ITlU1gHTx80vbDK .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-3ITlU1gHTx80vbDK .icon-shape .label rect,#mermaid-svg-3ITlU1gHTx80vbDK .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-3ITlU1gHTx80vbDK .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-3ITlU1gHTx80vbDK .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-3ITlU1gHTx80vbDK :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 业务数据发生变更
MQ异步更新ZSet分值
用户查询排行榜
读取Redis ZSet分页返回
定时任务同步至MySQL备份

Java伪代码

java 复制代码
// 发送:传增量分数
public void scoreChange(Long userId, Integer delta) {
    Map<String, Object> msg = Map.of(
        "msgId", UUID.randomUUID().toString(),   // 幂等用
        "userId", userId,
        "delta", delta
    );
    mqProducer.send("score_update_topic", JSON.toJSONString(msg));
}

// 消费端
public void handleScoreMsg(String msg) {
    if (msg == null) return;
    ScoreDTO dto = JSON.parseObject(msg, ScoreDTO.class);
    if (dto == null || dto.getUserId() == null || dto.getDelta() == null) return;

    // 1. 幂等:已处理过直接返回
    if (!markProcessed(dto.getMsgId())) return;

    // 2. 落库(真实来源)
    userScoreMapper.addScore(dto.getUserId(), dto.getDelta());

    // 3. 更新 Redis 排行榜(累加,不是覆盖)
    redisTemplate.opsForZSet()
        .incrementScore("user:rank", dto.getUserId().toString(), dto.getDelta());
}

// 查询:带分数
public Set<TypedTuple<Object>> getTopRank(int topN) {
    if (topN <= 0) return Collections.emptySet();
    return redisTemplate.opsForZSet()
        .reverseRangeWithScores("user:rank", 0, topN - 1);
}

模式选型小结

模式 适用场景 核心收益
读缓存 读多写少静态数据 分担数据库读压力
写缓存+MQ异步落库 高并发写:库存、秒杀 削峰,保护数据库
读写缓存(ZSet) 排行榜、积分排名 高并发有序分页查询

3 Redis|Lua脚本:保证原子性

Redis 单条命令原子;多条命令组合不是原子。Java代码做"判断+执行"两步操作,并发下会出现超卖、数据错乱。

Lua脚本:把多条Redis命令封装,Redis单线程完整执行脚本,脚本内命令原子执行,中间不会插入其他客户端命令;同时减少网络往返。
#mermaid-svg-1Yu5ytAnrGlcUjMy{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-1Yu5ytAnrGlcUjMy .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-1Yu5ytAnrGlcUjMy .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-1Yu5ytAnrGlcUjMy .error-icon{fill:#552222;}#mermaid-svg-1Yu5ytAnrGlcUjMy .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-1Yu5ytAnrGlcUjMy .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-1Yu5ytAnrGlcUjMy .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-1Yu5ytAnrGlcUjMy .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-1Yu5ytAnrGlcUjMy .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-1Yu5ytAnrGlcUjMy .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-1Yu5ytAnrGlcUjMy .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-1Yu5ytAnrGlcUjMy .marker{fill:#333333;stroke:#333333;}#mermaid-svg-1Yu5ytAnrGlcUjMy .marker.cross{stroke:#333333;}#mermaid-svg-1Yu5ytAnrGlcUjMy svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-1Yu5ytAnrGlcUjMy p{margin:0;}#mermaid-svg-1Yu5ytAnrGlcUjMy .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-1Yu5ytAnrGlcUjMy .cluster-label text{fill:#333;}#mermaid-svg-1Yu5ytAnrGlcUjMy .cluster-label span{color:#333;}#mermaid-svg-1Yu5ytAnrGlcUjMy .cluster-label span p{background-color:transparent;}#mermaid-svg-1Yu5ytAnrGlcUjMy .label text,#mermaid-svg-1Yu5ytAnrGlcUjMy span{fill:#333;color:#333;}#mermaid-svg-1Yu5ytAnrGlcUjMy .node rect,#mermaid-svg-1Yu5ytAnrGlcUjMy .node circle,#mermaid-svg-1Yu5ytAnrGlcUjMy .node ellipse,#mermaid-svg-1Yu5ytAnrGlcUjMy .node polygon,#mermaid-svg-1Yu5ytAnrGlcUjMy .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-1Yu5ytAnrGlcUjMy .rough-node .label text,#mermaid-svg-1Yu5ytAnrGlcUjMy .node .label text,#mermaid-svg-1Yu5ytAnrGlcUjMy .image-shape .label,#mermaid-svg-1Yu5ytAnrGlcUjMy .icon-shape .label{text-anchor:middle;}#mermaid-svg-1Yu5ytAnrGlcUjMy .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-1Yu5ytAnrGlcUjMy .rough-node .label,#mermaid-svg-1Yu5ytAnrGlcUjMy .node .label,#mermaid-svg-1Yu5ytAnrGlcUjMy .image-shape .label,#mermaid-svg-1Yu5ytAnrGlcUjMy .icon-shape .label{text-align:center;}#mermaid-svg-1Yu5ytAnrGlcUjMy .node.clickable{cursor:pointer;}#mermaid-svg-1Yu5ytAnrGlcUjMy .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-1Yu5ytAnrGlcUjMy .arrowheadPath{fill:#333333;}#mermaid-svg-1Yu5ytAnrGlcUjMy .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-1Yu5ytAnrGlcUjMy .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-1Yu5ytAnrGlcUjMy .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-1Yu5ytAnrGlcUjMy .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-1Yu5ytAnrGlcUjMy .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-1Yu5ytAnrGlcUjMy .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-1Yu5ytAnrGlcUjMy .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-1Yu5ytAnrGlcUjMy .cluster text{fill:#333;}#mermaid-svg-1Yu5ytAnrGlcUjMy .cluster span{color:#333;}#mermaid-svg-1Yu5ytAnrGlcUjMy div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-1Yu5ytAnrGlcUjMy .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-1Yu5ytAnrGlcUjMy rect.text{fill:none;stroke-width:0;}#mermaid-svg-1Yu5ytAnrGlcUjMy .icon-shape,#mermaid-svg-1Yu5ytAnrGlcUjMy .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-1Yu5ytAnrGlcUjMy .icon-shape p,#mermaid-svg-1Yu5ytAnrGlcUjMy .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-1Yu5ytAnrGlcUjMy .icon-shape .label rect,#mermaid-svg-1Yu5ytAnrGlcUjMy .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-1Yu5ytAnrGlcUjMy .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-1Yu5ytAnrGlcUjMy .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-1Yu5ytAnrGlcUjMy :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} Lua脚本(原子)
一次网络请求提交Lua脚本
执行期间不处理其他请求,原子完成
Redis服务端:读取‑判断‑扣减完整执行
Java多命令(非原子)
Java读取stock
中间可被其他线程插队,并发超卖风险
Java执行decr
stock>0?

Lua脚本 stock.lua

lua 复制代码
-- KEYS[1]:库存key ARGV[1]:扣减数量
local stock = tonumber(redis.call('get',KEYS[1]))
local deduct = tonumber(ARGV[1])
if not stock or not deduct or stock < deduct then
    return 0
end
redis.call('decrby',KEYS[1],deduct)
return 1

Java调用Lua脚本

java 复制代码
public boolean deductStockLua(String stockKey,int deductNum){
    String script = "local stock = tonumber(redis.call('get',KEYS[1]))\n" +
            "local deduct = tonumber(ARGV[1])\n" +
            "if not stock or not deduct or stock < deduct then\n" +
            "    return 0\n" +
            "end\n" +
            "redis.call('decrby',KEYS[1],deduct)\n" +
            "return 1";
    DefaultRedisScript<Long> redisScript = new DefaultRedisScript<>(script, Long.class);
    Long res = redisTemplate.execute(redisScript, Collections.singletonList(stockKey), deductNum);
    return res != null && res == 1L;
}

选型边界

  • 只操作Redis,无MySQL参与:优先Lua,性能高于分布式锁;
  • 如果逻辑涉及 Redis + MySQL 跨中间件,Lua无法保证MySQL原子性,需要分布式锁。

4 Redis|缓存问题集:穿透、击穿、雪崩;缓存一致性、延迟双删、缓存预热

4.1 缓存穿透

问题:查询不存在的数据,缓存查不到、数据库也查不到;请求直接打到DB;恶意请求可以打垮数据库。

解决方案:

  1. 空值缓存:不存在的数据缓存短期空key;
  2. 布隆过滤器前置过滤不存在key;
  3. 接口参数校验、黑名单。

#mermaid-svg-ZAvncKfNdDfxPz4W{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-ZAvncKfNdDfxPz4W .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-ZAvncKfNdDfxPz4W .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-ZAvncKfNdDfxPz4W .error-icon{fill:#552222;}#mermaid-svg-ZAvncKfNdDfxPz4W .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-ZAvncKfNdDfxPz4W .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-ZAvncKfNdDfxPz4W .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-ZAvncKfNdDfxPz4W .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-ZAvncKfNdDfxPz4W .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-ZAvncKfNdDfxPz4W .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-ZAvncKfNdDfxPz4W .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-ZAvncKfNdDfxPz4W .marker{fill:#333333;stroke:#333333;}#mermaid-svg-ZAvncKfNdDfxPz4W .marker.cross{stroke:#333333;}#mermaid-svg-ZAvncKfNdDfxPz4W svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-ZAvncKfNdDfxPz4W p{margin:0;}#mermaid-svg-ZAvncKfNdDfxPz4W .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-ZAvncKfNdDfxPz4W .cluster-label text{fill:#333;}#mermaid-svg-ZAvncKfNdDfxPz4W .cluster-label span{color:#333;}#mermaid-svg-ZAvncKfNdDfxPz4W .cluster-label span p{background-color:transparent;}#mermaid-svg-ZAvncKfNdDfxPz4W .label text,#mermaid-svg-ZAvncKfNdDfxPz4W span{fill:#333;color:#333;}#mermaid-svg-ZAvncKfNdDfxPz4W .node rect,#mermaid-svg-ZAvncKfNdDfxPz4W .node circle,#mermaid-svg-ZAvncKfNdDfxPz4W .node ellipse,#mermaid-svg-ZAvncKfNdDfxPz4W .node polygon,#mermaid-svg-ZAvncKfNdDfxPz4W .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-ZAvncKfNdDfxPz4W .rough-node .label text,#mermaid-svg-ZAvncKfNdDfxPz4W .node .label text,#mermaid-svg-ZAvncKfNdDfxPz4W .image-shape .label,#mermaid-svg-ZAvncKfNdDfxPz4W .icon-shape .label{text-anchor:middle;}#mermaid-svg-ZAvncKfNdDfxPz4W .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-ZAvncKfNdDfxPz4W .rough-node .label,#mermaid-svg-ZAvncKfNdDfxPz4W .node .label,#mermaid-svg-ZAvncKfNdDfxPz4W .image-shape .label,#mermaid-svg-ZAvncKfNdDfxPz4W .icon-shape .label{text-align:center;}#mermaid-svg-ZAvncKfNdDfxPz4W .node.clickable{cursor:pointer;}#mermaid-svg-ZAvncKfNdDfxPz4W .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-ZAvncKfNdDfxPz4W .arrowheadPath{fill:#333333;}#mermaid-svg-ZAvncKfNdDfxPz4W .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-ZAvncKfNdDfxPz4W .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-ZAvncKfNdDfxPz4W .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-ZAvncKfNdDfxPz4W .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-ZAvncKfNdDfxPz4W .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-ZAvncKfNdDfxPz4W .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-ZAvncKfNdDfxPz4W .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-ZAvncKfNdDfxPz4W .cluster text{fill:#333;}#mermaid-svg-ZAvncKfNdDfxPz4W .cluster span{color:#333;}#mermaid-svg-ZAvncKfNdDfxPz4W div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-ZAvncKfNdDfxPz4W .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-ZAvncKfNdDfxPz4W rect.text{fill:none;stroke-width:0;}#mermaid-svg-ZAvncKfNdDfxPz4W .icon-shape,#mermaid-svg-ZAvncKfNdDfxPz4W .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-ZAvncKfNdDfxPz4W .icon-shape p,#mermaid-svg-ZAvncKfNdDfxPz4W .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-ZAvncKfNdDfxPz4W .icon-shape .label rect,#mermaid-svg-ZAvncKfNdDfxPz4W .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-ZAvncKfNdDfxPz4W .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-ZAvncKfNdDfxPz4W .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-ZAvncKfNdDfxPz4W :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 查询不存在id
Redis无数据
全部请求打到MySQL

4.2 缓存击穿

问题:热点key过期瞬间,大量并发同时打到数据库。

解决方案:

  1. 热点key设置永不过期;
  2. 互斥锁:仅一个线程更新缓存,其他等待;
  3. 定时任务主动刷新热点key。

#mermaid-svg-W1hal5FGEYC9LIUd{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-W1hal5FGEYC9LIUd .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-W1hal5FGEYC9LIUd .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-W1hal5FGEYC9LIUd .error-icon{fill:#552222;}#mermaid-svg-W1hal5FGEYC9LIUd .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-W1hal5FGEYC9LIUd .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-W1hal5FGEYC9LIUd .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-W1hal5FGEYC9LIUd .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-W1hal5FGEYC9LIUd .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-W1hal5FGEYC9LIUd .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-W1hal5FGEYC9LIUd .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-W1hal5FGEYC9LIUd .marker{fill:#333333;stroke:#333333;}#mermaid-svg-W1hal5FGEYC9LIUd .marker.cross{stroke:#333333;}#mermaid-svg-W1hal5FGEYC9LIUd svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-W1hal5FGEYC9LIUd p{margin:0;}#mermaid-svg-W1hal5FGEYC9LIUd .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-W1hal5FGEYC9LIUd .cluster-label text{fill:#333;}#mermaid-svg-W1hal5FGEYC9LIUd .cluster-label span{color:#333;}#mermaid-svg-W1hal5FGEYC9LIUd .cluster-label span p{background-color:transparent;}#mermaid-svg-W1hal5FGEYC9LIUd .label text,#mermaid-svg-W1hal5FGEYC9LIUd span{fill:#333;color:#333;}#mermaid-svg-W1hal5FGEYC9LIUd .node rect,#mermaid-svg-W1hal5FGEYC9LIUd .node circle,#mermaid-svg-W1hal5FGEYC9LIUd .node ellipse,#mermaid-svg-W1hal5FGEYC9LIUd .node polygon,#mermaid-svg-W1hal5FGEYC9LIUd .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-W1hal5FGEYC9LIUd .rough-node .label text,#mermaid-svg-W1hal5FGEYC9LIUd .node .label text,#mermaid-svg-W1hal5FGEYC9LIUd .image-shape .label,#mermaid-svg-W1hal5FGEYC9LIUd .icon-shape .label{text-anchor:middle;}#mermaid-svg-W1hal5FGEYC9LIUd .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-W1hal5FGEYC9LIUd .rough-node .label,#mermaid-svg-W1hal5FGEYC9LIUd .node .label,#mermaid-svg-W1hal5FGEYC9LIUd .image-shape .label,#mermaid-svg-W1hal5FGEYC9LIUd .icon-shape .label{text-align:center;}#mermaid-svg-W1hal5FGEYC9LIUd .node.clickable{cursor:pointer;}#mermaid-svg-W1hal5FGEYC9LIUd .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-W1hal5FGEYC9LIUd .arrowheadPath{fill:#333333;}#mermaid-svg-W1hal5FGEYC9LIUd .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-W1hal5FGEYC9LIUd .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-W1hal5FGEYC9LIUd .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-W1hal5FGEYC9LIUd .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-W1hal5FGEYC9LIUd .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-W1hal5FGEYC9LIUd .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-W1hal5FGEYC9LIUd .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-W1hal5FGEYC9LIUd .cluster text{fill:#333;}#mermaid-svg-W1hal5FGEYC9LIUd .cluster span{color:#333;}#mermaid-svg-W1hal5FGEYC9LIUd div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-W1hal5FGEYC9LIUd .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-W1hal5FGEYC9LIUd rect.text{fill:none;stroke-width:0;}#mermaid-svg-W1hal5FGEYC9LIUd .icon-shape,#mermaid-svg-W1hal5FGEYC9LIUd .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-W1hal5FGEYC9LIUd .icon-shape p,#mermaid-svg-W1hal5FGEYC9LIUd .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-W1hal5FGEYC9LIUd .icon-shape .label rect,#mermaid-svg-W1hal5FGEYC9LIUd .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-W1hal5FGEYC9LIUd .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-W1hal5FGEYC9LIUd .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-W1hal5FGEYC9LIUd :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 热点key过期
大量并发同时涌入
数据库压力突增

4.3 缓存雪崩

问题:大量key同一时刻过期,或者Redis整体宕机;大量流量全部压向数据库。

解决方案:

  1. TTL增加随机偏移,避免集中过期;
  2. Redis高可用:主从、哨兵、集群;
  3. 服务侧限流熔断降级;
  4. 多级缓存:本地缓存 + Redis。

4.4 缓存与MySQL一致性

主流策略对比

策略 流程 缺点
先更新库,再更新缓存 update mysql → set redis 很多缓存数据没人读,浪费写性能
先更新库,再删除缓存 update mysql → del redis 极端并发会短暂脏数据
延迟双删(最终一致性方案)

抽象流程:

  1. 更新 MySQL;
  2. 立刻删除缓存;
  3. 通过延时MQ消息,延迟一段时间再次删除缓存;解决并发读写造成的脏缓存残留。

#mermaid-svg-3KDk0BduQzoJhLH0{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-3KDk0BduQzoJhLH0 .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-3KDk0BduQzoJhLH0 .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-3KDk0BduQzoJhLH0 .error-icon{fill:#552222;}#mermaid-svg-3KDk0BduQzoJhLH0 .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-3KDk0BduQzoJhLH0 .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-3KDk0BduQzoJhLH0 .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-3KDk0BduQzoJhLH0 .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-3KDk0BduQzoJhLH0 .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-3KDk0BduQzoJhLH0 .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-3KDk0BduQzoJhLH0 .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-3KDk0BduQzoJhLH0 .marker{fill:#333333;stroke:#333333;}#mermaid-svg-3KDk0BduQzoJhLH0 .marker.cross{stroke:#333333;}#mermaid-svg-3KDk0BduQzoJhLH0 svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-3KDk0BduQzoJhLH0 p{margin:0;}#mermaid-svg-3KDk0BduQzoJhLH0 .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-3KDk0BduQzoJhLH0 .cluster-label text{fill:#333;}#mermaid-svg-3KDk0BduQzoJhLH0 .cluster-label span{color:#333;}#mermaid-svg-3KDk0BduQzoJhLH0 .cluster-label span p{background-color:transparent;}#mermaid-svg-3KDk0BduQzoJhLH0 .label text,#mermaid-svg-3KDk0BduQzoJhLH0 span{fill:#333;color:#333;}#mermaid-svg-3KDk0BduQzoJhLH0 .node rect,#mermaid-svg-3KDk0BduQzoJhLH0 .node circle,#mermaid-svg-3KDk0BduQzoJhLH0 .node ellipse,#mermaid-svg-3KDk0BduQzoJhLH0 .node polygon,#mermaid-svg-3KDk0BduQzoJhLH0 .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-3KDk0BduQzoJhLH0 .rough-node .label text,#mermaid-svg-3KDk0BduQzoJhLH0 .node .label text,#mermaid-svg-3KDk0BduQzoJhLH0 .image-shape .label,#mermaid-svg-3KDk0BduQzoJhLH0 .icon-shape .label{text-anchor:middle;}#mermaid-svg-3KDk0BduQzoJhLH0 .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-3KDk0BduQzoJhLH0 .rough-node .label,#mermaid-svg-3KDk0BduQzoJhLH0 .node .label,#mermaid-svg-3KDk0BduQzoJhLH0 .image-shape .label,#mermaid-svg-3KDk0BduQzoJhLH0 .icon-shape .label{text-align:center;}#mermaid-svg-3KDk0BduQzoJhLH0 .node.clickable{cursor:pointer;}#mermaid-svg-3KDk0BduQzoJhLH0 .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-3KDk0BduQzoJhLH0 .arrowheadPath{fill:#333333;}#mermaid-svg-3KDk0BduQzoJhLH0 .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-3KDk0BduQzoJhLH0 .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-3KDk0BduQzoJhLH0 .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-3KDk0BduQzoJhLH0 .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-3KDk0BduQzoJhLH0 .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-3KDk0BduQzoJhLH0 .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-3KDk0BduQzoJhLH0 .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-3KDk0BduQzoJhLH0 .cluster text{fill:#333;}#mermaid-svg-3KDk0BduQzoJhLH0 .cluster span{color:#333;}#mermaid-svg-3KDk0BduQzoJhLH0 div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-3KDk0BduQzoJhLH0 .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-3KDk0BduQzoJhLH0 rect.text{fill:none;stroke-width:0;}#mermaid-svg-3KDk0BduQzoJhLH0 .icon-shape,#mermaid-svg-3KDk0BduQzoJhLH0 .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-3KDk0BduQzoJhLH0 .icon-shape p,#mermaid-svg-3KDk0BduQzoJhLH0 .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-3KDk0BduQzoJhLH0 .icon-shape .label rect,#mermaid-svg-3KDk0BduQzoJhLH0 .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-3KDk0BduQzoJhLH0 .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-3KDk0BduQzoJhLH0 .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-3KDk0BduQzoJhLH0 :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 更新MySQL
立即删除缓存
发送延时MQ消息
延迟到达,再次删除缓存

Java伪代码

java 复制代码
// 更新商品,延迟双删保证最终一致性
public void updateProduct(ProductDTO dto){
    productMapper.updateById(dto);
    String cacheKey = "product:" + dto.getId();
    // 立即删除缓存
    redisTemplate.delete(cacheKey);
    // 发送延时MQ,延迟1s再次删除缓存
    mqProducer.sendDelayMsg("cache_delay_delete_topic",cacheKey,1000);
}
缓存预热

活动上线/系统启动前,提前把热点数据加载进Redis;避免上线瞬间大量请求穿透数据库。适用大促、首页热点。

坑点小结

  • 穿透:查询不存在数据 ;击穿:单个热点key过期 ;雪崩:批量key失效/Redis宕机
  • 优先删缓存,不要频繁更新缓存;普通业务接受最终一致性,用延迟双删。

5 Redis|分布式锁 Redisson

原生 SETNX 锁存在:不可重入、锁被其他线程误删、无自动续期;生产环境直接使用 Redisson。

依赖

xml 复制代码
<dependency>
    <groupId>org.redisson</groupId>
    <artifactId>redisson-spring-boot-starter</artifactId>
    <version>3.23.5</version>
</dependency>

Redisson核心能力

  1. 可重入锁:同一个线程多次加锁不会死锁;底层Hash结构记录锁计数。
  2. 看门狗自动续期:默认锁30s,业务未执行完后台自动续期;防止业务没跑完锁过期释放。
  3. 超时防死锁:实例宕机锁会自动过期释放,杜绝死锁。

#mermaid-svg-sm7iYUUDxGRJjslp{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-sm7iYUUDxGRJjslp .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-sm7iYUUDxGRJjslp .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-sm7iYUUDxGRJjslp .error-icon{fill:#552222;}#mermaid-svg-sm7iYUUDxGRJjslp .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-sm7iYUUDxGRJjslp .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-sm7iYUUDxGRJjslp .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-sm7iYUUDxGRJjslp .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-sm7iYUUDxGRJjslp .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-sm7iYUUDxGRJjslp .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-sm7iYUUDxGRJjslp .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-sm7iYUUDxGRJjslp .marker{fill:#333333;stroke:#333333;}#mermaid-svg-sm7iYUUDxGRJjslp .marker.cross{stroke:#333333;}#mermaid-svg-sm7iYUUDxGRJjslp svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-sm7iYUUDxGRJjslp p{margin:0;}#mermaid-svg-sm7iYUUDxGRJjslp .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-sm7iYUUDxGRJjslp .cluster-label text{fill:#333;}#mermaid-svg-sm7iYUUDxGRJjslp .cluster-label span{color:#333;}#mermaid-svg-sm7iYUUDxGRJjslp .cluster-label span p{background-color:transparent;}#mermaid-svg-sm7iYUUDxGRJjslp .label text,#mermaid-svg-sm7iYUUDxGRJjslp span{fill:#333;color:#333;}#mermaid-svg-sm7iYUUDxGRJjslp .node rect,#mermaid-svg-sm7iYUUDxGRJjslp .node circle,#mermaid-svg-sm7iYUUDxGRJjslp .node ellipse,#mermaid-svg-sm7iYUUDxGRJjslp .node polygon,#mermaid-svg-sm7iYUUDxGRJjslp .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-sm7iYUUDxGRJjslp .rough-node .label text,#mermaid-svg-sm7iYUUDxGRJjslp .node .label text,#mermaid-svg-sm7iYUUDxGRJjslp .image-shape .label,#mermaid-svg-sm7iYUUDxGRJjslp .icon-shape .label{text-anchor:middle;}#mermaid-svg-sm7iYUUDxGRJjslp .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-sm7iYUUDxGRJjslp .rough-node .label,#mermaid-svg-sm7iYUUDxGRJjslp .node .label,#mermaid-svg-sm7iYUUDxGRJjslp .image-shape .label,#mermaid-svg-sm7iYUUDxGRJjslp .icon-shape .label{text-align:center;}#mermaid-svg-sm7iYUUDxGRJjslp .node.clickable{cursor:pointer;}#mermaid-svg-sm7iYUUDxGRJjslp .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-sm7iYUUDxGRJjslp .arrowheadPath{fill:#333333;}#mermaid-svg-sm7iYUUDxGRJjslp .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-sm7iYUUDxGRJjslp .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-sm7iYUUDxGRJjslp .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-sm7iYUUDxGRJjslp .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-sm7iYUUDxGRJjslp .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-sm7iYUUDxGRJjslp .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-sm7iYUUDxGRJjslp .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-sm7iYUUDxGRJjslp .cluster text{fill:#333;}#mermaid-svg-sm7iYUUDxGRJjslp .cluster span{color:#333;}#mermaid-svg-sm7iYUUDxGRJjslp div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-sm7iYUUDxGRJjslp .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-sm7iYUUDxGRJjslp rect.text{fill:none;stroke-width:0;}#mermaid-svg-sm7iYUUDxGRJjslp .icon-shape,#mermaid-svg-sm7iYUUDxGRJjslp .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-sm7iYUUDxGRJjslp .icon-shape p,#mermaid-svg-sm7iYUUDxGRJjslp .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-sm7iYUUDxGRJjslp .icon-shape .label rect,#mermaid-svg-sm7iYUUDxGRJjslp .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-sm7iYUUDxGRJjslp .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-sm7iYUUDxGRJjslp .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-sm7iYUUDxGRJjslp :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 获取Redisson锁
执行业务逻辑
看门狗后台线程自动续期
finally块释放锁
服务宕机,锁到期自动释放

Java示例

java 复制代码
public void createOrder(Long orderId){
    String lockKey = "lock:order:" + orderId;
    RLock rLock = redissonClient.getLock(lockKey);
    try {
        // 获取锁,不设置leaseTime,开启看门狗续期
        boolean getLock = rLock.tryLock(10, TimeUnit.SECONDS);
        if(getLock){
            // 业务逻辑:操作MySQL+Redis跨中间件
        } else {
            throw new RuntimeException("获取锁失败");
        }
    } finally {
        // 判断当前线程持有锁才释放
        if(rLock.isHeldByCurrentThread()){
            rLock.unlock();
        }
    }
}

⚠️ Lua脚本 vs Redisson锁 选型边界(重点)

  1. 逻辑只操作Redis,不操作MySQL:优先Lua脚本,性能更高。>

示例:Redis内库存预扣、计数校验、防重。

  1. 跨中间件:Redis + MySQL,复杂业务、长事务:必须用Redisson分布式锁。>

示例:订单创建、资金、状态流转。

使用规范

  • 锁释放写在finally块,防止异常不释放;
  • 尽量不手动指定锁过期时间,交给看门狗;
  • 高并发场景按需使用读写锁、公平锁。

整体总结

  1. Redis业务落地第一步:选对数据结构;String计数、Hash存对象、ZSet排行;Bitmap签到、HyperLogLog UV统计、Geo地理位置、Stream轻量消息队列。
  2. 缓存三大范式:读缓存分担读压力;写缓存配合MQ异步落库扛高并发写;ZSet实现排行榜。
  3. Lua脚本保证Redis多命令原子,减少网络IO;纯Redis逻辑优先Lua。
  4. 分清穿透/击穿/雪崩;普通业务用延迟双删做到最终一致性;热点场景做缓存预热。
  5. 分布式锁严格选型:纯Redis逻辑用Lua;跨Redis+MySQL复杂业务用Redisson;利用可重入、看门狗解决死锁与锁过期问题。
相关推荐
卷毛的技术笔记1 小时前
RocketMQ事务消息:我把分布式事务这层窗户纸捅破了
java·分布式·后端·java-rocketmq
yxlalm1 小时前
对话记忆持久化-Redis热与MySQL冷
android·redis·mysql
铁皮饭盒1 小时前
网页端, 40mb离线模型, 自动抠图, 不用 Python,不用服务器,不用 API Key, 不用显卡
前端·javascript·后端
yxlalm1 小时前
零基础快速上手Trae创建Java项目
java·人工智能
ly76891 小时前
磁盘 I/O 延迟突增:用 iostat、blktrace 与火焰图定位到具体调用栈
java·linux·前端·数据库·iostat·磁盘 i/o·blktrace
泡海椒1 小时前
jquick-pdf 核心原理解析:基于 HTML 模板动态渲染 PDF 的实现逻辑
java·开发语言·pdf
IT_陈寒1 小时前
Redis并发写入踩坑记录:别让超时设置坑了你
前端·人工智能·后端
Bs_MoneyMagnet1 小时前
基于springboot+vue的图书馆预约系统的设计与实现 源码+文档
java·vue.js·spring boot·后端·毕业设计·图书管理系统·计算机毕业设计
console.log('npc')2 小时前
03 — 核心框架:App 与中间件
后端·node.js·express