为什么你的日志收集架构在流量峰值时总是丢数据上周双十一预热流量刚涨了三倍我们的日志系统就先崩了。不是应用崩不是数据库崩是那个平时没人注意的日志收集管道。Kafka 的 topic 堆积了 800 万条消息Elasticsearch 直接变红Grafana 上日志延迟的曲线像火箭一样往上窜。最离谱的是我们丢了大概 20 分钟的日志那 20 分钟里刚好有个核心接口报错我们愣是找不着原因。说实话日志系统这东西平时没人看一出事就是大事。今天想聊聊为什么你的日志收集架构在流量峰值时总是丢数据以及我们是怎么把这个坑填上的。背景我们的日志架构长什么样先交代一下背景。我们的架构算是比较标准的微服务日志方案应用用 Logback 输出日志 → Filebeat 采集 → Kafka 做缓冲 → Logstash 解析 → Elasticsearch 存储 → Kibana 查询。看起来挺完善的对吧每个环节都有缓冲理论上不应该丢数据。但问题就出在这个理论上。平时流量平稳的时候这套架构跑得挺好。但一到流量峰值三个环节连环出问题Filebeat 采集跟不上、Kafka 消费者滞后、Elasticsearch 写入拒绝。整个管道就像一条堵车的高速公路入口还在不断进车最后只能把一部分车直接扔掉。问题根因三个环节的瓶颈我们花了大概一个下午排查最后定位到三个核心瓶颈。Filebeat 的 harvester 数量不够Filebeat 默认的 harvester 数量是有限制的。当应用日志文件滚动速度加快或者单个文件写入速度暴增时Filebeat 会启动多个 harvester 来跟踪文件。但默认配置下harvester 上限比较低而且如果文件被快速删除比如日志切割策略太激进Filebeat 还没来得及发送的数据就直接丢了。我们当时排查的时候看了 Filebeat 的日志发现大量这种错误ERROR filebeat/harvester/log_harvester.go:xxx Harvester for file /var/log/app/app.log.1 timed out说白了就是 Filebeat 采集速度跟不上应用写日志的速度新文件又不断产生旧文件里的数据还没来得及读就被删掉了。Kafka 消费者处理不过来第二个瓶颈在 Kafka。我们的日志流量平时大概是每秒 5 万条消息峰值时冲到 20 万条。Kafka 本身扛得住但下游的 Logstash 消费者只有 6 个实例每个实例 4 个 consumer thread处理能力上限大概就是每秒 8 万条。当流量超过这个阈值Kafka 的 consumer lag 就开始疯狂增长。我们当时监控到的最大 lag 是 1200 万条意味着最新的日志要延迟 3 分钟才能被处理。更要命的是Kafka 的 retention 时间是 7 天但如果 lag 持续增大某些 partition 的数据可能会在消费者还没读完的情况下就被清理了。Elasticsearch 写入拒绝第三个瓶颈在 Elasticsearch。日志数据经过 Logstash 解析后会批量写入 ES。ES 的写入能力受限于几个因素磁盘 I/O、segment merge 压力、索引刷新频率。当写入速率超过 ES 的处理能力ES 会返回429 Too Many RequestsLogstash 会把这些数据缓冲在内存队列里。但内存队列是有上限的默认 1000 条满了之后新数据就直接丢弃了。我们当时看到 Logstash 的日志里一堆这种报错[WARN][logstash.outputs.elasticsearch] Failed to execute action {:status429, :action[index, ...]}解决方案三层改造找到根因之后我们做了三层改造。不是一次性全上的而是分阶段验证逐步推进。第一层应用层增加日志缓冲和采样首先在应用层做保护。我们给 Logback 加了一个 AsyncAppender配合 RingBuffer把日志的内存缓冲从默认的 256 提升到 4096。这样即使下游短暂阻塞应用也不会因为同步写日志而卡死。同时我们引入了一个简单的日志采样策略。对于 DEBUG 级别的日志在流量峰值时自动采样 10%只保留 1/10 的数据。对于 ERROR 级别的日志始终全量保留。这个策略用了一个简单的 RateLimiter 实现// 伪代码示意if(levelDEBUGtrafficPeakMode){if(rateLimiter.tryAcquire()){appender.append(logEvent);}}else{appender.append(logEvent);}说实话采样这事儿一开始团队内部有争议。有人说日志不能丢丢了就没法排查问题了。但现实是如果全量日志把系统压垮了你连系统都没有了还排查什么关键日志ERROR、WARN、业务核心链路全量保留非关键日志采样这是一个务实的取舍。第二层Kafka 分区扩容 消费者并发提升第二层的改造在 Kafka 和消费者端。我们把日志 topic 的分区数从 12 个增加到 36 个这样每个分区的数据量更均匀也支持更多的消费者并行处理。Logstash 的实例数从 6 个增加到 12 个每个实例的 worker 数量从 4 提升到 8pipeline batch size 从 125 提升到 500。同时我们把 Logstash 的内存队列大小从 1000 提升到 10000并启用了持久化队列Persistent Queue这样即使 Logstash 重启队列里的数据也不会丢。Kafka 消费者的配置也做了优化fetch.min.bytes: 1 → 50000 fetch.max.wait.ms: 500 → 1000 max.poll.records: 500 → 2000这些参数的调整逻辑是减少消费者的空轮询增加每次 poll 的数据量提升整体吞吐。第三层Elasticsearch 写入优化 冷热分离第三层的改造在 ES 端。我们做了几个关键的写入优化1. 索引模板调整把索引的 refresh_interval 从默认的 1s 调整到 30s。这意味着日志数据从写入到可查询的延迟从 1 秒增加到 30 秒但写入吞吐量提升了大概 3 倍。对于日志场景来说30 秒的查询延迟是可以接受的。{index:{refresh_interval:30s,number_of_replicas:0,translog.durability:async}}注意我们把 number_of_replicas 在写入高峰期临时设为 0减少副本同步的压力。等流量回落后再恢复为 1。2. 写入批量优化Logstash 的 Elasticsearch output 配置把 bulk 的 size 从 500 提升到 2000flush interval 从 5s 提升到 15s。更大的批量意味着更少的 HTTP 请求开销但缺点是单条失败的成本更高。我们配合了action create和_id的自动生成减少版本冲突的可能。3. 冷热分离架构最后一个大的改造是冷热分离。我们把 ES 集群分成热节点和温节点热节点SSD 盘处理最近 7 天的日志需要快速查询温节点SATA 盘存储 7-30 天的日志查询频率低冷节点对象存储MinIO30 天以上的日志归档基本不查询通过 Index Lifecycle ManagementILM策略日志索引会自动从热节点迁移到温节点最后归档到冷存储。这样热节点只需要承载少量的活跃索引写入压力大幅减轻。踩坑记录改造过程中踩了几个坑记录一下免得你们重蹈覆辙。坑 1Filebeat 的 registry 文件损坏Filebeat 的 registry 文件记录了每个文件的采集偏移量。有一次我们直接重启了 Filebeat但 registry 文件因为磁盘满而写入失败导致重启后 Filebeat 重新从头读取了所有日志文件Kafka 里出现了大量重复数据。解决方法是给 registry 文件单独配一个持久化卷同时监控 registry 文件的大小和写入状态。坑 2Logstash Persistent Queue 的磁盘占用开启 PQ 之后Logstash 的磁盘占用暴涨。默认配置下PQ 的最大大小是 1GB但我们为了扛峰值把上限调到了 20GB。结果有一次流量峰值持续了一整天PQ 把磁盘占满了Logstash 直接卡住。最后我们给 PQ 加了 page capacity 限制和 checkpoint 频率控制同时监控磁盘使用率超过 80% 就报警。坑 3ES 的 translog 刷盘策略我们把translog.durability设为async之后写入性能确实提升了但有一次 ES 节点意外重启丢失了大概 2 秒的 translog 数据。虽然日志丢 2 秒不算严重但这提醒我们不能盲目追求性能。最后我们在热节点上保留async在温节点上恢复request同步刷盘做一个折中。坑 4日志采样导致关键信息缺失DEBUG 日志采样 10% 的策略上线后有一次排查一个偶发 Bug发现那条 DEBUG 日志刚好被采样丢掉了。后来我们调整策略对于特定业务链路比如支付、订单即使是 DEBUG 也全量保留对于普通查询类接口才做采样。用 MDCMapped Diagnostic Context里的 traceId 来做链路级别的过滤。写在最后日志系统这玩意儿说重要也重要说不重要也确实容易被人忽视。很多团队的日志架构都是能跑就行平时没问题一到关键时刻就掉链子。我们这次改造之后日志系统的峰值处理能力从每秒 8 万条提升到了每秒 35 万条Kafka 的 consumer lag 基本控制在 10 万条以内也就是 2-3 秒的延迟ES 的写入拒绝率从峰值时的 15% 降到了 0.1% 以下。最重要的是再也没有出现过日志丢失的情况。如果你也在搭建或者维护日志收集架构我的建议是不要迷信标准架构。Filebeat Kafka Logstash ES 这套方案很成熟但每个环节都有默认配置默认配置往往扛不住流量峰值。日志也要做降级。不是所有日志都值得全量保留。区分关键链路和非关键链路在极端情况下敢于采样甚至丢弃非关键日志保护核心系统的稳定性。监控日志系统本身。大部分团队只监控应用和数据库但日志管道的健康状况同样重要。consumer lag、磁盘使用率、采集延迟这些指标都应该进报警。冷热分离是必选项。日志数据的增长速度远超你的想象。不做冷热分离半年之后你的 ES 集群就会被历史日志拖垮。好了今天就聊到这。如果你也遇到过日志丢失的问题或者有更好的架构方案欢迎评论区交流。