Kafka Producer 发送慢 → TPS 骤降 90%

🐢 当"强一致性"遇上"猪队友",Producer 直接摆烂

一、现象:生产界的"心脏骤停"

表象是什么
复制代码
时间线:
00:00  TPS = 10000  😊 风生水起
00:05  TPS = 5000   😐 有点不对劲
00:10  TPS = 1000   😱 老板在看我
00:15  TPS = 100    💀 准备写检讨
00:30  TPS = 0      🪦 系统已凉凉
深渊在凝视
复制代码
# Producer 日志(循环播放的绝望)
[WARN] Expiring 10000 records for topic-0 due to timeout
[WARN] Expiring 10000 records for topic-0 due to timeout
[WARN] Expiring 10000 records for topic-0 due to timeout
...
[ERROR] Failed to send message: TimeoutException after 120000ms
[ERROR] Failed to send message: TimeoutException after 120000ms
[ERROR] Failed to send message: TimeoutException after 120000ms

"我发了 10000 条消息,等回复等到花儿都谢了"
"算了,不等了,直接丢弃"
"又发了 10000 条,又等不到回复"
"又丢了..."
"这班谁爱上谁上吧"
监控指标"集体装死"
指标 正常值 故障值 状态
record-send-rate 10000/s 0/s ❌ 归零
request-latency-avg 10ms 120000ms ❌ 超时
record-error-rate 0 100% ❌ 全错
Broker CPU 30% 30% ✅ 正常
Broker 网络 500MB/s 500MB/s ✅ 正常
Broker 磁盘 60% 60% ✅ 正常
现代诡异录
复制代码
Producer: "我发不出去!"
Broker: "我挺好啊,CPU 网络都正常"
网络: "我也挺好啊,带宽够用"
那到底是谁的问题?🤔

二、根因:从底层协议扒起

