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)。
落地小提示
- 对象缓存优先 Hash,不要把整个对象序列化塞进 String,局部更新代价很高。
- 排行榜一定用 ZSet,不要在业务代码内存排序。
- 计数、库存优先 String。
- 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;恶意请求可以打垮数据库。
解决方案:
- 空值缓存:不存在的数据缓存短期空key;
- 布隆过滤器前置过滤不存在key;
- 接口参数校验、黑名单。
#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过期瞬间,大量并发同时打到数据库。
解决方案:
- 热点key设置永不过期;
- 互斥锁:仅一个线程更新缓存,其他等待;
- 定时任务主动刷新热点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整体宕机;大量流量全部压向数据库。
解决方案:
- TTL增加随机偏移,避免集中过期;
- Redis高可用:主从、哨兵、集群;
- 服务侧限流熔断降级;
- 多级缓存:本地缓存 + Redis。
4.4 缓存与MySQL一致性
主流策略对比
| 策略 | 流程 | 缺点 |
|---|---|---|
| 先更新库,再更新缓存 | update mysql → set redis | 很多缓存数据没人读,浪费写性能 |
| 先更新库,再删除缓存 | update mysql → del redis | 极端并发会短暂脏数据 |
延迟双删(最终一致性方案)
抽象流程:
- 更新 MySQL;
- 立刻删除缓存;
- 通过延时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核心能力
- 可重入锁:同一个线程多次加锁不会死锁;底层Hash结构记录锁计数。
- 看门狗自动续期:默认锁30s,业务未执行完后台自动续期;防止业务没跑完锁过期释放。
- 超时防死锁:实例宕机锁会自动过期释放,杜绝死锁。
#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锁 选型边界(重点)
- 逻辑只操作Redis,不操作MySQL:优先Lua脚本,性能更高。>
示例:Redis内库存预扣、计数校验、防重。
- 跨中间件:Redis + MySQL,复杂业务、长事务:必须用Redisson分布式锁。>
示例:订单创建、资金、状态流转。
使用规范
- 锁释放写在
finally块,防止异常不释放; - 尽量不手动指定锁过期时间,交给看门狗;
- 高并发场景按需使用读写锁、公平锁。
整体总结
- Redis业务落地第一步:选对数据结构;String计数、Hash存对象、ZSet排行;Bitmap签到、HyperLogLog UV统计、Geo地理位置、Stream轻量消息队列。
- 缓存三大范式:读缓存分担读压力;写缓存配合MQ异步落库扛高并发写;ZSet实现排行榜。
- Lua脚本保证Redis多命令原子,减少网络IO;纯Redis逻辑优先Lua。
- 分清穿透/击穿/雪崩;普通业务用延迟双删做到最终一致性;热点场景做缓存预热。
- 分布式锁严格选型:纯Redis逻辑用Lua;跨Redis+MySQL复杂业务用Redisson;利用可重入、看门狗解决死锁与锁过期问题。