Show HN: AnuDB – 基于 RocksDB,在并行工作负载中比 SQLite 快 279 倍
AnuDB
AnuDB 是一个轻量级的、无服务器的文档数据库,专为 C++ 应用程序设计,通过 MessagePack 序列化高效地存储 JSON 文档。它为需要灵活数据管理和强大查询功能的应用程序提供了一个无服务器、无模式的解决方案。
由于 AnuDB 构建于 RocksDB 之上,因此确保了文档的原子性、持久性和一致性。您可以根据 StorageEngine.cpp 和 StorageEngine.h 中提到的 RocksDB 选项,调整 AnuDB 的内存/CPU 使用率。 基于这些配置,您可以获得根据您的特定平台需求量身定制的理想性能结果。
开始使用 AnuDB!
准备好在您的项目中使用 AnuDB 吗? 请按照以下简单步骤快速入门:
- 安装 AnuDB 请参阅安装指南在您的机器上设置 AnuDB。
- 探索示例代码 查看我们的使用示例以了解如何与 AnuDB 交互。
- 开始构建! 通过在您的应用程序中使用 AnuDB 并开始以闪电般的速度存储数据来直接投入使用。
- Docker 支持 AnuDB 支持 Docker! 在容器化环境中轻松运行 AnuDB。 请参阅我们的 Docker 设置指南以开始使用。
- MQTT 支持 AnuDB 与 MQTT 无缝集成,实现设备和服务器之间的实时数据通信。 如果您正在开发物联网或实时消息系统,AnuDB 的 MQTT 支持将使您的工作更轻松。 查看我们的 MQTT 集成指南以开始使用 MQTT 和 AnuDB。
- 基准测试
特性
- 嵌入式 & 无服务器: 直接在您的应用程序中运行,无需单独的服务器进程
- 嵌入式平台支持:专为标准 Linux/Windows 系统和嵌入式平台设计
- 更快的写入 – RocksDB (LSM-tree) 后端针对高速插入/更新进行了优化。
- JSON 文档存储:存储和查询复杂的 JSON 文档
- RocksDB 后端:构建于 RocksDB 之上,以实现高性能和持久性
- 事务属性:继承 RocksDB 的原子操作和可配置的持久性
- 压缩选项:支持 ZSTD 压缩以减少存储占用空间
- 灵活的查询:支持等式、比较、逻辑运算符和排序
- 索引:创建索引以加速对常用字段的查询
- 更新操作:丰富的文档修改操作集
- 导入/导出:简易的 JSON 导入和导出,用于数据迁移。导出的 JSON 文件也可用于将数据迁移到 Postgresql/SQL server/MySQL/Oracle DB
- C++11 兼容:旨在高效地支持各种嵌入式设备
- Windows/Linux 支持:专为 Windows/Linux 环境和嵌入式 Linux 平台设计
- MQTT 接口:通过 MQTT 协议从各种平台连接和操作
- 高并发:支持 32 个并发 nng 工作线程(可使用 CONCURRENT_THREADS 配置)以处理 MQTT 请求
- TLS 安全:使用 mbedTLS 进行安全通信,用于加密的 MQTT 连接
- 云 MQTT 支持:与主要的云 MQTT 代理兼容
- ACID 合规性:AnuDB 继承 RocksDB 的原子性、一致性、隔离性和持久性
MQTT 接口
AnuDB 现在支持通过 MQTT 协议进行交互,允许您从各种平台连接和操作数据库,而无需直接 C++ 集成。 该实现使用 nlohmann::json 进行 JSON 处理。 AnuDB 订阅 MQTT anudb/request
主题,然后如果请求在提到的主题上带有请求 ID,那么它会执行该操作,然后在 anudb/response/requestid
上发回响应,我们可以在 anudb/response/+
主题上获得所有响应,下图说明了这一点。
在下面的演示中,显示了 AnuDBMqttBridge 和 mosquitto 代理服务器已启动,然后使用 client.bash 脚本,运行了所有支持的 MQTT 命令
Docker 支持
要使用 Docker 运行 AnuDB:
1. 拉取 Docker 镜像
docker pull kanadaanu/anudb-mqtt:v1
2. 确保 MQTT 代理正在运行
在启动 AnuDB 之前,您需要运行一个 MQTT 代理(例如,EMQX,Mosquitto)。
3. 运行 Docker 容器
根据您的设置,使用以下命令之一:
# 使用 TLS (例如, EMQX public broker, mosquitto broker, 等)
docker run -v <disk folder>:/data kanadaanu/anudb-mqtt:v1 \
--broker_url tls+mqtt-tcp://<broker_url>:8883 \
--database_name AnuDB \
--tls_cacert <> \
--tls_cert <>
...
# 不使用 TLS
docker run -v <disk folder>:/data kanadaanu/anudb-mqtt:v1 \
--broker_url mqtt-tcp://<broker_url>:1883 \
--database_name AnuDB
注意
/data
文件夹是您应该挂载磁盘卷以持久化数据库的位置。- 如果您使用的是 TLS 连接,请将所需的证书文件(例如,CA 证书)放置在已挂载的
/data
目录中,以便二进制文件可以访问它们。
前提条件
- C++11 >= 兼容的编译器
- CMake 3.10 或更高版本
- ZSTD 开发库(可选,用于压缩支持)
- Mosquitto MQTT 代理(对于 MQTT 接口,支持版本 v3.1.1,但可以通过简单的 API 修改支持 5.0.0 版本)
- mbedTLS 库(用于 TLS 支持)
从源码构建
无需安装任何其他第三方库。 以下命令将生成 AnuDB.exe bin,该 bin 执行 AnuDB 支持的所有操作,它还将生成 liblibanu.a 静态库,您可以在您的应用程序中使用它以及所需的头文件。
# 克隆仓库
git clone https://github.com/hash-anu/AnuDB.git
cd AnuDB/third_party/nanomq/
git submodule update --init --recursive
cd ../..
mkdir build
cd build
cmake ..
make
or
# 配置 ZSTD 压缩支持
cmake -DZSTD_INCLUDE_DIR=/path/to/zstd/include -DZSTD_LIB_DIR=/path/to/zstd/lib ..
make
嵌入式平台构建
对于嵌入式平台,您可能需要使用交叉编译器工具链:
# 配置交叉编译器
cmake .. \
-DCMAKE_C_COMPILER=arm-linux-gnueabihf-gcc \
-DCMAKE_CXX_COMPILER=arm-linux-gnueabihf-g++ \
-DCMAKE_C_FLAGS="-march=armv7-a -mfpu=neon -mfloat-abi=hard -DNIOSTATS_CONTEXT=1" \
-DCMAKE_CXX_FLAGS="-march=armv7-a -mfpu=neon -mfloat-abi=hard -DNIOSTATS_CONTEXT=1 -Wno-error" \
-DWITH_ALL_TESTS=OFF \
-DWITH_TESTS=OFF \
-DWITH_TOOLS=OFF \
-DWITH_GFLAGS=OFF \
-DPORTABLE=ON \
-DBUILD_TESTS=OFF
示例
AnuDB 数据库的示例已添加到 examples 文件夹中。API 概览
快速开始
#include "Database.h"
#include <iostream>
int main() {
anudb::Database db("./my_database");
anudb::Status status = db.open();
if (!status.ok()) {
std::cerr << "Failed to open database: " << status.message() << std::endl;
return 1;
}
// 创建一个集合
status = db.createCollection("users");
// 获取集合
anudb::Collection* users = db.getCollection("users");
// 创建文档
nlohmann::json userData = {
{"name", "Hash"},
{"email", "hash@example.com"},
{"age", 33}
};
anudb::Document doc("user001", userData);
// 插入文档
status = users->createDocument(doc);
anudb::Document doc1;
status = users->readDocument("user001", doc1);
std::cout << doc1.data().dump(4) << std::endl;
// 关闭数据库
db.close();
return 0;
}
高性能 MQTT Worker 架构
AnuDB 实现了高性能并发工作架构来高效处理 MQTT 请求:
- 32 个并发工作线程:MQTT 桥生成 32 个 nng (nanomsg-next-generation) 工作线程以并行处理传入的请求
- 负载平衡:请求自动分配到所有工作线程以获得最佳性能
- 线程安全:所有数据库操作都是线程安全的,从而实现真正的并发处理
- 异步处理:每个worker独立运行,防止慢速操作阻塞整个系统
- 可伸缩的设计:worker 架构在多核系统上高效伸缩
Worker 线程架构图
┌─────────────────┐
│ MQTT Broker │
│ (Mosquitto) │
└────────┬────────┘
│
▼
┌─────────────────┐
│ MQTT Bridge │
│ Connector │
└────────┬────────┘
│
▼
┌───────────────────────────────┐
│ Message Dispatcher │
└───┬───────┬───────┬───────┬───┘
┌─────────────┘ │ │ └─────────────┐
│ │ │ │
┌─────────▼───────┐ ┌─────────▼───┐ ▲ ┌─────────▼───────┐
│ nng Workers │ │ nng Workers │ │ │ nng Workers │
│ (Threads 1-8) │ │ (Threads 9-16)│ │ │ (Threads 25-32) │
└─────────────────┘ └─────────────┘ │ └─────────────────┘
┌───┴───────┐
│nng Workers│
│(17-24) │
└───────────┘
│
▼
┌─────────────────┐
│ AnuDB │
│ Core Engine │
└─────────────────┘
设置 MQTT 接口
- 安装并启动 Mosquitto MQTT 代理:
# 在 Ubuntu/Debian 上
sudo apt-get install mosquitto mosquitto-clients
sudo systemctl start mosquitto
# 在 Windows 上
# 从 https://mosquitto.org/download/ 下载并安装
# 通过 Windows 服务启动服务
- 运行 AnuDB MQTT 服务(在构建项目之后):
# 移动到 build/mqtt 文件夹
# Windows
# 使用 AnuDBMqttBridge 启动 AnuDB MQTT 服务 (TLS 示例)
.\AnuDBMqttBridge.exe --broker_url tls+mqtt-tcp://<mqtt broker URL>:8883 --database_name <您的数据库名称> --tls_cacert <ca-cert 路径>
# 使用 AnuDBMqttBridge 启动 AnuDB MQTT 服务 (不使用 TLS 的示例)
.\AnuDBMqttBridge.exe --broker_url mqtt-tcp://<mqtt broker URL>:1883 --database_name <您的数据库名称>
# Ubuntu/Debian
./AnuDBMqttBridge --broker_url mqtt-tcp://<mqtt broker URL>:1883 --database_name <您的数据库名称>
注意:如果在您的本地主机上安装了 mosquitto 代理,请在 <mqtt broker URL> 中使用 localhost。
AnuDBMqttBridge 的用法:
用法: AnuDBMqttBridge.exe --broker_url <url> --database_name <name> [--username <user>] [--password <pass>] [--tls_cacert <path>] [--tls_cert <path>] [--tls_key <path>] [--tls_pass <pass>]
使用 MQTT 客户端脚本
我们为 Linux (client.bash) 和 Windows (client.ps1) 环境提供了客户端脚本,以通过 MQTT 与 AnuDB 交互:
# 基本用法
./client.bash
# 运行负载测试
./client.bash --load-test --threads 16 --operations 1000
支持的 MQTT 命令
以下命令通过 MQTT 接口支持:
集合管理
命令 | 描述 | 示例 Payload
---|---|---
create_collection
| 创建一个新集合 | {"command":"create_collection","collection_name":"users","request_id":"req123"}
delete_collection
| 删除现有集合 | {"command":"delete_collection","collection_name":"users","request_id":"req123"}
get_collections
| 列出所有集合 | {"command":"get_collections","request_id":"req123"}
export_collection
| 将集合导出到指定目录中的 JSON 文件 | {"command":"export_collection","collection_name":"users","dest_dir":"./product_mqtt_export/","request_id":"req123"}
文档操作
命令 | 描述 | 示例 Payload
---|---|---
create_document
| 创建一个新文档(也用于更新) | {"command":"create_document","collection_name":"users","document_id":"user001","content":{"name":"John","age":30},"request_id":"req123"}
read_document
| 按 ID 读取文档 | {"command":"read_document","collection_name":"users","document_id":"user001","request_id":"req123"}
delete_document
| 删除文档 | {"command":"delete_document","collection_name":"users","document_id":"user001","request_id":"req123"}
注意:要更新文档,请使用 create_document
命令以及现有的文档 ID。 这将使用新内容覆盖先前的文档。
索引操作
命令 | 描述 | 示例 Payload
---|---|---
create_index
| 在字段上创建索引 | {"command":"create_index","collection_name":"users","field":"age","request_id":"req123"}
delete_index
| 删除索引 | {"command":"delete_index","collection_name":"users","field":"age","request_id":"req123"}
get_indexes
| 列出集合的所有索引 | {"command":"get_indexes","collection_name":"users","request_id":"req123"}
查询操作
命令 | 描述 | 示例 Payload
---|---|---
find_documents
| 查找与查询匹配的文档 | {"command":"find_documents","collection_name":"users","query":{"$eq":{"age":30}},"request_id":"req123"}
查询运算符
find_documents
命令支持以下查询运算符:
运算符 | 描述 | 示例
---|---|---
$eq
| 等式匹配 | {"$eq":{"field":"value"}}
$gt
| 大于 | {"$gt":{"field":value}}
$lt
| 小于 | {"$lt":{"field":value}}
$and
| 逻辑 AND | {"$and":[{"$eq":{"field":"value"}},{"$gt":{"field":value}}]}
$or
| 逻辑 OR | {"$or":[{"$eq":{"field":"value"}},{"$gt":{"field":value}}]}
$orderBy
| 排序结果 | {"$orderBy":{"field":"asc"}}
客户端脚本功能
提供的客户端脚本提供多种功能:
- 展示所有 MQTT 操作的交互式演示模式
- 用于测试特定命令的任意命令执行
- 具有可配置的线程和操作的负载测试
- 详细的性能报告
- 包含命令行选项的综合帮助
API 概览
数据库操作
操作 | 描述
---|---
Database(const std::string& path)
| 设置数据库路径的构造函数
Status open()
| 打开数据库
Status close()
| 关闭数据库
Status createCollection(const std::string& name)
| 创建一个新集合
Collection* getCollection(const std::string& name)
| 获取指向集合的指针
std::vector<std::string> getCollectionNames()
| 列出所有集合名称
Status dropCollection(const std::string& name)
| 删除集合
Status exportAllToJsonAsync(const std::string& collection, const std::string& outputDir)
| 将集合导出到 JSON
Status importFromJsonFile(const std::string& collection, const std::string& jsonFile)
| 将 JSON 数据导入到集合中
集合操作
操作 | 描述
---|---
Status createDocument(Document& doc)
| 创建一个新文档
Status readDocument(const std::string& id, Document& doc)
| 按 ID 读取文档
Status updateDocument(const std::string& id, const json& updateDoc, bool upsert = false)
| 更新文档
Status deleteDocument(const std::string& id)
| 删除文档
Status createIndex(const std::string& field)
| 在字段上创建索引
Status deleteIndex(const std::string& field)
| 删除索引
std::vector<std::string> findDocument(const json& query)
| 查找与查询匹配的文档,为了使查找操作有效,强制对字段进行索引
文档类
操作 | 描述
---|---
Document()
| 默认构造函数
Document(const std::string& id, const json& data)
| 带有 ID 和数据的构造函数
std::string id() const
| 获取文档 ID
void setId(const std::string& id)
| 设置文档 ID
json& data()
| 获取对文档数据的引用
void setData(const json& data)
| 设置文档数据
事务属性
AnuDB 利用 RocksDB 的存储引擎并继承以下事务特性:
- 原子操作:单个写入操作(创建、更新、删除)是原子的
- 预写日志:用于崩溃恢复的可配置 WAL
- 时间点快照:查询期间的读取一致性
查询操作
AnuDB 支持使用基于 JSON 的查询语言的各种查询操作:
运算符 | 描述 | 示例
---|---|---
$eq
| 等式匹配 | {"$eq": {"field": value}}
示例
$gt
| 大于 | {"$gt": {"field": value}}
示例
$lt
| 小于 | {"$lt": {"field": value}}
示例
$and
| 逻辑 AND | {"$and": [query1, query2, ...]}
示例
$or
| 逻辑 OR | {"$or": [query1, query2, ...]}
示例
$orderBy
| 排序结果 | {"$orderBy": {"field": "asc"}}
示例
更新操作
运算符 | 描述 | 示例
---|---|---
$set
| 设置字段值,并使用 '.' 支持嵌套字段 | {"$set": {"field": value}}
示例
$unset
| 删除字段,并使用 '.' 支持嵌套字段 | {"$unset": {"field": ""}}
示例
$push
| 添加到数组 | {"$push": {"array": value}}
示例
$pull
| 从数组中删除 | {"$pull": {"array": value}}
示例
使用示例
创建和填充集合
#include "Database.h"
#include <iostream>
anudb::Database db("./product_db");
db.open();
// 创建一个集合
db.createCollection("products");
anudb::Collection* products = db.getCollection("products");
// 创建文档
nlohmann::json productData = {
{"name", "Laptop"},
{"price", 1299.99},
{"category", "Electronics"},
{"specs", {
{"processor", "i9"},
{"ram", "32GB"}
}}
};
anudb::Document doc("prod001", productData);
products->createDocument(doc);
查询示例
// 查找所有电子产品
nlohmann::json query = {
{"$eq", {
{"category", "Electronics"}
}}
};
std::vector<std::string> results = products->findDocument(query);
// 价格范围查询
nlohmann::json priceRangeQuery = {
{"$and", {
{{"$gt", {{"price", 1000.0}}}},
{{"$lt", {{"price", 2000.0}}}}
}}
};
results = products->findDocument(priceRangeQuery);
更新示例
// 更新文档字段
nlohmann::json updateOp = {
{"$set", {
{"price", 1399.99},
{"specs.ram", "64GB"}
}}
};
products->updateDocument("prod001", updateOp);
// 向文档添加标签
nlohmann::json pushOp = {
{"$push", {
{"tags", "sale"}
}}
};
products->updateDocument("prod001", pushOp);
性能考虑
- 在需要 findDoc 操作的字段上创建索引
- 使用特定的查询而不是广泛的查询以获得更好的性能
- 大型集合的导出/导入操作可能很密集; 相应地计划
- 在嵌入式平台上,考虑使用 ZSTD 压缩以减少存储需求
- 根据您的设备功能调整内存预算和缓存大小
- 对于 MQTT 操作,利用 32 个并发 worker 线程以获得最佳吞吐量
- 考虑根据您的特定硬件功能调整 worker 线程数
- 对于大容量 MQTT 使用,请确保您的代理已针对预期负载正确配置
嵌入式平台优化
- 可配置的内存限制以在受约束的设备上工作
- ZSTD 压缩减少了存储占用空间和 I/O 操作
- 适用于嵌入式处理器的低 CPU 开销
- 最小的依赖关系以实现更小的部署大小
- 支持各种架构的交叉编译
- MQTT 接口在资源受限的设备上启用轻量级客户端
- TLS 支持可以有条件地编译以减少不需要时的二进制大小
限制
- 仅嵌入式; 没有客户端-服务器架构(通过 MQTT 接口除外)
- 没有内置的复制或分片
致谢
- 构建于 RocksDB 之上
- 使用 nlohmann/json 进行 JSON 解析
- 使用 MessagePack 进行序列化
- 使用 ZSTD 进行压缩(可选)
- 使用 NanoMQ 进行 MQTT 通信
- 使用 nng (nanomsg-next-generation) 进行 worker 线程架构
- 使用 mbedTLS 进行 TLS 安全
关于
AnuDB:基于 C++ 的文档存储,基于 RocksDB,具有本机 MQTT 支持
主题
lightweight rocksdb mqtt iot ai cpp nosql document-database messagepack zstd messagepack-serializer mosquitto-mqtt-broker nlohmann-json nanomq anudb