2.1 Producer 发送流程"五步走"
复制代码
┌─────────────────────────────────────────────────────────────┐
│              Producer 发送消息完整流程                       │
├─────────────────────────────────────────────────────────────┤
│                                                             │
│  Step 1: 消息序列化                                         │
│  Record → byte[] (快速,微秒级)                             │
│                                                             │
│  Step 2: 分区选择                                           │
│  根据 key/hash 决定发送到哪个 partition (快速,微秒级)       │
│                                                             │
│  Step 3: 放入 RecordAccumulator (发送缓冲区)                 │
│  等待批处理 (可配置 linger.ms,默认 0)                       │
│                                                             │
│  Step 4: Sender 线程批量发送                                │
│  通过网络发送到 Broker (毫秒级)                              │
│                                                             │
│  Step 5: 等待 Broker 确认 (acks 配置决定) ⭐ 问题出在这里!  │
│                                                             │
│  acks=0: 不等待确认,发完就跑                               │
│  acks=1: 等待 Leader 确认                                   │
│  acks=all: 等待所有 ISR 副本确认 ⚠️ 本次主角                 │
│                                                             │
└─────────────────────────────────────────────────────────────┘
2.2 acks=all 的"死亡陷阱"
复制代码
┌─────────────────────────────────────────────────────────────┐
│              acks=all 确认机制详解                           │
├─────────────────────────────────────────────────────────────┤
│                                                             │
│  配置:acks=all + min.insync.replicas=2                     │
│                                                             │
│  正常情况:                                                 │
│  Producer → Leader → Follower1 ✓ → Follower2 ✓ → ACK       │
│           (收到 2 个 ISR 确认,返回成功)                     │
│                                                             │
│  故障情况:                                                 │
│  Producer → Leader → Follower1 ✓ → Follower2 ❌ (同步慢)   │
│           ↓                                                │
│           ISR 收缩 = [Leader, Follower1] (只有 1 个?不对!) │
│           ↓                                                │
│           等等... Leader 自己也算 ISR 成员!                 │
│           实际 ISR = [Leader] (只有 1 个)                    │
│           ↓                                                │
│           min.insync.replicas=2 > ISR=1 ❌ 不满足条件!      │
│           ↓                                                │
│           Leader 拒绝写入!Producer 无限等待!               │
│           ↓                                                │
│           直到 delivery.timeout.ms 超时,消息被丢弃          │
│                                                             │
└─────────────────────────────────────────────────────────────┘
2.3 ISR(In-Sync Replicas)机制详解
复制代码
┌─────────────────────────────────────────────────────────────┐
│                    ISR 动态收缩机制                          │
├─────────────────────────────────────────────────────────────┤
│                                                             │
│  初始状态:                                                 │
│  Partition: topic-0-0                                       │
│  Replicas: [Broker1, Broker2, Broker3] (3 个副本)           │
│  ISR: [Broker1, Broker2, Broker3] (3 个都在同步)            │
│  Leader: Broker1                                            │
│                                                             │
│  Follower2 同步变慢(网络抖动/磁盘慢):                     │
│  replica.lag.time.max.ms = 30000 (30 秒)                    │
│  ↓                                                          │
│  Follower2 超过 30 秒没追上 Leader                           │
│  ↓                                                          │
│  ISR 收缩:[Broker1, Broker3] (Follower2 被踢出)            │
│                                                             │
│  Follower3 也同步变慢:                                     │
│  ↓                                                          │
│  ISR 收缩:[Broker1] (只剩 Leader 自己!) ⚠️ 危险!          │
│                                                             │
│  此时 min.insync.replicas=2:                                │
│  ISR 数量 (1) < min.insync.replicas (2)                     │
│  ↓                                                          │
│  Leader 拒绝写入任何消息!                                  │
│  ↓                                                          │
│  Producer 等待 ack 超时 → 消息丢弃                          │
│                                                             │
└─────────────────────────────────────────────────────────────┘
2.4 超时配置"三重门"
复制代码
// Producer 有三个关键超时配置,层层嵌套
┌─────────────────────────────────────────────────────────────┐
│                    超时配置层级关系                          │
├─────────────────────────────────────────────────────────────┤
│                                                             │
│  第一层:request.timeout.ms (默认 30 秒)                     │
│  ├─ 含义:单次请求等待 Broker 响应的超时时间                │
│  ├─ 触发:发送一批消息后,等待 ack 的时间                   │
│  └─ 超时后:重试(如果 retries > 0)                        │
│                                                             │
│  第二层:delivery.timeout.ms (默认 120 秒) ⭐                │
│  ├─ 含义:消息从发送到最终成功/失败的总时间                 │
│  ├─ 触发:包含所有重试时间的总和                            │
│  └─ 超时后:消息被丢弃,返回 TimeoutException               │
│                                                             │
│  第三层:max.block.ms (默认 60 秒)                          │
│  ├─ 含义:send() 方法阻塞等待缓冲区的最大时间               │
│  ├─ 触发:RecordAccumulator 满了,等待空间释放              │
│  └─ 超时后:抛出 TimeoutException                           │
│                                                             │
│  关系:delivery.timeout.ms >= request.timeout.ms            │
│                                                             │
└─────────────────────────────────────────────────────────────┘
2.5 故障时间线"死亡螺旋"
复制代码
┌──────────────────────────────────────────────────────────────┐
│                    故障时间线完整还原                        │
├──────────────────────────────────────────────────────────────┤
│                                                              │
│  T0      Follower2 磁盘变慢(可能是 GC/其他租户干扰)         │
│  T0+5s   Follower2 开始 lag,落后 Leader 5 秒                 │
│  T0+30s  Follower2 lag > replica.lag.time.max.ms (30 秒)     │
│  T0+31s  ISR 收缩,Follower2 被踢出 ISR                       │
│  T0+35s  Follower3 也开始 lag(同一块磁盘?)                 │
│  T0+65s  ISR 收缩,只剩 [Leader] (1 个副本)                   │
│                                                              │
│  ⚠️ 此时 min.insync.replicas=2 > ISR=1,Leader 拒绝写入!    │
│                                                              │
│  T1      Producer 发送消息 batch-1                           │
│  T1+30s  request.timeout.ms 超时,触发重试                    │
│  T1+60s  重试再次超时                                        │
│  T1+90s  重试再次超时                                        │
│  T1+120s delivery.timeout.ms 超时,消息被丢弃                │
│          Producer 日志:Expiring 10000 records               │
│                                                              │
│  T2      Producer 发送消息 batch-2                           │
│  T2+120s 同样被丢弃                                          │
│  ...                                                         │
│                                                              │
│  💀 结果:TPS 从 10000 跌到 0,消息大量丢失                   │
│                                                              │
└──────────────────────────────────────────────────────────────┘
2.6 为什么 Broker 监控"一切正常"?
复制代码
┌─────────────────────────────────────────────────────────────┐
│              Broker 监控"假正常"之谜                         │
├─────────────────────────────────────────────────────────────┤
│                                                             │
│  CPU 正常 ✅   → 因为 Leader 根本没在处理写入请求            │
│                请求在"等待 ISR 满足条件"阶段就被阻塞了       │
│                不是"忙不过来",是"不让写"                    │
│                                                             │
│  网络正常 ✅   → 因为网络带宽确实够用                        │
│                问题是协议层面的拒绝,不是网络层面的阻塞      │
│                                                             │
│  磁盘正常 ✅   → 因为磁盘确实没写满                          │
│                问题是 ISR 数量不够,不是磁盘空间不够         │
│                                                             │
│  类比:你去银行办业务                                       │
│        - 银行大厅很空(CPU 正常)                           │
│        - 网络信号很好(网络正常)                           │
│        - 但窗口说"需要 2 个柜员签字,但只有 1 个在岗"         │
│        - 你就一直等,等到超时                               │
│        - 最后业务没办成,还被赶出去了                       │
│                                                             │
└─────────────────────────────────────────────────────────────┘

