📖 开场:日记本的故事
想象你在写日记 📔:
方案1:写在草稿纸上(内存)
优点:写得快!✍️⚡
缺点:突然停电,内容全没了!💀
方案2:写在笔记本上(磁盘)
优点:停电也不怕,永久保存!💪
缺点:写得慢!✍️🐌
方案3:先写草稿,定期抄到笔记本(异步刷盘)
优点:又快又安全(一般情况下)!😊
缺点:如果还没来得及抄就停电,最近的内容会丢!😰
方案4:写草稿的同时让朋友也抄一份(主从同步)
优点:你的本子丢了,朋友还有备份!🛡️
缺点:需要协调两个人,稍微慢一点!⏱️
这就是RocketMQ的刷盘和主从同步机制!
💾 Part 1: 刷盘机制(Flush Mechanism)
🤔 什么是刷盘?
刷盘(Flush) = 把内存中的数据写入磁盘
Producer发送消息
↓
Broker接收消息
↓
写入内存(PageCache)← 很快!⚡
↓
刷盘到磁盘 ← 慢但安全!💾
为什么需要刷盘?
内存的问题:
- ⚡ 快!读写速度极快(纳秒级)
- 💀 不安全!断电就丢失
- 💰 贵!容量小
磁盘的优点:
- 💾 安全!持久化存储
- 💰 便宜!容量大
- 🐌 慢!读写速度慢(毫秒级)
刷盘就是在速度和安全之间找平衡!
📊 RocketMQ的两种刷盘方式
1️⃣ 同步刷盘(Synchronous Flush)🔐
定义:消息写入磁盘后,才返回成功
流程:
Producer发送消息
↓
Broker接收消息
↓
写入内存(PageCache)
↓
立即刷盘到磁盘 ← 等待完成!⏱️
↓
刷盘成功 ✅
↓
返回成功给Producer ✅
时序图:
Producer Broker Disk
│ │ │
│ 发送消息 │ │
├─────────────────>│ │
│ │ 写入PageCache │
│ ├──────────> │
│ │ 刷盘到磁盘 │
│ ├─────────────────>│
│ │ │
│ │ 等待刷盘完成 │
│ │<─────────────────┤
│ │ │
│ 返回成功 │ │
│<─────────────────┤ │
│ │ │
生活比喻:
你去银行存钱,柜员必须把钱放进保险柜后,才给你回执单 🏦💰
配置:
# 同步刷盘
flushDiskType=SYNC_FLUSH
优点:
- ✅ 数据安全性极高(断电也不丢)
- ✅ 适合金融、交易等场景
缺点:
- ❌ 性能较低(磁盘IO成为瓶颈)
- ❌ 延迟较高(每条消息都要等刷盘)
- ❌ TPS较低(每秒处理消息数少)
性能数据:
同步刷盘 TPS: 1000-3000 消息/秒
2️⃣ 异步刷盘(Asynchronous Flush)⚡
定义:消息写入内存后,立即返回成功,后台异步刷盘
流程:
Producer发送消息
↓
Broker接收消息
↓
写入内存(PageCache)
↓
立即返回成功给Producer ✅ ← 不等刷盘!
↓
后台线程异步刷盘到磁盘 ⏰
时序图:
Producer Broker Disk
│ │ │
│ 发送消息 │ │
├─────────────────>│ │
│ │ 写入PageCache │
│ ├──────────> │
│ 立即返回成功 │ │
│<─────────────────┤ │
│ │ │
│ │ 后台异步刷盘 │
│ ├─────────────────>│
│ │ │
生活比喻:
你去银行存钱,柜员收到钱后立即给你回执单,然后慢慢把钱放进保险柜 🏦💰
配置:
# 异步刷盘(默认)
flushDiskType=ASYNC_FLUSH
# 刷盘间隔(毫秒,默认500ms)
flushIntervalCommitLog=500
# 一次刷盘至少要多少页(默认4页=16KB)
flushCommitLogLeastPages=4
优点:
- ✅ 性能极高(不等待磁盘IO)
- ✅ 延迟低
- ✅ TPS高
缺点:
- ❌ 有丢失风险(断电时,内存中未刷盘的数据会丢失)
- ❌ 最多丢失500ms内的数据(一个刷盘间隔)
性能数据:
异步刷盘 TPS: 10000-50000 消息/秒(比同步快10-50倍!)
🎯 刷盘策略对比
| 特性 | 同步刷盘 | 异步刷盘 |
|---|---|---|
| 配置 | SYNC_FLUSH | ASYNC_FLUSH(默认) |
| 性能(TPS) | 1000-3000 | 10000-50000 |
| 延迟 | 高(10-50ms) | 低(<1ms) |
| 数据安全 | 🛡️🛡️🛡️ 极高 | 😐 中等 |
| 断电丢失 | ✅ 不丢失 | ❌ 丢失500ms内的数据 |
| 适用场景 | 金融、交易、订单 | 日志、监控、一般业务 |
| 推荐度 | 高可靠性场景 ⭐⭐⭐ | 高性能场景 ⭐⭐⭐⭐⭐ |
💻 刷盘机制的代码配置
Broker配置文件
# ======================== 刷盘配置 ========================
# ⭐ 刷盘方式:SYNC_FLUSH(同步)或 ASYNC_FLUSH(异步)
flushDiskType=ASYNC_FLUSH
# ============= 异步刷盘参数 =============
# 刷盘间隔时间(毫秒)
# 默认500ms刷一次盘
flushIntervalCommitLog=500
# 一次刷盘至少要多少页
# 1页=4KB,默认4页=16KB
# 含义:内存积累了至少16KB数据才刷盘
flushCommitLogLeastPages=4
# 两次刷盘的最大间隔时间(毫秒)
# 含义:即使数据不足16KB,10秒也必须刷一次
flushCommitLogThoroughInterval=10000
# ============= 同步刷盘参数 =============
# 同步刷盘超时时间(毫秒)
syncFlushTimeout=5000
# ============= CommitLog相关 =============
# CommitLog文件大小(默认1GB)
mapedFileSizeCommitLog=1073741824
# 是否使用 TransientStorePool
# 开启后会先写入堆外内存,再异步刷盘,性能更高
transientStorePoolEnable=false
生产者端配置
import org.apache.rocketmq.client.producer.DefaultMQProducer;
import org.apache.rocketmq.client.producer.SendResult;
import org.apache.rocketmq.common.message.Message;
public class ProducerWithFlushConfig {
public static void main(String[] args) throws Exception {
DefaultMQProducer producer = new DefaultMQProducer("flush_test_group");
producer.setNamesrvAddr("localhost:9876");
// ⭐ 设置发送超时时间(如果是同步刷盘,需要调大)
producer.setSendMsgTimeout(10000); // 10秒
producer.start();
// 发送消息
Message msg = new Message(
"FlushTestTopic",
"TagA",
"测试刷盘机制".getBytes()
);
SendResult result = producer.send(msg);
System.out.println("发送结果: " + result);
producer.shutdown();
}
}
🔍 刷盘的底层原理
PageCache(页缓存)
什么是PageCache?
PageCache是Linux操作系统提供的文件缓存机制:
应用程序(RocketMQ)
↓
写入 PageCache(内存)← 快!
↓
操作系统异步刷盘 ← 慢
↓
磁盘
RocketMQ的写入流程:
// 1. 写入MappedFile(底层是mmap映射的PageCache)
mappedFile.appendMessage(message);
↓
// 2. 数据在PageCache中
// 3. 同步刷盘:立即调用 force() 强制刷盘
if (flushDiskType == SYNC_FLUSH) {
mappedFile.flush(); // 调用 FileChannel.force()
}
// 4. 异步刷盘:后台线程定期调用 flush()
if (flushDiskType == ASYNC_FLUSH) {
// FlushRealTimeService 线程每500ms刷一次
}
FileChannel.force() 的作用
Java NIO的force()方法:
FileChannel channel = ...;
// force(false): 只保证数据刷盘,不保证元数据
channel.force(false);
// force(true): 数据和元数据都刷盘(更安全但更慢)
channel.force(true);
RocketMQ使用:
// 同步刷盘时调用
fileChannel.force(false); // 只刷数据,不刷元数据(更快)
TransientStorePool(堆外内存池)
高级优化:使用堆外内存作为缓冲
开启 transientStorePoolEnable=true:
Producer发送消息
↓
Broker写入堆外内存(DirectByteBuffer)← 极快!
↓
后台线程1:堆外内存 → PageCache
↓
后台线程2:PageCache → 磁盘
好处:
- ✅ 减少PageCache的写入压力
- ✅ 提高性能
- ✅ 适合高并发场景
配置:
transientStorePoolEnable=true
transientStorePoolSize=5 # 堆外内存池大小
🔄 Part 2: 主从同步(Master-Slave Replication)
🤔 什么是主从同步?
定义:主节点(Master)的数据同步到从节点(Slave)
Master(主)
│
写入消息 │ 复制消息
↓
Slave(从)
为什么需要主从?
单Master的问题:
只有1个Master节点
Master宕机 💀
↓
服务不可用!❌
↓
即使数据在磁盘上,也无法访问!
主从架构:
Master(主)+ Slave(从)
Master宕机 💀
↓
Slave继续提供服务 ✅(只能读,不能写)
↓
或者 Slave升级为Master(DLedger模式)
📊 RocketMQ的主从架构
1️⃣ 单Master模式 ❌
┌─────────────┐
│ Master │
└─────────────┘
特点:
- ✅ 配置简单
- ✅ 性能最高
- ❌ 单点故障(Master挂了就完了)
- ❌ 不推荐生产环境使用!
2️⃣ 多Master模式 😐
┌─────────────┐ ┌─────────────┐ ┌─────────────┐
│ Master-1 │ │ Master-2 │ │ Master-3 │
└─────────────┘ └─────────────┘ └─────────────┘
特点:
- ✅ 无单点故障(一个Master挂了,其他Master继续服务)
- ✅ 性能高
- ❌ 可能丢失少量数据(异步刷盘时)
- ❌ 没有热备(Master挂了,该Master上的消息无法消费,直到恢复)
3️⃣ 多Master多Slave模式(异步复制)⭐⭐⭐⭐
┌─────────────┐ ┌─────────────┐ ┌─────────────┐
│ Master-1 │ │ Master-2 │ │ Master-3 │
└──────┬──────┘ └──────┬──────┘ └──────┬──────┘
│ 异步复制 │ │
↓ ↓ ↓
┌─────────────┐ ┌─────────────┐ ┌─────────────┐
│ Slave-1 │ │ Slave-2 │ │ Slave-3 │
└─────────────┘ └─────────────┘ └─────────────┘
特点:
- ✅ 高可用(Master挂了,Slave继续提供读服务)
- ✅ 性能高(异步复制,不等待Slave确认)
- ❌ 可能丢失少量数据(Master挂了,Slave可能没同步到最新数据)
适用场景:
- 一般业务场景
- **最常用的架构!**⭐⭐⭐⭐⭐
4️⃣ 多Master多Slave模式(同步双写)🛡️
┌─────────────┐ ┌─────────────┐ ┌─────────────┐
│ Master-1 │ │ Master-2 │ │ Master-3 │
└──────┬──────┘ └──────┬──────┘ └──────┬──────┘
│ 同步复制 │ │
↓ (等待确认) ↓ ↓
┌─────────────┐ ┌─────────────┐ ┌─────────────┐
│ Slave-1 │ │ Slave-2 │ │ Slave-3 │
└─────────────┘ └─────────────┘ └─────────────┘
特点:
- ✅ 数据安全性极高(Master和Slave都有数据,不丢失)
- ✅ 高可用
- ❌ 性能较低(需要等待Slave确认)
- ❌ 延迟较高
适用场景:
- 金融、交易等对数据要求极高的场景
5️⃣ DLedger高可用集群 🚀(推荐)
特点:
- ✅ 自动故障转移(Master挂了,自动选举新Master)
- ✅ 强一致性(基于Raft协议)
- ✅ 不需要手动切换
- ✅ **RocketMQ 4.5+的推荐方案!**⭐⭐⭐⭐⭐
原理:
基于 Raft 协议:
- Leader(Master)
- Follower(Slave)
- Leader挂了,自动选举新Leader
📡 主从同步的方式
1️⃣ 异步复制(Async Replication)⚡
流程:
Producer发送消息
↓
Master接收消息
↓
Master写入磁盘(或PageCache)
↓
立即返回成功给Producer ✅ ← 不等Slave!
↓
后台异步复制到Slave ⏰
时序图:
Producer Master Slave
│ │ │
│ 发送消息 │ │
├──────────────>│ │
│ │ 写入 │
│ ├─────> │
│ 立即返回 │ │
│<──────────────┤ │
│ │ 异步复制 │
│ ├─────────────>│
│ │ │
配置:
# Master配置
brokerRole=ASYNC_MASTER
优点:
- ✅ 性能高(不等待Slave)
- ✅ 延迟低
缺点:
- ❌ Master挂了,Slave可能没有最新数据
- ❌ 可能丢失数据(异步复制间隔内的数据)
2️⃣ 同步双写(Sync Replication)🛡️
流程:
Producer发送消息
↓
Master接收消息
↓
Master写入磁盘
↓
Master等待Slave写入 ⏱️
↓
Slave写入成功 ✅
↓
Master返回成功给Producer ✅
时序图:
Producer Master Slave
│ │ │
│ 发送消息 │ │
├──────────────>│ │
│ │ 写入 │
│ ├─────> │
│ │ 同步复制 │
│ ├─────────────>│
│ │ │
│ │ 确认 │
│ │<─────────────┤
│ 返回成功 │ │
│<──────────────┤ │
│ │ │
配置:
# Master配置
brokerRole=SYNC_MASTER
优点:
- ✅ 数据安全性极高(Master和Slave都有)
- ✅ Master挂了,Slave有完整数据
缺点:
- ❌ 性能较低(需要等待Slave)
- ❌ 延迟较高
🎯 主从同步策略对比
| 特性 | 异步复制 | 同步双写 |
|---|---|---|
| 配置 | ASYNC_MASTER | SYNC_MASTER |
| 性能 | ⚡⚡⚡ 高 | 😐 中等 |
| 延迟 | <1ms | 5-10ms |
| 数据安全 | 😐 中等 | 🛡️🛡️🛡️ 极高 |
| Master宕机影响 | 可能丢失部分数据 | 不丢失数据 |
| 适用场景 | 一般业务 ⭐⭐⭐⭐⭐ | 金融、交易 ⭐⭐⭐ |
💻 主从同步的配置
Master配置
# ======================== Broker基本配置 ========================
brokerClusterName=DefaultCluster
brokerName=broker-a # ⭐ 主从的brokerName必须相同!
brokerId=0 # ⭐ 0表示Master,>0表示Slave
# ======================== 角色配置 ========================
# ⭐ ASYNC_MASTER: 异步主(推荐)
# ⭐ SYNC_MASTER: 同步主
# ⭐ SLAVE: 从
brokerRole=ASYNC_MASTER
# ======================== 刷盘配置 ========================
# ASYNC_FLUSH: 异步刷盘(推荐)
# SYNC_FLUSH: 同步刷盘
flushDiskType=ASYNC_FLUSH
# ======================== NameServer配置 ========================
namesrvAddr=localhost:9876
# ======================== 存储路径 ========================
storePathRootDir=/data/rocketmq/store
storePathCommitLog=/data/rocketmq/store/commitlog
# ======================== 网络配置 ========================
listenPort=10911
# ======================== 主从同步配置 ========================
# Slave从Master同步的最大offset差距(默认256MB)
# 超过这个差距,Slave会拒绝继续同步
haMasterAddress=
haListenPort=10912 # 主从同步监听端口
# ======================== 高可用配置 ========================
# 当Master不可用时,Slave是否可读(默认true)
slaveReadEnable=true
# 同步双写时,Slave确认的最大等待时间(毫秒)
syncFlushTimeout=5000
Slave配置
# ======================== Broker基本配置 ========================
brokerClusterName=DefaultCluster
brokerName=broker-a # ⭐ 必须和Master相同!
brokerId=1 # ⭐ >0表示Slave,建议从1开始递增
# ======================== 角色配置 ========================
brokerRole=SLAVE # ⭐ 从节点
# ======================== 刷盘配置 ========================
flushDiskType=ASYNC_FLUSH
# ======================== NameServer配置 ========================
namesrvAddr=localhost:9876
# ======================== 存储路径 ========================
storePathRootDir=/data/rocketmq/store-slave
storePathCommitLog=/data/rocketmq/store-slave/commitlog
# ======================== 网络配置 ========================
listenPort=11911 # 和Master不同的端口
# ======================== 主从同步配置 ========================
# ⭐ Slave主动连接Master的地址
haMasterAddress=192.168.1.100:10912 # Master的HA端口
完整的主从集群部署
架构:
NameServer-1 NameServer-2
│ │
┌─────┴─────────────────┴─────┐
│ │
Master-a Master-b
(broker-a) (broker-b)
10.0.1.100:10911 10.0.1.101:10911
│ │
│ │
Slave-a Slave-b
(broker-a-s) (broker-b-s)
10.0.1.102:11911 10.0.1.103:11911
Master-a配置(10.0.1.100):
brokerClusterName=DefaultCluster
brokerName=broker-a
brokerId=0
brokerRole=ASYNC_MASTER
flushDiskType=ASYNC_FLUSH
namesrvAddr=10.0.1.10:9876;10.0.1.11:9876
listenPort=10911
haListenPort=10912
storePathRootDir=/data/rocketmq/store
Slave-a配置(10.0.1.102):
brokerClusterName=DefaultCluster
brokerName=broker-a # ⭐ 和Master-a相同
brokerId=1
brokerRole=SLAVE
flushDiskType=ASYNC_FLUSH
namesrvAddr=10.0.1.10:9876;10.0.1.11:9876
listenPort=11911
haMasterAddress=10.0.1.100:10912 # ⭐ 指向Master-a
storePathRootDir=/data/rocketmq/store-slave
Master-b和Slave-b配置类似
🔍 主从同步的底层原理
HAService(High Availability Service)
核心组件:
Master端:
├─ AcceptSocketService: 监听Slave连接
├─ HAConnection: 处理每个Slave连接
│ ├─ ReadSocketService: 读取Slave的同步请求
│ └─ WriteSocketService: 向Slave发送数据
└─ GroupTransferService: 处理同步双写的ACK
Slave端:
└─ HAClient: 连接Master,拉取数据
同步流程(详细)
异步复制流程:
1️⃣ Slave启动
Slave.HAClient.start()
↓
连接Master的HA端口(10912)
↓
发送当前的offset: "我已经同步到offset=12345"
2️⃣ Master接收连接
Master.AcceptSocketService接收连接
↓
创建HAConnection
↓
ReadSocketService读取Slave的offset
3️⃣ Master发送数据
WriteSocketService从CommitLog读取数据(从offset=12345开始)
↓
发送数据给Slave
4️⃣ Slave接收数据
HAClient接收数据
↓
写入本地CommitLog
↓
更新offset: "我现在同步到offset=12500了"
↓
发送新的offset给Master
5️⃣ 循环重复3-4步
图示:
Master Slave
│ │
│<──────── 连接请求 ────────────────┤
│ 连接成功 │
├─────────────────────────────────>│
│ │
│<──────── offset=12345 ────────────┤
│ "我已经同步到这里了" │
│ │
│─────── 数据(12345-12500) ────────>│
│ "这是新数据" │
│ │
│<──────── offset=12500 ────────────┤
│ "我同步完了,现在到12500" │
│ │
│─────── 数据(12500-12600) ────────>│
│ │
同步双写流程:
1️⃣ Producer发送消息到Master
2️⃣ Master写入CommitLog
3️⃣ Master等待Slave确认
GroupTransferService.waitForSlave(offset)
↓
等待Slave同步到这个offset
↓
超时时间:syncFlushTimeout=5000ms
4️⃣ Slave同步数据并确认
Slave写入CommitLog
↓
发送offset给Master: "我同步到offset=12500了"
5️⃣ Master收到确认
GroupTransferService: "Slave已确认到offset=12500"
↓
返回成功给Producer
6️⃣ 如果超时
超过5秒Slave还没确认
↓
Master仍然返回成功(不阻塞Producer)
↓
但会记录日志:WARN "sync to slave timeout"
Offset管理
Master维护每个Slave的同步进度:
// Master端记录
Map<String, Long> slaveOffsets = new HashMap<>();
// Slave-1同步到100万
slaveOffsets.put("192.168.1.101:11911", 1000000L);
// Slave-2同步到98万(落后了)
slaveOffsets.put("192.168.1.102:11911", 980000L);
// Master当前最大offset: 102万
long masterOffset = 1020000L;
// Slave-1落后2万
// Slave-2落后4万
查询同步进度:
# 查看主从同步差距
sh mqadmin brokerStatus -b 127.0.0.1:10911
# 输出:
Master Offset: 1020000
Slave-1 Offset: 1000000 (diff: 20000)
Slave-2 Offset: 980000 (diff: 40000)
🎯 刷盘和主从同步的组合
四种组合方案
方案1:异步刷盘 + 异步复制 ⚡⚡⚡
Producer → Master(写入PageCache)→ 立即返回 ✅
↓ ↓
异步刷盘 异步复制到Slave
特点:
- ⚡⚡⚡ 性能最高
- 😐 数据安全性一般
- ❌ 可能丢失数据:
- Master宕机 + PageCache未刷盘 = 丢失
- Master宕机 + Slave未同步 = 丢失
适用场景:
- 日志收集、监控数据
- 对数据丢失不敏感的场景
- 默认配置!
性能:TPS 40000-50000
方案2:同步刷盘 + 异步复制 🛡️⚡
Producer → Master(写入磁盘)→ 等待刷盘完成 ⏱️ → 返回 ✅
↓ ↓
同步刷盘 异步复制到Slave
特点:
- 🛡️ Master不会丢数据(已刷盘)
- ⚡ 性能中等(等刷盘,但不等复制)
- ❌ Master宕机,Slave可能没有最新数据
适用场景:
- 重要业务,但可以接受Master宕机后的短暂不可用
性能:TPS 8000-10000
方案3:异步刷盘 + 同步双写 ⚡🛡️
Producer → Master(写入PageCache)→ 等待Slave确认 ⏱️ → 返回 ✅
↓ ↓
异步刷盘 同步复制到Slave
特点:
- 🛡️ Master和Slave至少有一个有数据(Slave已确认)
- ⚡ 性能中等(不等刷盘,但等复制)
- ❌ 如果Master和Slave都没刷盘就都宕机,还是会丢数据(概率极低)
适用场景:
- 对可用性要求高的场景
- 可以接受一定的性能损失
性能:TPS 10000-15000
方案4:同步刷盘 + 同步双写 🛡️🛡️🛡️
Producer → Master(写入磁盘)→ 等待刷盘和Slave确认 ⏱️⏱️ → 返回 ✅
↓ ↓
同步刷盘 同步复制到Slave(也刷盘)
特点:
- 🛡️🛡️🛡️ 数据安全性最高
- Master刷盘 + Slave刷盘 = 双保险
- 🐌 性能最低(等两个IO操作)
- ✅ Master和Slave都宕机也不丢数据
适用场景:
- 金融、交易、订单等对数据要求极高的场景
- 可靠性优先!
性能:TPS 3000-5000
🎯 组合方案对比
| 组合方案 | 刷盘 | 复制 | 性能(TPS) | 数据安全 | 适用场景 |
|---|---|---|---|---|---|
| 方案1 | 异步 | 异步 | 40000-50000 ⚡⚡⚡ | 😐 | 日志、监控(默认)⭐⭐⭐⭐⭐ |
| 方案2 | 同步 | 异步 | 8000-10000 😐 | 🛡️🛡️ | 重要业务 ⭐⭐⭐ |
| 方案3 | 异步 | 同步 | 10000-15000 ⚡ | 🛡️🛡️ | 高可用场景 ⭐⭐⭐⭐ |
| 方案4 | 同步 | 同步 | 3000-5000 🐌 | 🛡️🛡️🛡️ | 金融交易 ⭐⭐⭐ |
💻 配置示例
方案1:异步刷盘 + 异步复制(默认)
# Master
brokerRole=ASYNC_MASTER
flushDiskType=ASYNC_FLUSH
# Slave
brokerRole=SLAVE
flushDiskType=ASYNC_FLUSH
方案2:同步刷盘 + 异步复制
# Master
brokerRole=ASYNC_MASTER
flushDiskType=SYNC_FLUSH # ⭐ 同步刷盘
# Slave
brokerRole=SLAVE
flushDiskType=ASYNC_FLUSH
方案3:异步刷盘 + 同步双写
# Master
brokerRole=SYNC_MASTER # ⭐ 同步主
flushDiskType=ASYNC_FLUSH
# Slave
brokerRole=SLAVE
flushDiskType=ASYNC_FLUSH
方案4:同步刷盘 + 同步双写
# Master
brokerRole=SYNC_MASTER # ⭐ 同步主
flushDiskType=SYNC_FLUSH # ⭐ 同步刷盘
# Slave
brokerRole=SLAVE
flushDiskType=SYNC_FLUSH # ⭐ Slave也同步刷盘
🎓 面试题速答
Q1: RocketMQ的刷盘方式有哪些?
A: 两种!
- 同步刷盘(SYNC_FLUSH):消息写入磁盘后才返回成功
- 安全但慢,TPS 1000-3000
- 异步刷盘(ASYNC_FLUSH):消息写入内存后立即返回,后台异步刷盘
- 快但可能丢数据,TPS 10000-50000
- 默认配置!
Q2: 什么是PageCache?
A: PageCache = Linux操作系统的文件缓存
RocketMQ的写入流程:
写入PageCache(内存)→ 快!
→ 操作系统异步刷盘 → 慢
好处:
- 减少磁盘IO
- 提高性能
- 操作系统自动管理
Q3: 主从同步的方式有哪些?
A: 两种!
- 异步复制(ASYNC_MASTER):Master不等Slave确认就返回成功
- 性能高,但Master宕机可能丢数据
- 同步双写(SYNC_MASTER):Master等待Slave确认后才返回成功
- 安全但慢,Master宕机不丢数据
Q4: 如何选择刷盘和主从同步策略?
A: 根据业务场景选择!
高性能场景(日志、监控):
异步刷盘 + 异步复制
TPS: 40000+
一般业务场景:
异步刷盘 + 同步双写
或
同步刷盘 + 异步复制
TPS: 8000-15000
高可靠场景(金融、交易):
同步刷盘 + 同步双写
TPS: 3000-5000
Q5: Master宕机后Slave能写入吗?
A: 不能!
Slave只能提供读服务,不能写入
解决方案:
- 手动切换:把Slave提升为Master(需要重启)
- DLedger模式:自动故障转移(推荐)
Q6: 同步双写的超时时间是多少?
A:
syncFlushTimeout=5000 # 默认5秒
超时后的行为:
- Master仍然返回成功(不阻塞Producer)
- 记录WARN日志:"sync to slave timeout"
- Slave继续异步同步
🎯 最佳实践总结
生产环境配置建议 ✅
一般业务场景:
# Master
brokerRole=ASYNC_MASTER # 异步主
flushDiskType=ASYNC_FLUSH # 异步刷盘
flushIntervalCommitLog=500 # 500ms刷一次
# Slave
brokerRole=SLAVE
flushDiskType=ASYNC_FLUSH
haMasterAddress=<master-ip>:10912
高可靠场景:
# Master
brokerRole=SYNC_MASTER # 同步主
flushDiskType=SYNC_FLUSH # 同步刷盘
syncFlushTimeout=5000
# Slave
brokerRole=SLAVE
flushDiskType=SYNC_FLUSH # Slave也同步刷盘
haMasterAddress=<master-ip>:10912
监控指标 📊
刷盘监控:
1. 刷盘延迟:commitLogDiskRatio
2. 刷盘队列大小:flushBehindBytes
3. 刷盘失败次数:flushFailedTimes
主从同步监控:
1. 主从差距:slaveFallBehindMasterHow
2. 同步延迟:haTransferredBytes
3. 同步失败次数:haConnectionCount
告警阈值:
# 主从差距超过100MB告警
slaveFallBehindMasterHow > 104857600
# 刷盘队列超过10MB告警
flushBehindBytes > 10485760
优化建议 ⚙️
磁盘优化:
1. 使用SSD(比HDD快10倍以上)
2. 使用RAID10(兼顾性能和安全)
3. 禁用磁盘缓存的write-back(防止断电丢数据)
4. 调整磁盘调度算法:deadline或noop
网络优化:
1. 主从之间使用万兆网卡
2. 减少网络跳数
3. 监控网络延迟
OS优化:
# 调整文件描述符限制
ulimit -n 655350
# 禁用swap
swapoff -a
# 调整vm参数
vm.overcommit_memory=1
vm.swappiness=10
vm.dirty_ratio=40
vm.dirty_background_ratio=10
🎬 总结:一张图看懂刷盘和主从同步
RocketMQ刷盘和主从同步全景图
┌──────────────────────────────────────────────────────┐
│ Producer │
└─────────────────────┬────────────────────────────────┘
│ 发送消息
↓
┌──────────────────────────────────────────────────────┐
│ Master Broker │
│ │
│ 1. 接收消息 │
│ 2. 写入PageCache(内存) │
│ │
│ ┌────────────────┐ ┌────────────────┐ │
│ │ 同步刷盘? │ │ 同步复制? │ │
│ │ ├─ YES: 等刷盘 │ │ ├─ YES: 等Slave│ │
│ │ └─ NO: 不等 │ │ └─ NO: 不等 │ │
│ └────────────────┘ └────────────────┘ │
│ ↓ ↓ │
│ ┌────────────┐ ┌────────────┐ │
│ │ 刷盘到磁盘 │ │ 发送到Slave │ │
│ │ (异步/同步) │ │ (异步/同步) │ │
│ └────────────┘ └────────────┘ │
│ ↓ │
└───────────────────────────────────┼──────────────────┘
│ HA同步
↓
┌──────────────────────────────────────────────────────┐
│ Slave Broker │
│ │
│ 1. 接收同步数据 │
│ 2. 写入PageCache │
│ 3. 刷盘到磁盘(异步/同步) │
│ 4. 确认给Master(如果是同步复制) │
│ │
└──────────────────────────────────────────────────────┘
四种组合策略:
异步刷盘+异步复制: ⚡⚡⚡ 最快,一般安全
同步刷盘+异步复制: 🛡️⚡ Master安全,Slave可能丢
异步刷盘+同步复制: ⚡🛡️ 高可用,性能中等
同步刷盘+同步复制: 🛡️🛡️🛡️ 最安全,较慢
🎉 恭喜你!
你已经完全掌握了RocketMQ的刷盘机制和主从同步!🎊
核心要点:
- 刷盘:同步刷盘安全但慢,异步刷盘快但可能丢数据
- 主从同步:异步复制快但可能丢,同步双写安全但慢
- 组合策略:根据业务场景选择合适的组合
下次面试,这样回答:
"RocketMQ有两种刷盘方式:同步刷盘和异步刷盘。同步刷盘是消息写入磁盘后才返回成功,性能较低但数据安全;异步刷盘是写入内存后立即返回,后台异步刷盘,性能高但可能丢失500ms内的数据。
主从同步也有两种:异步复制和同步双写。异步复制是Master不等Slave确认就返回,性能高但Master宕机可能丢数据;同步双写是Master等Slave确认后才返回,Master宕机不丢数据。
生产环境一般使用异步刷盘+异步复制,性能最高。金融场景使用同步刷盘+同步双写,数据最安全。我们项目中根据不同业务使用不同的组合策略。"
面试官:👍 "回答得很专业!你对RocketMQ底层原理理解很深!"
🎈 表情包时间 🎈
学完RocketMQ刷盘和主从同步后:
之前:
😵 "刷盘?主从?啥意思?"
现在:
😎 "同步刷盘+同步双写,数据绝对安全!"
面试官:
😲 "这小子对底层原理很熟啊!"
本文完 🎬
记得点赞👍 收藏⭐ 分享🔗
上一篇: 185-RocketMQ的顺序消息和事务消息实现.md
下一篇: 187-如何保证消息的幂等消费.md
作者注:写完这篇,感觉可以去RocketMQ源码组了!📮
如果这篇文章对你有帮助,请给我一个Star⭐!版权声明:本文采用 CC BY-NC-SA 4.0 许可协议,转载请注明出处。