目录
[1. String 类型(字符串)](#1. String 类型(字符串))
[2. Hash 类型(哈希表)](#2. Hash 类型(哈希表))
[3. List 类型(列表)](#3. List 类型(列表))
[4. Set 类型(集合)](#4. Set 类型(集合))
[5. ZSet 类型(有序集合)](#5. ZSet 类型(有序集合))
[1. 基于 List 的阻塞队列(最常用)](#1. 基于 List 的阻塞队列(最常用))
[2. Pub/Sub 发布订阅模型(实时通知)](#2. Pub/Sub 发布订阅模型(实时通知))
[3. Stream 类型(高可靠性队列)](#3. Stream 类型(高可靠性队列))
[4. 消息队列的增强方案](#4. 消息队列的增强方案)
[5. 最佳实践与注意事项](#5. 最佳实践与注意事项)
[八、什么是 Redis 缓存穿透?如何解决?](#八、什么是 Redis 缓存穿透?如何解决?)
[九、什么是 Redis 缓存雪崩?它与缓存穿透有何区别?如何解决?](#九、什么是 Redis 缓存雪崩?它与缓存穿透有何区别?如何解决?)
[1. 核心问题定位](#1. 核心问题定位)
[2. 方案](#2. 方案)
[1). 基础方案:Cache Aside Pattern(旁路缓存模式)](#1). 基础方案:Cache Aside Pattern(旁路缓存模式))
[2). 高并发优化:延迟双删策略](#2). 高并发优化:延迟双删策略)
[3). 分布式系统方案:异步消息队列(MQ)](#3). 分布式系统方案:异步消息队列(MQ))
[4). 自动化方案:订阅Binlog实现最终一致](#4). 自动化方案:订阅Binlog实现最终一致)
5). 极端场景:分布式锁. 极端场景:分布式锁)
一、什么是Redis
Redis是一个开源的、基于内存的高性能Key-Value数据库,支持多种数据结构(字符串、列表、集合、有序集合、哈希等),提供持久化、事务、分布式锁等功能,每秒可处理超10万次读写操作。
二、Redis性能高的原因?
-
纯内存操作:数据存储在内存中,读写速度极快。
-
单线程模型:避免上下文切换和锁竞争,使用多路I/O复用(epoll)。
-
高效数据结构:如跳表(SkipList)、压缩列表(ZipList)优化存储。
三、Redis的持久化方式及优缺点
-
RDB(快照):
-
优点:文件紧凑,恢复速度快;主进程不影响性能(子进程写入)。
-
缺点:可能丢失最后一次快照后的数据。
-
-
AOF(日志追加):
-
优点:数据安全性高(可配置每秒或每条命令同步)。
-
缺点:文件体积大,恢复速度慢。
-
-
混合模式:结合RDB和AOF,平衡性能与安全性。
四、Redis的淘汰策略及适用场景
| 策略 | 说明 |
|---|---|
| noeviction | 内存不足时报错,适用于不允许数据丢失的场景 |
| allkeys-lru | 淘汰最近最少使用的Key,适用于热点数据场景 |
| volatile-lru | 仅淘汰过期集合中的最近最少使用Key,适用于有TTL的数据 |
| allkeys-random | 随机淘汰Key,适用于无明确热点数据的场景 |
| volatile-ttl | 优先淘汰存活时间(TTL)较短的Key,适用于需要快速释放内存的场景 |
五、redis核心数据类型有哪些
| 数据结构 | 底层实现 | 典型应用场景 |
|---|---|---|
| String(字符串) | 简单动态字符串(SDS) | 缓存用户信息、计数器(如点赞数/访问量)、分布式锁、会话存储、限流控制 |
| List(列表) | 双向链表 + 压缩列表(ZipList)/ QuickList(Redis 3.2+) | 消息队列、最新动态列表、任务队列、分页查询、实时日志流处理 |
| Hash(哈希) | 字典(哈希表)+ 压缩列表(ZipList)/ Listpack(新版) | 存储对象属性(如用户信息)、购物车、好友关系、配置中心、实时统计 |
| Set(集合) | 整数集合(Intset)或哈希表(当元素为整数且数量少时用Intset,否则用哈希表) | 去重(如用户ID去重)、共同好友计算、随机抽奖、权限控制、标签系统 |
| Zset(有序集合) | 跳跃表(Skiplist)+ 哈希表 | 排行榜(如游戏积分、热搜榜)、带权重的任务调度、地理位置排序、实时推荐系统 |
扩展:
1. String 类型(字符串)
核心命令 :SET(设置)、GET(获取)、INCR(递增)、DECR(递减)、EXPIRE(设置过期时间)、MSET(批量设置)、MGET(批量获取)
案例代码:
java
// 设置键值对(带1800秒过期时间)
jedis.setex("user:1001:session", 1800, "active"); // SETEX key seconds value:设置过期时间的字符串
String session = jedis.get("user:1001:session"); // GET key:获取值
// 文章浏览量计数器(原子递增)
long views = jedis.incr("article:1001:views"); // INCR key:值自增1,返回递增后结果(键不存在时初始化为0)
jedis.decr("product:1001:stock"); // DECR key:值自减1
// 批量设置配置项
jedis.mset("config:max_users", "100", "config:timeout", "30"); // MSET key value [key value ...]:一次性设置多个键值对
List<String> values = jedis.mget("config:max_users", "config:timeout"); // MGET key [key ...]:一次性获取多个值
2. Hash 类型(哈希表)
核心命令 :HSET(设置字段)、HGET(获取字段)、HGETALL(获取所有字段)、HINCRBY(字段递增)、HDEL(删除字段)
案例代码:
java
// 存储用户信息(对象属性)
jedis.hset("user:1001", "name", "Alice"); // HSET key field value:设置哈希表字段值
jedis.hset("user:1001", "age", "30");
String age = jedis.hget("user:1001", "age"); // HGET key field:获取字段值
// 获取用户所有属性
Map<String, String> user = jedis.hgetAll("user:1001"); // HGETALL key:返回所有字段和值(Map形式)
// 用户积分原子递增
jedis.hincrBy("user:1001", "points", 10); // HINCRBY key field increment:字段值按增量增加(整数)
jedis.hdel("user:1001", "temp_field"); // HDEL key field [field ...]:删除一个或多个字段
3. List 类型(列表)
核心命令 :LPUSH(左侧插入)、RPUSH(右侧插入)、LPOP(左侧弹出)、RPOP(右侧弹出)、LRANGE(范围查询)、BLPOP(阻塞左侧弹出)
案例代码:
java
// 消息队列(左侧入队,右侧出队)
jedis.lpush("task_queue", "Task1", "Task2"); // LPUSH key element [element ...]:左侧插入一个或多个元素
String task = jedis.rpop("task_queue"); // RPOP key:右侧弹出一个元素
// 实时日志(存储最新10条)
jedis.lpush("log_messages", "2023-12-10: Error: Disk full");
List<String> logs = jedis.lrange("log_messages", 0, 9); // LRANGE key start stop:获取指定范围的元素(0到9表示前10条)
// 阻塞队列(无数据时等待)
List<String> tasks = jedis.blpop(0, "task_queue"); // BLPOP key [key ...] timeout:阻塞式左侧弹出,0表示无限等待
4. Set 类型(集合)
核心命令 :SADD(添加元素)、SMEMBERS(获取所有元素)、SCARD(元素数量)、SISMEMBER(检查元素是否存在)、SDIFF(差集)、SINTER(交集)
案例代码:
java
// 用户标签管理
jedis.sadd("user:1001:tags", "VIP", "Tech", "Gamer"); // SADD key member [member ...]:添加一个或多个元素(去重)
Set<String> tags = jedis.smembers("user:1001:tags"); // SMEMBERS key:获取集合所有元素
// 社交推荐(共同好友)
Set<String> userFriends = jedis.smembers("user:1001:friends");
Set<String> commonFriends = jedis.sinter("user:1001:friends", "user:1002:friends"); // SINTER key [key ...]:交集(共同好友)
// 去重抽奖用户池
long count = jedis.scard("lottery_users"); // SCARD key:获取元素数量
boolean isExist = jedis.sismember("lottery_users", "user1"); // SISMEMBER key member:检查元素是否存在
5. ZSet 类型(有序集合)
核心命令 :ZADD(添加/更新)、ZRANGE(升序范围查询)、ZREVRANGE(降序范围查询)、ZREM(删除)、ZINCRBY(分数递增)、ZRANK(排名)
案例代码:
java
// 积分排行榜(升序排列)
jedis.zadd("leaderboard", 1500, "PlayerA"); // ZADD key [NX|XX] [GT|LT] [CH] [INCR] score member:添加成员及分数(可带选项)
jedis.zadd("leaderboard", 1200, "PlayerB");
Set<Tuple> top3 = jedis.zrangeWithScores("leaderboard", 0, 2); // ZRANGE key start stop [WITHSCORES]:升序获取指定范围的成员及分数
// 待办任务(按截止时间排序)
long now = System.currentTimeMillis();
jedis.zadd("tasks", now + 3600000, "Task1"); // 1小时后截止
Set<String> dueTasks = jedis.zrangeByScore("tasks", 0, System.currentTimeMillis()); // ZRANGEBYSCORE key min max:按分数范围查询
// 删除成员并获取排名
jedis.zrem("leaderboard", "PlayerB"); // ZREM key member [member ...]:删除一个或多个成员
Long rank = jedis.zrank("leaderboard", "PlayerA"); // ZRANK key member:获取成员的升序排名(从0开
最佳实践总结
-
键设计 :采用
业务模块:实体ID:属性的分层命名(如user:1001:profile),避免冲突且便于批量操作。 -
批量操作 :使用
MSET/MGET(String)、PIPELINE(多命令批量)减少网络延迟。 -
过期时间 :对临时数据(如会话、缓存)设置
EXPIRE,避免内存泄漏。 -
集群兼容 :在 Redis Cluster 中,通过
{hashTag}确保同一实体的键落在同一哈希槽(如order:{1001}:items)。 -
错误处理 :检查命令返回值(如
INCR返回新值,SETNX返回是否成功),避免空指针异常。
六、redis如何实现延迟队列
核心原理
利用Redis的有序集合(Sorted Set,ZSET) ,将任务的执行时间戳 作为score,任务内容作为member。通过定时轮询 或事件触发获取并处理到期任务。
扩展
1、实现原理
Redis实现延迟队列的核心是有序集合(Sorted Set,ZSET):
-
成员(member):存储任务标识(如任务ID或序列化后的任务内容)。
-
分数(score):存储任务的执行时间戳(毫秒级)。
-
排序机制:ZSET会根据score自动排序,最小score的任务排在队列最前。
-
轮询机制:通过定时任务(如每秒一次)扫描ZSET,获取score小于等于当前时间的任务,执行后从ZSET中删除。
2、核心代码示例(Java)
java
// 添加延迟任务
public void addDelayTask(String taskId, String taskContent, long delaySeconds) {
long executeTime = System.currentTimeMillis() + delaySeconds * 1000;
redisTemplate.opsForZSet().add("delay_queue", taskContent, executeTime);
}
// 轮询处理到期任务
public void processDelayTasks() {
long now = System.currentTimeMillis();
Set<String> tasks = redisTemplate.opsForZSet().rangeByScore("delay_queue", 0, now);
if (tasks != null && !tasks.isEmpty()) {
for (String task : tasks) {
// 执行任务逻辑(如发送短信、取消订单等)
System.out.println("Processing task: " + task);
// 从ZSET中删除已处理的任务
redisTemplate.opsForZSet().remove("delay_queue", task);
}
}
}
3、适用场景
-
订单超时关闭(如15分钟未支付)。
-
定时任务重试(如接口调用失败后延迟重试)。
-
缓存预热、活动提醒等。
替代方案
-
高可靠性需求:用RabbitMQ(死信队列)或RocketMQ(精确延迟级别)。
-
大规模延迟消息:用Kafka(时间轮算法)。
七、redis如何实现消息队列
Redis 实现消息队列的核心方式有三种:基于 List 的阻塞队列(最常用) 、Pub/Sub 发布订阅模型 (实时通知) 和 Stream 类型(高可靠性队列)。
| 方案 | 适用场景 | 优点 | 缺点 |
|---|---|---|---|
| List 阻塞队列 | 简单任务、短期存储、高并发 | 简单高效、原子性 | 无ACK、单消费者限制 |
| Pub/Sub | 实时通知、广播、低延迟 | 零延迟、低开销 | 无持久化、消息易丢失 |
| Stream | 高可靠性、负载均衡、历史回溯 | 支持ACK、消费者组 | 性能开销较高 |
-
简单场景:优先使用 List 阻塞队列(如日志处理、异步任务)。
-
实时广播:使用 Pub/Sub(如在线状态更新、实时推送)。
-
高可靠性要求:选择 Stream(如订单处理、金融交易)。
备注:用 Redis 做消息队列,优点是轻量易部署、性能高、开发简单,缺点是功能单一、可靠性保障弱;专业 MQ 优点是功能丰富、可靠性高、能应对复杂场景,缺点是部署运维复杂、性能开销大。
扩展
1. 基于 List 的阻塞队列(最常用)
原理
-
使用 Redis 的 List 类型 ,通过
LPUSH(左端插入)和RPOP(右端弹出)实现 FIFO 队列。 -
阻塞版本 :
BLPOP/BRPOP可在队列为空时阻塞等待,避免 CPU 空转。
代码示例
java
// 生产者:向队列左端插入任务
jedis.lpush("task_queue", "Task1");
jedis.lpush("task_queue", "Task2");
// 消费者:阻塞式右端弹出(最多等待10秒)
List<String> tasks = jedis.brpop(10, "task_queue");
System.out.println("处理任务: " + tasks.get(1)); // 输出 "Task1" 或 "Task2"
优点
-
简单高效:单线程架构确保原子性,适合简单任务。
-
持久化支持:可配置 RDB/AOF 持久化,避免重启后消息丢失。
-
批量操作 :支持
LPUSH/RPOP批量插入/弹出,减少网络开销。
缺点
-
无消息确认:消费者崩溃可能导致消息丢失(需自行实现 ACK 机制)。
-
单消费者限制 :默认不支持多消费者竞争(可通过
RPOPLPUSH配合备份队列实现)。
2. Pub/Sub 发布订阅模型(实时通知)
原理
-
发布者 向频道(Channel)发送消息,订阅者 实时接收。
-
无持久化:消息仅在订阅时推送,断开连接后消息丢失。
代码示例
java
// 订阅者
new Thread(() -> {
JedisPubSub pubSub = new JedisPubSub() {
@Override
public void onMessage(String channel, String message) {
System.out.println("收到消息: " + message);
}
};
jedis.subscribe(pubSub, "news_channel");
}).start();
// 发布者
jedis.publish("news_channel", "重大新闻:Redis 6.0发布!");
适用场景
-
实时广播(如在线状态通知、实时日志)。
-
对消息可靠性要求不高的场景。
3. Stream 类型(高可靠性队列)
原理
-
Redis 5.0+ 引入的 对数结构,支持消息持久化、消费者组(Consumer Group)、消息确认(ACK)和历史消息回溯。
-
核心概念:
-
消息ID :自动生成的递增ID(如
1630451234567-0)。 -
消费者组:多个消费者共享队列,每条消息仅被组内一个消费者处理。
-
Pending列表:已发送但未确认的消息,支持重试和超时。
-
代码示例
java
// 生产者:添加消息到Stream
jedis.xadd("order_stream", new StreamEntryID(), Map.of("item", "手机", "price", "3999"));
// 消费者组创建
jedis.xgroupCreate("order_stream", "order_group", new StreamEntryID(), true);
// 消费者:从组内读取消息
Map<StreamEntryID, Map<String, String>> messages = jedis.xreadGroup(
"order_group", "consumer1",
StreamEntryID.NEW_ENTRY, 1, 0, "order_stream"
);
// 处理消息后发送ACK
for (Entry<StreamEntryID, Map<String, String>> entry : messages.entrySet()) {
jedis.xack("order_stream", "order_group", entry.getKey());
}
优点
-
可靠性高:支持消息确认、重试和持久化。
-
负载均衡:消费者组自动分配消息,避免重复处理。
-
历史回溯:可查询任意ID之后的消息。
缺点
- 性能开销:相比 List 略高,需权衡可靠性与性能。
4. 消息队列的增强方案
(1)死信队列与重试机制
-
使用
RPOPLPUSH将消息备份到处理队列的同时,移入备份队列。若处理失败,可将备份队列的消息重新放回主队列。 -
示例:
java// 原子性转移消息到备份队列 jedis.rpoplpush("task_queue", "backup_queue");
(2)延迟队列
-
结合 ZSet 实现延迟消息:
java// 添加延迟任务(当前时间+10秒后执行) long delay = System.currentTimeMillis() + 10000; jedis.zadd("delay_queue", delay, "Task1"); // 定时任务检查到期任务 Set<String> dueTasks = jedis.zrangeByScore("delay_queue", 0, System.currentTimeMillis());
(3)分布式限流
-
使用 List 配合
INCR实现令牌桶限流:java// 生成令牌 jedis.incr("token_bucket"); jedis.expire("token_bucket", 1); // 每秒重置
5. 最佳实践与注意事项
(1)键设计规范
-
队列键名采用 业务:模块:队列 格式(如
mq:order:task),便于监控和管理。 -
避免过长的键名,影响性能。
(2)持久化配置
-
根据场景选择:
-
RDB:快照持久化,适合对实时性要求不高的场景。
-
AOF :日志追加,适合要求高可靠性的场景(可配置
appendfsync always)。
-
(3)监控与告警
-
通过 Redis 的
INFO stats监控队列长度、处理速率等指标:bashINFO stats | grep keyspace_hits -
使用 RedisInsight 或 Prometheus+Grafana 可视化监控。
(4)集群模式
-
在 Redis Cluster 中,确保同一队列的键落在同一哈希槽(使用
{hashTag}):javajedis.lpush("mq:{order}:task", "Task1"); // 确保order的键在同一槽位
(5)消费者幂等性
- 通过消息唯一ID(如 Stream 的消息ID)或业务ID(如订单号)确保重复消息不重复处理。
八、什么是 Redis 缓存穿透?如何解决?
概念:缓存穿透指客户端请求的数据在缓存(Redis)和数据库中都不存在,导致每次请求都穿透缓存直达数据库,造成数据库压力过大(甚至宕机)。例如,恶意请求查询不存在的用户 ID(如id=-1)。
解决方案:
-
布隆过滤器:在缓存前添加布隆过滤器,提前过滤不存在的请求。布隆过滤器可快速判断数据是否可能存在,若不存在则直接返回,避免访问数据库。
-
空值缓存:对查询结果为空的数据,在 Redis 中缓存空值(设置较短过期时间,如 5 分钟),防止相同请求重复穿透。
-
接口校验:在 API 层对请求参数进行校验(如 ID 必须为正整数),过滤非法请求。
九、什么是 Redis缓存雪崩?它与缓存穿透有何区别?如何解决?
概念:缓存雪崩指大量缓存 key 在同一时间过期,或 Redis 服务宕机,导致所有请求瞬间直达数据库,造成数据库过载。
与缓存穿透的区别:
-
缓存穿透:请求的数据 "本身不存在",导致穿透(高频少量无效请求)。
-
缓存雪崩:请求的数据 "存在但缓存失效",导致批量请求穿透(低频大量有效请求)。
解决方案:
-
过期时间随机化:为缓存 key 设置过期时间时添加随机值(如expire + Math.random()*1000),避免集中过期。
-
多级缓存:增加本地缓存(如 Caffeine),减少对 Redis 的依赖,即使 Redis 宕机,本地缓存可临时承接部分请求。
-
服务熔断 / 限流:使用 Sentinel 或 Hystrix 对数据库请求进行限流,超出阈值则拒绝请求,保护数据库。
-
Redis 集群:部署主从 + 哨兵或 Redis Cluster,避免单点故障导致 Redis 整体不可用。
十、什么是Redis缓存击穿
定义 :
某个热点Key在缓存中过期时,大量并发请求同时访问该Key,导致所有请求穿透缓存,直接查询数据库,造成数据库压力骤增。
核心特点:
-
针对单个热点Key:问题集中在某一个被高频访问的数据。
-
并发请求集中:Key过期瞬间,大量请求同时到达。
-
临时性:问题通常在缓存重建后缓解。
典型场景 :
电商促销时,某款热门商品的详情缓存过期,瞬间大量用户请求直接查询数据库。
解决方案:
-
互斥锁:重建缓存时加锁,确保只有一个请求访问数据库。
-
逻辑过期:缓存过期后返回旧数据,后台异步更新。
-
永不过期:对热点数据不设过期时间,定期后台刷新。
十一、雪崩、穿透、击穿的对比总结
| 问题类型 | 成因 | 影响范围 | 典型场景 | 核心解决方案 |
|---|---|---|---|---|
| 缓存击穿 | 单个热点Key过期,并发请求穿透 | 单个Key,临时性 | 热门商品详情缓存过期 | 互斥锁、逻辑过期、永不过期 |
| 缓存雪崩 | 大量Key同时过期或缓存服务崩溃 | 批量Key,系统性 | 统一过期时间导致批量失效 | 分散过期时间、多级缓存、限流降级 |
| 缓存穿透 | 查询不存在的数据,持续穿透 | 无数据,持续性 | 恶意攻击或错误参数查询不存在数据 | 布隆过滤器、缓存空值、接口限流 |
直观类比
-
击穿:像"单点爆破"------一个热点Key失效导致局部崩溃。
-
雪崩:像"连锁反应"------大量Key同时失效导致全面崩溃。
-
穿透:像"无底洞"------查询不存在的数据,每次请求都直击数据库。
十二、如何保证Redis和数据库的数据一致性?
保证Redis和数据库一致性通常采用Cache Aside(旁路缓存)模式,即读缓存未命中时查数据库并写入缓存,写操作时先更新数据库再删除缓存。为解决删除缓存失败的问题,可以通过消息队列异步重试或监听Binlog触发更新。对于并发更新场景,可以使用分布式锁或版本号控制。此外,Redis集群本身通过主从复制和哨兵模式保证高可用,但需注意异步复制可能导致的短暂不一致。最终,应根据业务容忍度选择强一致性或最终一致性方案。
扩展
1. 核心问题定位
首先明确数据不一致的根源:Redis作为缓存层,与数据库(如MySQL)的更新操作存在时间差,导致并发场景下缓存与数据库数据不一致。常见场景包括:
-
缓存击穿:热点数据过期后,大量请求直接穿透到数据库。
-
脏数据:缓存更新失败或顺序错误,导致缓存与数据库数据不同步。
-
主从同步延迟:数据库主从架构中,从库数据未及时同步,缓存回填旧数据。
2. 方案
旁路缓存模式是基础方案,读写流程简单直接;而其他方案主要针对高并发、分布式、强一致性等场景,对写流程进行了扩展或优化
1). 基础方案:Cache Aside Pattern(旁路缓存模式)
-
操作流程:
-
读操作:先查缓存,未命中则查数据库,写入缓存后返回。
-
写操作 :先更新数据库,再删除缓存(而非更新缓存,避免复杂计算)。
-
-
优点:实现简单,适合大多数业务场景。
-
缺点:极端并发下可能出现短暂不一致(如写后立即读)。
-
适用场景:
- 普通业务(如商品详情页),对一致性要求不高,读多写少。
2). 高并发优化:延迟双删策略
-
操作流程:
-
读操作:与旁路缓存一致。
-
写操作 :先删除Redis缓存,再更新数据库,再休眠500ms(休眠时间需大于业务读操作耗时(如通过压测评估)),再次删除缓存。
-
-
目的:覆盖第一次删除失败或读请求在休眠期间重建脏缓存的情况。
-
适用场景:
- 高并发秒杀、热点数据频繁更新的场景(如库存扣减)。
3). 分布式系统方案:异步消息队列(MQ)
-
操作流程:
-
读操作:与旁路缓存一致。
-
写操作:
-
更新数据库。
-
发送消息到Kafka/RabbitMQ。
-
消费者异步删除缓存。
-
-
-
优点:
-
解耦数据库与缓存操作,提高吞吐量。
-
MQ的重试机制保证消息可靠性。
-
-
缺点:引入额外组件,增加系统复杂度。
-
适用场景:
- 跨服务数据同步(如订单服务更新后通知库存服务删缓存)、分布式系统。
4). 自动化方案:订阅Binlog实现最终一致
-
技术实现:
-
使用Canal等工具订阅数据库Binlog,解析变更事件。
-
异步更新或删除缓存。
-
-
优点:
-
彻底解耦,无需修改应用代码。
-
适合高可用分布式系统。
-
-
适用场景:
- 需要彻底解耦的复杂系统(如多数据源、多缓存集群)。
5). 极端场景:分布式锁
-
操作流程:
- 写操作前获取分布式锁(如Redisson),确保同一时间只有一个线程更新数据库和缓存。
-
优点:避免并发冲突。
-
缺点:性能开销大,降低系统并发能力。
-
适用场景:
- 金融交易、核心数据变更等强一致性要求场景。