三、急救方案(从"止血"到"治本")

3.1 方案一:降级 acks 配置(最快止血)
复制代码
//  这是"断臂求生"方案,能恢复 TPS 但牺牲一致性
Properties props = new Properties();
props.put("bootstrap.servers", "localhost:9092");
props.put("acks", "1");  // 从 all 降级为 1

//  优点:
// 1. 只等待 Leader 确认,不等待 Follower
// 2. TPS 立刻恢复
// 3. 不需要重启 Broker

//  风险:
// 1. Leader 宕机可能丢失数据(Follower 可能没同步)
// 2. 数据一致性从"强"降级为"弱"
// 3. 金融/订单场景慎用!

// 💡 适用场景:
// - 日志类数据,允许少量丢失
// - 紧急恢复业务,后续再修复
// - 非核心业务
3.2 方案二:紧急修复 ISR(治本方案)
复制代码
# 第一步:查看 ISR 状态
bin/kafka-topics.sh --bootstrap-server localhost:9092 \
    --describe --topic hot-topic

# 输出示例:
# Topic: hot-topic  Partition: 0  Leader: 1  Replicas: 1,2,3  ISR: 1
#                                                    ↑
#                                              只有 Leader 自己!

# 第二步:查看 Follower lag 情况
bin/kafka-log-dirs.sh --bootstrap-server localhost:9092 \
    --describe --topic hot-topic

# 第三步:检查 Follower Broker 状态
# 登录 Broker2 和 Broker3,检查:
# 1. 磁盘 IO:iostat -x 1
# 2. 网络:iftop
# 3. GC 日志:grep "Full GC" broker.log
# 4. 磁盘空间:df -h

# 第四步:修复 Follower
# 如果是磁盘满:扩容或清理
# 如果是网络问题:修复网络
# 如果是 Broker 宕机:重启 Broker

# 第五步:等待 ISR 恢复
# 监控 ISR 数量,等待恢复到 2 个以上
watch -n 1 'kafka-topics.sh --bootstrap-server localhost:9092 \
    --describe --topic hot-topic | grep ISR'
3.3 方案三:临时调整 min.insync.replicas(折中方案)
复制代码
# 临时降低 min.insync.replicas 要求
bin/kafka-configs.sh --bootstrap-server localhost:9092 \
    --entity-type topics \
    --entity-name hot-topic \
    --alter \
    --add-config min.insync.replicas=1

