文档中心>检索分析服务>TSearch 内核能力介绍>Batch Commit:对高并发小批量写入的优化

Batch Commit:对高并发小批量写入的优化

最近更新时间:2025-09-12 14:15:02

我的收藏

背景

在日志类业务、埋点收集等高并发小批量写入场景中,系统往往面临写入请求频繁、单次数据量较小的问题。传统“请求即落盘”的写入路径会为每个独立的 bulk 请求生成一个数据 Part 并立即写入磁盘,进而导致:
Part 文件数量激增,系统在短时间内生成大量细小的碎片文件;
后台合并(Merge)压力上升,频繁的小文件合并严重消耗 I/O 和 CPU;
写入性能抖动,前台写入受后台 Merge 任务拖累;
存储资源浪费,过多小文件占用 inode,压缩比下降;
系统稳定性下降,Merge 队列堆积时易产生请求排队和延迟波动。
为解决以上结构性瓶颈,我们引入了写入攒批能力,在引擎层新增写入缓冲逻辑,实现在内存中对数据进行按需积累、统一提交的策略。
写入攒批能力主要面向以下场景:
高并发、小批量写入频繁的在线日志接入
多分区散写场景,如设备 ID、用户 ID 为分区键
对写入性能、Merge 成本、稳定性有更高要求的业务系统
该能力通过在内核中延迟生成 Part,控制写入节奏,主动将多个小写入合并为一个紧凑的落盘操作,有效提升系统在高并发接入下的整体写入质量和资源效率。

方案

写入缓冲(攒批)机制详解

写入攒批的核心思路是在内存中暂存批量数据,满足特定条件后再统一生成 Part 落盘,从而以更少、更大、更紧凑的 Part 替代大量零散小 Part。该机制包括以下几个关键阶段:

1. 请求解析为 Block(延迟生成 Part)

当接收到一个 bulk 请求后,系统会对其中的文档进行解析,构造成内部的 Block 数据结构。每个 Block 相当于一个逻辑上的写入批次,但此时不会立即生成 Part 文件,即不会触发磁盘写入。此步骤的意义在于打破“请求即落盘”的链路,将写入节奏从请求驱动转为数据积累驱动,为后续缓冲与合并创造条件。

2. 写入 WAL(保证数据可靠性)

解析后的 Block 会首先写入到 WAL(Write-Ahead Log),这是一个顺序写入的日志文件,确保数据在内存攒批期间即使发生故障也不会丢失。WAL 写入具有以下特性:
支持快速追加写,性能高
后续落盘成功后即可被安全清理
在系统异常恢复时可重放
该步骤确保攒批过程在保证性能的同时不牺牲可靠性。

3. 写入内存缓冲区(攒批)

Block 会写入引擎中的 MutableBuffer(可变缓冲区),这是内存中的临时数据结构,用于对多个请求的数据进行聚合缓冲。多个 bulk 请求的数据可以持续累积到这个缓冲区中,直到触发条件满足。这一步是核心的“攒批”行为,通过有序缓存数据,为后续生成更大、更连续的 Part 打下基础。

4. 提交触发机制(可配置)

为了控制攒批时长与写入时机,系统提供了灵活的触发策略,满足任一条件即可触发 Commit:
数量阈值触发:缓冲区内数据达到预设的行数或字节数,
时间阈值触发:距离本轮攒批的第一次写入时间已超过一定时长,可保证低频写入不会长时间滞留
该机制在写入吞吐与写入延迟之间实现动态平衡:高频写入以数据量触发,低频写入以时间触发,确保既能合并批次,又能保障响应性。

5. 批量生成 Part(统一落盘)

一旦触发条件满足,系统将当前缓冲区中的所有数据一次性转为一个或多个 紧凑型 Part 文件 并写入磁盘。这些 Part 比原始单个请求生成的小 Part 更大、更稠密、更适于后续合并和扫描。生成 Part 时的优化包括:
对同一分区的多条数据批量处理,避免分散写入多个文件
更好的数据局部性和列压缩比
降低文件数量、目录层级与 inode 占用

6. 清理 WAL 与内存缓存(释放资源)

在 Part 落盘完成后,系统会:
清理对应的 WAL 段,释放磁盘空间
回收已写入的 MutableBuffer 区域
为下一批次写入腾出空间
整个攒批流程至此完成一个闭环。整体设计图如下:


机制总结与优势对照

对应问题
传统写入
攒批写入
小文件碎片
每请求一个小 Part
多请求合并生成大 Part
Merge 压力
Merge 次数频繁
Merge 次数减少,合并效率更高
IO 效率
频繁小写,压缩差
批量写入,压缩更优
系统稳定性
Merge 队列堆积风险高
写入平稳,资源利用更均衡
写入延迟
每次落盘受 Merge 干扰
Merge 异步,前台更流畅
通过在引擎层引入攒批机制,不仅优化了写入链路的结构与节奏,更显著提升了系统对高并发、高频次、小体量数据写入的处理能力,是日志型、埋点型等写密集型场景的重要内核能力升级。

配置参数

索引级配置

参数
说明
默认值
index.tsearch.engine.batch_commit.enable
是否开启攒批写入
false
index.tsearch.engine.batch_commit.refresh_buffer_size
Cache 在内存中的 Blocks commit 为 Part 的阈值。配置范围[4MB, 64MB]
8MB
index.tsearch.engine.batch_commit.refresh_interval
一批 Blocks 如果在 refresh_interval 时间范围内未达到 refresh_buffer_size 指定的阈值,则也 commit 为 Part
30s
index.tsearch.engine.batch_commit.refresh_rows_count
Cache 在内存中的 Blocks commit 为 Part 的阈值。为0时,此参数不生效
0
index.tsearch.engine.batch_commit.allow_back_off_native_write
如果达到了内存限制(全局2GB),是否允许回退到直接写 Part 到逻辑
true

配置示例

PUT /http_logs

{
    "settings": {
        "number_of_shards": 1,
        "number_of_replicas": 1,
        "index.tsearch.engine.batch_commit.enable": true,
        "index.tsearch.engine.batch_commit.refresh_buffer_size": "16MB",
"index.tsearch.engine.batch_commit.refresh_interval": "30s",
        "index.tsearch.engine.batch_commit.refresh_rows_count": 0,
        "index.tsearch.engine.batch_commit.allow_back_off_native_write": true
    },
    "mappings": {
        "properties": {
            "foo": {
                "type": "text"
            },
            "myint": {
                "type": "long"
            }
        }
    }
}

集群级配置

参数
说明
默认值
cluster.tsearch.engine.batch_commit.enable
集群内的索引是否默认开启 BatchCommit,即创建索引时,如果用户没有配置`index.tsearch.engine.batch_commit.enable`,则会使用`cluster.tsearch.engine.batch_commit.enable`
false

配置示例

PUT /_cluster/settings

{
"persistent": {
"cluster.tsearch.engine.batch_commit.enable": true
}
}

使用限制

目前用于 Batch Commit 的全局内存限制为2GB。如果超过这个上限,则退化为直接写 Part 的逻辑或者返回错误,通过 allow_back_off_native_write 配置。