1. 概述

本文将快速介绍 Big Queue,这是一个基于 Java 实现的持久化队列(persistent queue)

我们会简要分析它的架构设计,并通过几个简单但实用的代码示例,带你快速上手使用。如果你正在寻找一个轻量、高效、能处理海量数据的本地队列方案,Big Queue 是一个值得考虑的选择。

2. 使用方式

首先,在项目中引入 bigqueue 依赖:

<dependency>
    <groupId>com.leansoft</groupId>
    <artifactId>bigqueue</artifactId>
    <version>0.7.0</version>
</dependency>

同时需要添加其专属仓库(因为未发布到中央仓库):

<repository>
    <id>github.release.repo</id>
    <url>https://raw.github.com/bulldog2011/bulldog-repo/master/repo/releases/</url>
</repository>

提示:这个库虽然小众,但 API 设计非常简洁,如果你熟悉 Java 的基础队列操作,几乎可以无缝切换。

2.1 初始化

通过构造函数即可创建一个 Big Queue 实例:

@Before
public void setup() {
    String queueDir = System.getProperty("user.home");
    String queueName = "baeldung-queue";
    bigQueue = new BigQueueImpl(queueDir, queueName);
}
  • queueDir:队列数据的存储根目录
  • queueName:队列名称,会在此目录下生成一个同名文件夹用于持久化数据

⚠️ 重要:使用完毕后务必调用 close() 方法释放资源,避免内存泄漏或文件句柄未释放:

bigQueue.close();

2.2 插入元素

使用 enqueue 方法将数据写入队尾:

@Test
public void whenAddingRecords_ThenTheSizeIsCorrect() {
    for (int i = 1; i <= 100; i++) {
        bigQueue.enqueue(String.valueOf(i).getBytes());
    }
 
    assertEquals(100, bigQueue.size());
}

⚠️ 踩坑提醒:Big Queue 只支持 byte[] 类型!
这意味着你需要自己负责对象的序列化(如 JSON、Protobuf、Kryo 等),插入前转成字节数组。

2.3 读取元素

使用 dequeue 方法从队头取出并移除元素:

@Test
public void whenAddingRecords_ThenTheyCanBeRetrieved() {
    bigQueue.enqueue(String.valueOf("new_record").getBytes());

    String record = new String(bigQueue.dequeue());
 
    assertEquals("new_record", record);
}

同样,取出后需要手动反序列化。
注意:从空队列中 dequeue 会抛出 NullPointerException,不是 NoSuchElementException,这点和标准队列不同!

✅ 安全做法是先判断是否为空:

if (!bigQueue.isEmpty()) {
    byte[] data = bigQueue.dequeue();
    // 处理数据
}

✅ 如果想清空整个队列而不逐个消费,可以直接调用:

bigQueue.removeAll();

简单粗暴,适合做重置操作。

2.4 查看队首元素(Peek)

peek 方法可以查看队头元素但不移除它,适合做预判处理:

@Test
public void whenPeekingRecords_ThenSizeDoesntChange() {
    for (int i = 1; i <= 100; i++) {
        bigQueue.enqueue(String.valueOf(i).getBytes());
    }
 
    String firstRecord = new String(bigQueue.peek());

    assertEquals("1", firstRecord);
    assertEquals(100, bigQueue.size()); // 队列长度不变
}

2.5 清理已消费的数据

⚠️ 关键机制:调用 dequeue 后,数据虽然从逻辑上被“消费”,但物理上仍保留在磁盘中!

这会导致磁盘空间持续增长,必须手动触发垃圾回收:

bigQueue.gc();

✅ 这个 gc() 方法就像 JVM 的垃圾回收器一样,会清理掉已经被消费的旧记录,释放磁盘空间。

建议在批量消费后定期调用,避免“磁盘爆炸”。

3. 架构与特性

Big Queue 最令人惊讶的一点是:整个核心代码只有 12 个源文件,约 20KB,却实现了高性能的持久化队列。

它本质上是一个基于内存映射文件(memory-mapped file)实现的磁盘队列,兼顾了速度与容量。

3.1 支持超大数据量

  • ✅ 队列大小仅受限于磁盘空间,理论上可无限扩展(加硬盘就行),这也是“Big”名字的由来。
  • 所有数据持久化到磁盘,进程崩溃后不丢消息。
  • 性能瓶颈主要在磁盘 I/O,因此强烈建议使用 SSD。

3.2 极致的读取速度

底层使用 memory-mapped file,将文件映射到虚拟内存。
队列头部(可访问部分)常驻内存,访问延迟极低。

✅ 即使队列达到 TB 级别,读取时间复杂度依然是 **O(1)**!

💡 原理类比:就像 mmap 读文件比传统 IO 快很多,这里也是同理。

3.3 优势总结

  • 无限扩展:只要磁盘够,队列就无限大
  • 高性能:在普通机器上,多线程吞吐可达 166MB/s
    • 若每条消息 1KB,则每秒处理约 16.6 万条
    • 单线程下甚至可达 33.3 万条/秒,性能非常可观
  • 轻量级:代码极少,无复杂依赖,易于嵌入
  • 持久化保障:断电不丢数据

3.4 局限性

  • 需手动 GC:已消费数据不会自动删除,必须显式调用 gc(),否则磁盘会越用越多
  • 无自动序列化:所有数据必须手动转为 byte[],对使用者要求更高
  • 功能单一:只支持基础队列操作,不支持优先级、延迟、广播等高级特性

4. 总结

Big Queue 是一个极简但高效的持久化队列实现,特别适合以下场景:

  • 需要本地持久化缓冲大量数据
  • 对读写性能有一定要求
  • 不想引入 Kafka、RabbitMQ 等重型中间件

虽然功能简单,但在合适场景下能发挥巨大价值。
对于轻量级、高性能、本地化的队列需求,它是一个被低估的“小钢炮”工具。

示例代码已上传至 GitHub:https://github.com/baeldung/tutorials/tree/master/data-structures


原始标题:Introduction to Big Queue | Baeldung

« 上一篇: Java 预览特性详解
» 下一篇: Java 13 新特性详解