# ✅ 优点:
# 1. 不需要改 Producer 配置
# 2. 恢复写入能力
# 3. 可动态调整,无需重启

# ❌ 风险:
# 1. 数据可靠性降低
# 2. 只有一个副本确认,可能丢失

# 💡 建议:
# 1. 作为临时应急方案
# 2. ISR 恢复后立刻调回 2
# 3. 配合监控告警使用
3.4 方案四:增加超时时间(延缓问题)
复制代码
// 这是"止痛药"方案,不能治本但能争取时间

Properties props = new Properties();
props.put("delivery.timeout.ms", "300000");  // 从 120 秒 调到 300 秒
props.put("request.timeout.ms", "60000");    // 从 30 秒 调到 60 秒
props.put("retries", "5");                   // 增加重试次数

//  优点:
// 1. 给 ISR 恢复争取更多时间
// 2. 减少消息丢弃
// 3. 不需要改 Broker 配置

//  风险:
// 1. Producer 阻塞时间变长
// 2. 内存占用增加(缓冲区积压)
// 3. 如果 ISR 一直不恢复,最终还是会超时

// 💡 建议:
// 1. 配合 ISR 修复一起使用
// 2. 监控 Producer 内存使用
// 3. 设置上限,避免无限等待
3.5 方案五:死信队列兜底(最后一道防线)
复制代码
// 这是"保险"方案,确保消息不丢失

public class ReliableProducer {
    
    private final KafkaProducer<String, String> producer;
    private final DeadLetterQueue dlq;
    
    public void sendWithFallback(ProducerRecord<String, String> record) {
        try {
            producer.send(record, (metadata, exception) -> {
                if (exception != null) {
                    // 发送失败,写入死信队列
                    log.error("发送失败,写入死信队列", exception);
                    dlq.send(record, exception);
                }
            }).get(120, TimeUnit.SECONDS);
        } catch (TimeoutException e) {
            // 超时,也写入死信队列
            log.error("发送超时,写入死信队列", e);
            dlq.send(record, e);
        } catch (Exception e) {
            log.error("发送异常", e);
            dlq.send(record, e);
        }
    }
}

// 死信队列实现(可以用另一个 Kafka Topic)
public class DeadLetterQueue {
    
    private final KafkaProducer<String, String> dlqProducer;
    private static final String DLQ_TOPIC = "dead-letter-queue";
    
    public void send(ProducerRecord<String, String> original, Exception e) {
        ProducerRecord<String, String> dlqRecord = new ProducerRecord<>(
            DLQ_TOPIC,
            original.key(),
            buildDlqMessage(original, e)
        );
        dlqProducer.send(dlqRecord);
    }
    
    private String buildDlqMessage(ProducerRecord<String, String> original, Exception e) {
        return String.format(
            "{\"originalTopic\":\"%s\",\"originalKey\":\"%s\",\"originalValue\":\"%s\",\"error\":\"%s\",\"timestamp\":%d}",
            original.topic(),
            original.key(),
            original.value(),
            e.getMessage(),
            System.currentTimeMillis()
        );
    }
}

四、预防方案(监控 + 配置 + 架构)

4.1 关键配置"黄金组合"
复制代码
# Producer 推荐配置

# ========== 确认机制 ==========
acks=all                        # 强一致性(核心业务)
# acks=1                        # 高性能(日志类业务)

# ========== 超时配置 ==========
delivery.timeout.ms=120000      # 消息总超时 2 分钟
request.timeout.ms=30000        # 单次请求超时 30 秒
max.block.ms=60000              # send() 阻塞最多 60 秒

# ========== 重试配置 ==========
retries=3                       # 重试 3 次
retry.backoff.ms=1000           # 重试间隔 1 秒

# ========== 批量配置 ==========
linger.ms=5                     # 等待 5ms 凑批
batch.size=16384                # 每批最大 16KB
compression.type=lz4            # 压缩减少网络传输

# ========== 缓冲配置 ==========
buffer.memory=33554432          # 32MB 发送缓冲区
max.in.flight.requests.per.connection=5  # 最多 5 个未确认请求
4.2 Broker 关键配置
复制代码
# Broker 推荐配置

