一 背景
我们项目中有大量写库操作,当服务出现高并发时会导致数据库IO压力变大。
为了应对这种情况,降低数据库压力,我们需要基于Disruptor设计一套通用的异步批处理流水线。
二 技术选型
其实选择 Disruptor高性能队列之前,本来是想使用 Java内置队列的。
但是都被我pass掉了。原因如下:
concurrentLinkedQueue 虽然是 无锁代表着吞吐量高,但是它内存是不可控的会有OOM风险,以及它的数据结构是链表,频繁的创建和销毁导致GC压力大。
LinkedBlokingQueue 有锁,可选有界 但是数据结构是 链表会有GC压力大。
ArrayBlockingQueue 虽然是 有界队列,内存可控,数据结构为数组对GC友好,但是它存在性能瓶颈原因是因为存在锁竞争,锁竞争就会导致 cpu 工作效率低下,因为时间都花在 锁、挂起、唤醒上。还有一点就是她存在 伪共享,导致缓存命中率下降。
意识到这些问题,决定使用disruptor 高性能队列框架。
原因是因为 disruptor是环形数组天然有界,而且内存是预分配的可控,不会导致OOM,而且采用了CAS来解决锁竞争,使用了缓存行填充解决了 伪共享的问题。
基于 Disruptor 的 无锁设计 和 内存预分配,使得发布事件非常快。Disruptor是英国外汇交易公司LMAX开发的一个高性能队列,每秒可支撑600万订单。
三 实践
核心思想是将高频的单条写入请求,在内存中聚合成批次,最后进行批量写入,极大地减少了数据库的IO次数。
3.1 核心组件
3.1.1 BatchDataRing(RingBuffer 管理器)
负责初始化和管理 Disruptor 框架,提供事件发布接口和注册 ==> 门面模式
主要配置参数
- ring-size: RingBuffer 大小,默认 4,194,304(1024 * 128 * 32)
- db-batch-size: 数据库批量处理大小,默认 640
- db-batch-chunk-size: 数据块大小,默认 40
- ring-batch-size: RingBuffer 批量处理大小,默认 1024

核心机制
- 单消费者模式:通过 LimitedThreadFactory 保证只有一个线程处理事件
3.1.2 BatchDataHandler(消费者)
实现 EventHandler 接口,负责处理 RingBuffer 中的事件。
设计亮点是 支持多种数据类型,内部为每种类型维护以下数据结构
数据结构:
private Map<Class,Function<List, Boolean>> funcs = new ConcurrentHashMap<>(); // 类型处理函数映射
private Map<Class,List> caches = new ConcurrentHashMap<>(); // 类型数据缓存映射
3.1.3 数据单元 dataEvent
就是 ringBuffer 流动的盒子
3.1.4 具体的业务适配器
3.2 工作流程
3.2.1 事件发布流程
通过 BatchDataRing.publish() 方法发布事件到 RingBuffer ,事件按 sequence 顺序存储在 RingBuffer 中。
3.2.2 事件处理流程
- 单线程按 sequence 顺序处理事件
- 将事件数据按类型分类缓存到对应列表中
- 达到批量处理条件时触发批量保存
3.2.3 批量处理触发条件
- 每处理 dbBatchSize(640) 个事件
- RingBuffer 结束且未达到 ringBatchSize(1024) 的倍数
3.2.4 批量保存机制
- 遍历所有类型的数据缓存
- 每种类型数据独立进行批量处理
- 将每种类型的数据按 dbBatchChunkSize(40) 分割成多个 chunk
- 使用线程池并发处理所有 chunk
- 等待所有 chunk 处理完成,记录处理时间
3.3. 并发控制策略
3.3.1 外部串行,内部并发
- 外部串行:事件按 sequence 顺序由单线程处理,保证处理顺序性
- 内部并发:批量数据通过线程池并发处理,提升处理性能
3.3.2 线程控制
通过 CAS 操作确保只创建一个处理线程,保证事件处理的顺序性。
java
public class LimitedThreadFactory implements ThreadFactory {
private final AtomicInteger count = new AtomicInteger(0);
public Thread newThread(Runnable r) {
if (count.compareAndSet(0, 2)) {
return new Thread(r);
} else {
throw new IllegalStateException("Created more that one thread");
}
}
}
3.4. 性能优化设计
3.4.1 分层批量处理
scss
RingBuffer (4,194,304)
↓
RingBuffer 批量 (1024)
↓
数据库批量 (640)
↓
并发处理单元 (40)
3.4.2 多类型数据处理
- 不同类型数据独立缓存和处理
- 每种类型数据达到批量条件时独立处理
- 避免不同类型数据之间的相互阻塞
3.4.3 并发处理优化
- 使用 Executors.newCachedThreadPool() 线程池
- 数据分块并发处理,提升 I/O 利用率
