4. 日志生成策略:同步 vs 异步日志、批量写入、内存映射文件、零拷贝技术

日志这东西,在高频交易系统里,说白了就是「救命稻草」。行情来了,策略跑了,订单下了,每一步都得留下痕迹。但问题来了——你写日志的速度,不能比交易信号还慢。否则日志还没写完,行情已经变了,那还玩什么?

所以今天咱们聊聊日志生成的几种核心策略。我这些年踩过的坑不少,有些经验希望能帮你少走弯路。

4.1 同步日志 vs 异步日志

先说说最基础的选择:同步写还是异步写?

同步日志,就是每产生一条日志,程序就停下来等它写完。好处是简单,坏处是——慢。我早期做的一个策略,就因为同步写日志,导致行情来了来不及处理,直接错过了几个 tick。那叫一个心疼。

异步日志呢?日志先扔到一个内存队列里,后台线程慢慢写。主线程该干嘛干嘛,不耽误事。但要注意,如果队列满了,或者系统崩溃了,日志可能丢。嗯,这里有个权衡。

核心区别:

  • 同步:保证不丢,但拖慢主流程
  • 异步:性能好,但有丢失风险

我个人习惯是:关键路径(比如订单、成交)用同步写,非关键路径(比如心跳、状态)用异步写。这样既保命,又不拖累性能。

4.2 批量写入

说到性能优化,批量写入是个好东西。你想想看,每次写一条日志,就要调用一次系统调用,那开销多大?不如攒够一批,一次性写进去。

我见过一个项目,单条写入时延迟在 50 微秒左右,改成批量写入后,延迟降到了 5 微秒。十倍差距,你说值不值?

批量写入的常见策略:

  • 按时间触发:比如每 10 毫秒刷一次
  • 按数量触发:比如攒够 100 条再写
  • 按大小触发:比如缓冲区满了就写

我曾经在项目中遇到过一个问题:批量写入时,如果缓冲区设置太大,会导致日志延迟过高,排查问题的时候根本看不到实时数据。后来我改成「时间+数量」双重触发,效果好了很多。

小技巧:批量写入的缓冲区大小,建议设置为操作系统的页大小(通常是 4KB)的整数倍。这样可以避免内存碎片,提升写入效率。

4.3 内存映射文件(Memory-Mapped File)

这个技术,说白了就是把磁盘文件映射到内存地址空间。你写日志的时候,感觉就像在写内存,实际上数据会由操作系统帮你刷到磁盘上。

为什么快?因为省去了用户态到内核态的拷贝。你想想看,传统写文件要经过:用户缓冲区 → 内核缓冲区 → 磁盘。而内存映射文件直接操作内核缓冲区,少了一次拷贝。

我个人的经验是:内存映射文件特别适合写大文件、高频写入的场景。比如我们系统里,行情日志就是用 mmap 写的,每秒几十万条写入,延迟稳定在 1 微秒以内。

但要注意:

  • 映射的文件大小要提前固定,不能动态扩展
  • 系统崩溃时,未刷盘的数据可能丢失
  • 32 位系统下,映射空间有限

避坑指南:我曾经在项目中用 mmap 写日志,结果忘了调用 msync 强制刷盘。系统突然断电后,丢了将近 1 秒的日志。后来我改成定时调用 msync,才解决了这个问题。

4.4 零拷贝技术

零拷贝,名字听着高大上,其实原理不复杂。传统的数据传输,数据要在内核态和用户态之间来回拷贝好几次。零拷贝就是想办法减少这些拷贝次数。

在高频交易系统里,零拷贝技术常用于日志的传输和归档。比如:

  • sendfile:直接把文件内容从内核缓冲区发送到网络,不经过用户态
  • splice:在两个文件描述符之间传输数据,也是零拷贝
  • mmap + write:先映射文件,再写入,减少一次拷贝

我记得有一次,系统需要把日志实时传输到远程服务器做分析。用传统 read + write 方式,延迟在 100 微秒左右。改成 sendfile 后,延迟降到了 20 微秒。效果立竿见影。

但零拷贝也不是万能的:

  • 只适用于特定场景(比如文件到网络)
  • 需要操作系统支持
  • 代码复杂度会增加

4.5 四种策略对比

策略 延迟 吞吐量 数据安全 适用场景
同步日志 关键路径、小数据量
异步日志 非关键路径、大数据量
批量写入 高吞吐、可接受延迟
内存映射文件 极低 极高 高频写入、大文件
零拷贝 极低 极高 日志传输、归档

4.6 知识体系图

下面这张图,帮你理清这四种策略的关系和选择逻辑:

日志生成策略知识体系 日志生成策略 同步 vs 异步 批量写入 内存映射文件 零拷贝 同步:保数据 异步:保性能 时间触发 数量触发 mmap 映射 msync 刷盘 sendfile 选择策略的核心原则: 关键路径用同步 + 零拷贝,非关键路径用异步 + 批量写入

4.7 实战建议

说了这么多,最后给点实战建议:

  1. 先评估需求:你的系统对延迟有多敏感?对数据安全有多高要求?别一上来就上零拷贝,杀鸡焉用牛刀。
  2. 分层设计:关键日志用同步,非关键日志用异步。我习惯在代码里加一个日志级别开关,线上调优时很方便。
  3. 测试要到位:我曾经在测试环境跑得好好的,一上生产就出问题。后来发现是磁盘 IO 压力不同导致的。所以一定要在生产环境做压力测试。
  4. 监控不能少:日志写入延迟、队列长度、磁盘使用率,这些指标都要监控。出了问题能第一时间发现。

最后说一句:日志系统不是写完了就完事了。它需要持续优化、持续监控。你想想看,如果日志系统出了问题,你连排查问题的依据都没有,那才是真正的灾难。


专注资料整理