# ========== ISR 相关 ==========
min.insync.replicas=2           # 最小同步副本数
replica.lag.time.max.ms=30000   # Follower lag 超过 30 秒踢出 ISR
unclean.leader.election.enable=false  # 禁止非 ISR 副本成为 Leader

# ========== 副本同步 ==========
num.replica.fetchers=2          # 副本拉取线程数
replica.fetch.wait.max.ms=500   # 副本拉取等待时间
replica.fetch.min.bytes=1       # 有数据就拉取
replica.fetch.max.bytes=10485760  # 单次拉取最大 10MB
4.3 监控指标"必备清单"
复制代码
# Prometheus 监控配置

groups:
  - name: kafka-producer-alerts
    rules:
      # Producer 发送失败率
      - alert: KafkaProducerErrorRateHigh
        expr: rate(kafka_producer_record_error_total[5m]) > 0.01
        for: 5m
        labels:
          severity: critical
        annotations:
          summary: "Producer 发送错误率超过 1%"
          
      # Producer 发送延迟
      - alert: KafkaProducerRequestLatencyHigh
        expr: kafka_producer_request_latency_avg > 10000
        for: 5m
        labels:
          severity: warning
        annotations:
          summary: "Producer 请求延迟超过 10 秒"
          
      # ISR 收缩告警
      - alert: KafkaISRShrunk
        expr: kafka_server_replicamanager_isrshrinkspersec > 0
        for: 1m
        labels:
          severity: warning
        annotations:
          summary: "ISR 发生收缩,检查 Follower 状态"
          
      # UnderReplicated 分区
      - alert: KafkaUnderReplicatedPartitions
        expr: kafka_server_underreplicatedpartitions > 0
        for: 5m
        labels:
          severity: critical
        annotations:
          summary: "存在未同步的分区"
4.4 关键 JMX 指标解读
复制代码
// Producer 关键 JMX 指标

ObjectName objectName = new ObjectName(
    "kafka.producer:type=producer-metrics,client-id=*"
);

MBeanServer mBeanServer = ManagementFactory.getPlatformMBeanServer();

// 1. 消息发送速率
Attribute sendRate = mBeanServer.getAttribute(
    objectName,
    "record-send-rate"
);

// 2. 消息错误率
Attribute errorRate = mBeanServer.getAttribute(
    objectName,
    "record-error-rate"
);

// 3. 请求延迟
Attribute requestLatency = mBeanServer.getAttribute(
    objectName,
    "request-latency-avg"
);

// 4. 缓冲区使用情况
Attribute bufferAvailable = mBeanServer.getAttribute(
    objectName,
    "bufferpool-wait-time-total"
);

// 5. 压缩率
Attribute compressionRate = mBeanServer.getAttribute(
    objectName,
    "compression-rate-avg"
);
4.5 健康检查脚本
复制代码
#!/bin/bash
# kafka-producer-health-check.sh

BOOTSTRAP_SERVER="localhost:9092"
TOPIC="hot-topic"

echo "=== 检查 Topic ISR 状态 ==="
kafka-topics.sh --bootstrap-server $BOOTSTRAP_SERVER \
    --describe --topic $TOPIC | grep -E "ISR|Replicas"

echo ""
echo "=== 检查 UnderReplicated 分区 ==="
UNDER_REP=$(kafka-topics.sh --bootstrap-server $BOOTSTRAP_SERVER \
    --describe --under-replicated-partitions | wc -l)

if [ $UNDER_REP -gt 1 ]; then
    echo "发现 $UNDER_REP 个未同步分区"
    kafka-topics.sh --bootstrap-server $BOOTSTRAP_SERVER \
        --describe --under-replicated-partitions
else
    echo " 所有分区同步正常"
fi

echo ""
echo "=== 检查 Broker 状态 ==="
for broker in 1 2 3; do
    echo "Broker $broker:"
    # 检查 Broker 是否在线
    nc -z localhost 909$broker && echo "   在线" || echo "  离线"
