💾 RocketMQ的刷盘机制和主从同步:数据安全的双保险!

46 阅读18分钟

📖 开场:日记本的故事

想象你在写日记 📔:

方案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_FLUSHASYNC_FLUSH(默认)
性能(TPS)1000-300010000-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_MASTERSYNC_MASTER
性能⚡⚡⚡ 高😐 中等
延迟<1ms5-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: 两种!

  1. 同步刷盘(SYNC_FLUSH):消息写入磁盘后才返回成功
    • 安全但慢,TPS 1000-3000
  2. 异步刷盘(ASYNC_FLUSH):消息写入内存后立即返回,后台异步刷盘
    • 快但可能丢数据,TPS 10000-50000
    • 默认配置!

Q2: 什么是PageCache?

A: PageCache = Linux操作系统的文件缓存

RocketMQ的写入流程:

写入PageCache(内存)→ 快!
→ 操作系统异步刷盘 → 慢

好处

  • 减少磁盘IO
  • 提高性能
  • 操作系统自动管理

Q3: 主从同步的方式有哪些?

A: 两种!

  1. 异步复制(ASYNC_MASTER):Master不等Slave确认就返回成功
    • 性能高,但Master宕机可能丢数据
  2. 同步双写(SYNC_MASTER):Master等待Slave确认后才返回成功
    • 安全但慢,Master宕机不丢数据

Q4: 如何选择刷盘和主从同步策略?

A: 根据业务场景选择!

高性能场景(日志、监控):

异步刷盘 + 异步复制
TPS: 40000+

一般业务场景

异步刷盘 + 同步双写

同步刷盘 + 异步复制
TPS: 8000-15000

高可靠场景(金融、交易):

同步刷盘 + 同步双写
TPS: 3000-5000

Q5: Master宕机后Slave能写入吗?

A: 不能!

Slave只能提供读服务,不能写入

解决方案

  1. 手动切换:把Slave提升为Master(需要重启)
  2. 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的刷盘机制和主从同步!🎊

核心要点

  1. 刷盘:同步刷盘安全但慢,异步刷盘快但可能丢数据
  2. 主从同步:异步复制快但可能丢,同步双写安全但慢
  3. 组合策略:根据业务场景选择合适的组合

下次面试,这样回答

"RocketMQ有两种刷盘方式:同步刷盘和异步刷盘。同步刷盘是消息写入磁盘后才返回成功,性能较低但数据安全;异步刷盘是写入内存后立即返回,后台异步刷盘,性能高但可能丢失500ms内的数据。

主从同步也有两种:异步复制和同步双写。异步复制是Master不等Slave确认就返回,性能高但Master宕机可能丢数据;同步双写是Master等Slave确认后才返回,Master宕机不丢数据。

生产环境一般使用异步刷盘+异步复制,性能最高。金融场景使用同步刷盘+同步双写,数据最安全。我们项目中根据不同业务使用不同的组合策略。"

面试官:👍 "回答得很专业!你对RocketMQ底层原理理解很深!"


🎈 表情包时间 🎈

       学完RocketMQ刷盘和主从同步后:

              之前:
         😵 "刷盘?主从?啥意思?"
         
              现在:
         😎 "同步刷盘+同步双写,数据绝对安全!"
         
              面试官:
         😲 "这小子对底层原理很熟啊!"

本文完 🎬

记得点赞👍 收藏⭐ 分享🔗

上一篇: 185-RocketMQ的顺序消息和事务消息实现.md
下一篇: 187-如何保证消息的幂等消费.md


作者注:写完这篇,感觉可以去RocketMQ源码组了!📮
如果这篇文章对你有帮助,请给我一个Star⭐!

版权声明:本文采用 CC BY-NC-SA 4.0 许可协议,转载请注明出处。