done

echo ""
echo "=== 检查 Producer 指标 ==="
# 通过 JMX 或 Prometheus 查询 Producer 指标
# 这里简化处理
curl -s http://prometheus:9090/api/v1/query \
    --data-urlencode "query=rate(kafka_producer_record_error_total[5m])" \
    | jq '.data.result[0].value[1]' | awk '{if($1>0.01) print " 错误率高"; else print " 错误率正常"}'
4.6 容量规划建议
复制代码
┌─────────────────────────────────────────────────────────────┐
│              Producer 容量规划公式                           │
├─────────────────────────────────────────────────────────────┤
│                                                             │
│  所需缓冲区 = (TPS × 平均消息大小 × delivery.timeout.ms)    │
│              ÷ batch.size × 安全系数                        │
│                                                             │
│  示例计算:                                                 │
│  - TPS: 10000/s                                             │
│  - 平均消息大小: 1KB                                        │
│  - delivery.timeout.ms: 120000ms (2 分钟)                    │
│  - batch.size: 16KB                                         │
│  - 安全系数: 1.5                                            │
│                                                             │
│  所需缓冲区 = (10000 × 1KB × 120) ÷ 16KB × 1.5             │
│            = 1,200,000 KB ÷ 16KB × 1.5                     │
│            = 75,000 batches × 1.5                          │
│            = 112,500 batches                               │
│            = 112,500 × 16KB ≈ 1.8 GB                       │
│                                                             │
│  建议配置:buffer.memory = 2GB                              │
│                                                             │
│  ⚠️ 注意:如果缓冲区满了,send() 会阻塞或抛出异常           │
│                                                             │
└─────────────────────────────────────────────────────────────┘

五、故障排查"速查表"

复制代码
┌─────────────────────────────────────────────────────────────┐
│              Producer 发送慢故障排查速查表                   │
├─────────────────────────────────────────────────────────────┤
│                                                             │
│  症状 1:TPS 骤降,Producer 日志报 Timeout                     │
│  ├─ 检查 ISR 数量:kafka-topics.sh --describe                 │
│  ├─ 检查 min.insync.replicas 配置                             │
│  ├─ 检查 Follower lag:kafka-log-dirs.sh --describe           │
│  └─ 检查 Broker 状态:是否有 Broker 宕机                        │
│                                                             │
│  症状 2:消息大量被丢弃 (Expiring records)                     │
│  ├─ 检查 delivery.timeout.ms 配置                            │
│  ├─ 检查 request.timeout.ms 配置                             │
│  ├─ 检查重试次数:retries 配置                                 │
│  └─ 检查死信队列是否有积压                                      │
│                                                             │
│  症状 3:Producer 内存飙升                                    │
│  ├─ 检查 buffer.memory 配置                                  │
│  ├─ 检查 RecordAccumulator 积压情况                          │
│  ├─ 检查发送速率是否超过 Broker 处理能力                        │
│  └─ 检查是否有消息阻塞导致缓冲区无法释放                          │
│                                                             │
│  症状 4:部分 Topic 正常,部分 Topic 异常                       │
│  ├─ 检查异常 Topic 的 ISR 状态                                │
│  ├─ 检查异常 Topic 的 Leader 分布                             │
│  ├─ 检查是否有 Broker 负载不均衡                               │
│  └─ 检查异常 Topic 的分区是否集中在同一 Broker                   │
│                                                             │
└─────────────────────────────────────────────────────────────┘

六、终极建议(血泪总结)

复制代码
┌─────────────────────────────────────────────────────────────┐
│              acks 配置选择决策树                             │
├─────────────────────────────────────────────────────────────┤
│                                                             │
│  你的业务能容忍数据丢失吗?                                     │
│                                                             │
│  ├─ 能容忍(日志、监控数据)                                    │
│  │   └─→ acks=1 或 acks=0                                   │
│  │       优点:TPS 高,延迟低                                  │
│  │       风险:Leader 宕机可能丢数据                            │
│  │                                                           │
│  └─ 不能容忍(订单、支付、用户数据)                              │
│      └─→ acks=all + min.insync.replicas=2                    │
│          优点:数据强一致,不丢数据                               │
│          风险:ISR 不足时会阻塞                                 │
│          对策:监控 ISR + 死信队列兜底                           │
│                                                              │
└─────────────────────────────────────────────────────────────┘
6.2 架构建议
复制代码
推荐架构:

┌─────────────┐    ┌─────────────┐    ┌─────────────┐
│   业务服务   │ →  │  Producer   │ →  │   Kafka     │
│             │    │  + 重试     │    │   Cluster   │
└─────────────┘    └──────┬──────┘    └─────────────┘
                          │
                          │ 失败
                          ▼
                  ┌─────────────┐
                  │  死信队列   │
                  │  (DLQ)      │
                  └──────┬──────┘
                         │
                         │ 后续处理
                         ▼
                  ┌─────────────┐
                  │  补偿任务   │
                  │  (Retry)    │
                  └─────────────┘

acks=all 是"强一致性"的承诺,但前提是 ISR 要给力 。否则 Producer 就会陷入"等也不是,不等也不是"的两难境地。解决方案不是"无限等待",而是"有限等待 + 死信兜底"。

情景再现

复制代码
┌─────────────────────────────────────────────────────────────┐
│              Kafka Producer 故障模板                     │
├─────────────────────────────────────────────────────────────┤
│                                                             │
│  【故障概述】                                               │
│  - 故障时间:2024-XX-XX XX:XX                              │
│  - 影响范围:XX 业务线,TPS 从 10000 降至 0                   │
│  - 持续时间:XX 分钟                                        │
│                                                             │
│  【根因分析】                                               │
│  - 直接原因:ISR 收缩至 1 个,小于 min.insync.replicas=2     │
│  - 根本原因:Follower2 磁盘 IO 瓶颈导致同步 lag              │
│  - 触发条件:大促流量峰值,Follower 跟不上 Leader            │
│                                                             │
│  【处理过程】                                               │
│  - T+0: 发现告警,TPS 骤降                                   │
│  - T+5: 定位到 ISR 收缩问题                                  │
│  - T+10: 临时降级 acks=1,恢复 TPS                          │
│  - T+30: 修复 Follower2 磁盘问题                            │
│  - T+60: ISR 恢复,acks 调回 all                            │
│                                                             │
│  【改进措施】                                               │
│  - [x] 增加 ISR 收缩告警(已完成)                           │
│  - [x] 配置死信队列(已完成)                               │
│  - [ ] 优化 Follower 磁盘 IO(进行中)                       │
│  - [ ] 容量规划评审(计划中)                               │
│                                                             │
│  【经验教训】                                               │
│  - acks=all 必须配合 ISR 监控                               │
│  - 超时配置必须设置,避免无限等待                           │
│  - 核心业务必须有死信队列兜底                               │
│                                                             │
└─────────────────────────────────────────────────────────────┘

Again :强一致性是有代价的,这个代价就是 ISR 必须给力。否则,Producer 就会用 TPS 告诉你什么叫"等待的煎熬"。

相关推荐
花间相见2 小时前
【Ubuntu实用工具】—— Fcitx5 输入法安装与完整配置指南(新手友好+避坑版)
linux·数据库·ubuntu
数据知道2 小时前
MongoDB 比较查询运算符:$gt, $lt, $ne, $in 在范围筛选中的实战应用
数据库·mongodb
德彪稳坐倒骑驴2 小时前
数仓中的数据建模方法
数据库·oracle
网小鱼的学习笔记2 小时前
leetcode283移动零元素
java·开发语言·算法
青衫码上行2 小时前
高频SQL 50题 | 聚合
数据库·sql·mysql·leetcode·面试
自在极意功。2 小时前
Spring Boot 自动配置原理基本理解
java·spring boot·后端·自动配置原理
一点多余.2 小时前
java中的单例模式
java·开发语言·单例模式
有点心急10212 小时前
SQL 执行 MCP 工具开发(二)
数据库·sql
lucky67072 小时前
Laravel7.X十大核心特性解析
spring boot·kafka·linq