NanoDB 是一款基于 Rust 构建的嵌入式时序数据库,采用 TSM(Time‑Structured Merge)存储引擎,以 SQL 为核心交互语言,兼容 InfluxDB 行协议写入,适用于 IoT、监控指标、链路追踪等时序数据场景。
NanoDB 的数据组织在表中,对应一类时序数据(等同于 InfluxDB 中的 Measurement)。表结构通过 CREATE TABLE 预定义,包含列名、数据类型和主键。
| SQL 类型 | 存储类型 | 说明 |
|---|---|---|
INT / INTEGER / BIGINT |
Int64 | 64 位有符号整数 |
FLOAT / DOUBLE / REAL |
Float64 | 64 位浮点数 |
VARCHAR / TEXT / STRING |
Utf8 | 字符串 |
BOOLEAN / BOOL |
Boolean | 布尔值 |
TIMESTAMP |
Timestamp | 时间戳(纳秒) |
每张表必须包含 time 列(BIGINT 类型),存储纳秒级 Unix 时间戳,是时序数据的时间轴。
VARCHAR 类型的维度列(如 host、region),用于分类过滤cpu_usage、memory_bytes),存储实际测量值数据库是表和保留策略的命名空间,一个实例可管理多个数据库。
定义数据保留时长和分片周期。每个数据库创建时自动生成名为 autogen 的默认策略(分片周期 7 天)。
./nanodb
nanocli -host localhost -port 3000 -username admin -password yourpassword
Connected to http://localhost:3000 version 1.8
InfluxDB shell version: 0.1.0
>
> CREATE DATABASE mydb
> use mydb
Using database mydb
> CREATE TABLE cpu (
time BIGINT NOT NULL,
host VARCHAR,
region VARCHAR,
usage_user DOUBLE,
usage_system DOUBLE,
PRIMARY KEY (host)
)
写入通过行协议完成,详见写入数据章节。
> SELECT * FROM cpu WHERE host = 'server01' and time>$start and time<$end
time host region usage_user usage_system
---- ---- ------ ---------- ------------
1748000000000000000 server01 us-east 64.2 10.5
NanoDB 使用 config/config.toml 作为主配置文件:
[meta]
dir = "/path/to/meta" # 元数据存储目录(meta.db 所在位置)
[data]
dir = "/path/to/dbdata" # TSM 数据文件目录
wal-dir = "/path/to/waldata" # WAL 预写日志目录
[http]
bind-address = "0.0.0.0:3000" # HTTP 监听地址
auth-enabled = true # 是否开启认证
| 配置项 | 类型 | 说明 |
|---|---|---|
meta.dir |
string | 元数据(数据库、保留策略、用户)持久化目录 |
data.dir |
string | TSM 数据文件目录,建议使用高速磁盘 |
data.wal‑dir |
string | WAL 日志目录,建议与 data.dir 分开放置 |
http.bind‑address |
string | 服务监听地址,默认 0.0.0.0:3000 |
http.auth‑enabled |
bool | 启用后所有请求需认证,默认 false |
日志通过 config/log4rs.yaml 配置,支持文件滚动和日志级别调整。
使用 nanocli 客户端连接 NanoDB:
# 基本连接
nanocli -host localhost -port 3000
# 带认证连接
nanocli -host localhost -port 3000 -username admin -password yourpassword
# 连接后直接切换到指定数据库
nanocli -host localhost -port 3000 -username admin -password yourpassword -database mydb
连接成功后进入交互式 Shell:
Connected to http://localhost:3000 version 1.8
InfluxDB shell version: 0.1.0
>
| 命令 | 说明 |
|---|---|
use <database> |
切换当前数据库 |
format <json|csv|column> |
设置输出格式(默认 column) |
precision <ns|u|ms|s|m|h|rfc3339> |
设置时间戳精度(默认 ns) |
settings |
查看当前连接配置 |
help |
查看帮助 |
exit / quit / Ctrl+D |
退出客户端 |
> CREATE DATABASE mydb
> CREATE DATABASE IF NOT EXISTS mydb
创建成功后,系统自动生成名为 autogen 的默认保留策略(保留 7 天,分片周期 7 天)。
> DROP DATABASE mydb
> DROP DATABASE IF EXISTS mydb
> SHOW DATABASES
name: databases
name
----
mydb
metrics
iot
> use mydb
Using database mydb
切换后,后续所有 SQL 默认在 mydb 数据库中执行。
写入数据前须先定义表结构。time 列(纳秒时间戳)是必须的,PRIMARY KEY 必须且只能指定一个。
语法:
CREATE TABLE [database.]table_name (
time BIGINT NOT NULL, -- 必须:纳秒时间戳
tag_col VARCHAR, -- 标签列:字符串类型,用于分类过滤
field_col DOUBLE, -- 指标列:数值类型,存储实际测量值
...
PRIMARY KEY (tag_col) -- 必须,只能有一个
)
字段类型与行协议写入对应关系:
| 列类型 | 行协议写入示例 |
|---|---|
BIGINT |
count=100i |
DOUBLE / FLOAT |
value=3.14 |
VARCHAR / TEXT |
msg="hello" |
BOOLEAN |
active=true |
示例:
> CREATE TABLE cpu (
time BIGINT NOT NULL,
host VARCHAR,
region VARCHAR,
usage_user DOUBLE,
usage_system DOUBLE,
usage_idle DOUBLE,
PRIMARY KEY (host)
)
> CREATE TABLE disk (
time BIGINT NOT NULL,
host VARCHAR,
path VARCHAR,
bytes_used BIGINT,
bytes_free BIGINT,
PRIMARY KEY (host)
)
> SHOW TABLES
Tables_in_mydb
--------------
cpu
disk
memory
> SHOW COLUMNS FROM cpu
或使用 DESCRIBE / DESC:
> DESCRIBE cpu
> DESC cpu
Field Type Null
----- ---- ----
time Int64 NO
host Utf8 YES
region Utf8 YES
usage_user Float64 YES
usage_system Float64 YES
usage_idle Float64 YES
数据写入使用 InfluxDB 行协议(Line Protocol)。行协议格式如下:
<table>[,<tag>=<val>,...] <field>=<val>[,...] <timestamp_ns>
| 部分 | 说明 |
|---|---|
table |
表名,对应 CREATE TABLE 中的表名 |
tag=val |
标签键值对,逗号分隔,对应 VARCHAR 列 |
field=val |
指标键值对,空格后跟随,对应数值/布尔列 |
timestamp_ns |
纳秒 Unix 时间戳,对应 time 列 |
| 类型 | 写法 | 示例 |
|---|---|---|
| Float64 | 纯数字(无后缀) | 3.14 |
| Int64 | 数字 + i |
100i |
| UInt64 | 数字 + u |
200u |
| String | 双引号包裹 | "hello world" |
| Boolean | true / false |
true |
| 位置 | 需转义字符 |
|---|---|
| 表名 | , 和空格 |
| Tag Key / Value | , = 和空格 |
| Field Key | , = 和空格 |
| Field Value(字符串) | " 和 \ |
# CPU 监控数据(浮点指标)
cpu,host=server01,region=us-east usage_user=64.2,usage_system=10.5,usage_idle=25.3 1748000000000000000
# 网络请求计数(整数指标)
http_requests,host=web01,method=GET,status=200 count=1024i 1748000000000000000
# 磁盘使用量(无符号整数)
disk,host=server01,path=/data bytes_used=107374182400u,bytes_free=53687091200u 1748000000000000000
# 日志事件(字符串指标)
logs,host=server01,level=ERROR message="connection timeout after 30s" 1748000000000000000
# IoT 传感器(混合类型)
sensor,device_id=temp_001,location=room_a temperature=23.5,humidity=65.2,online=true 1748000000000000000
多条数据以换行符分隔,一次请求写入:
cpu,host=server01 usage_user=64.2,usage_system=10.5 1748000000000000000
cpu,host=server02 usage_user=32.1,usage_system=5.3 1748000000000000000
cpu,host=server03 usage_user=71.8,usage_system=15.2 1748000000000000000
提示: 写入使用 InfluxDB 兼容的
/write端点,请求体大小限制为 20 MB(解压后)。大批量写入建议启用 gzip 压缩。
所有查询在客户端 Shell 中直接输入 SQL 执行。NanoDB 使用 DataFusion 引擎,支持标准 SQL SELECT 语法。
> SELECT * FROM cpu
time host region usage_user usage_system usage_idle
---- ---- ------ ---------- ------------ ----------
1748000000000000000 server01 us-east 64.2 10.5 25.3
1748000000000000000 server02 us-west 32.1 5.3 62.6
> SELECT time, host, usage_user FROM cpu
> SELECT * FROM cpu WHERE host = 'server01'
> SELECT * FROM cpu WHERE host = 'server01' AND region = 'us-east'
time 列支持纳秒整数和时间字符串两种写法,时间字符串自动转换为 UTC 纳秒。
支持的时间字符串格式:
| 格式 | 示例 |
|---|---|
| RFC 3339 | '2024-05-23T10:00:00Z' |
| RFC 3339 带时区 | '2024-05-23T18:00:00+08:00' |
| 日期时间(UTC) | '2024-05-23 10:00:00' |
| 仅日期(UTC 零点) | '2024-05-23' |
> SELECT * FROM cpu WHERE time > '2024-05-23T00:00:00Z'
> SELECT * FROM cpu
WHERE time >= '2024-05-23 00:00:00'
AND time < '2024-05-24 00:00:00'
> SELECT * FROM cpu WHERE time BETWEEN '2024-05-23' AND '2024-05-24'
> SELECT host, AVG(usage_user) AS avg_cpu FROM cpu GROUP BY host
host avg_cpu
---- -------
server01 64.2
server02 32.1
server03 71.8
> SELECT host, MAX(usage_user) AS peak_cpu, MIN(usage_user) AS min_cpu
FROM cpu
WHERE time > '2024-05-23'
GROUP BY host
> SELECT COUNT(*) FROM cpu WHERE host = 'server01'
> SELECT * FROM cpu ORDER BY time DESC
> SELECT * FROM cpu ORDER BY time DESC LIMIT 10
> SELECT * FROM cpu ORDER BY time LIMIT 100 OFFSET 200
> use mydb
Using database mydb
> SELECT time, host, usage_user, usage_system
FROM cpu
WHERE host = 'server01'
AND time > '2024-05-23 00:00:00'
ORDER BY time DESC
LIMIT 50
time host usage_user usage_system
---- ---- ---------- ------------
1748086300000000000 server01 67.4 11.2
1748086200000000000 server01 65.8 10.9
1748086100000000000 server01 64.2 10.5
...
> SELECT host, AVG(usage_user) AS avg_cpu
FROM cpu
WHERE time > '2024-05-23'
GROUP BY host
ORDER BY avg_cpu DESC
host avg_cpu
---- -------
server03 71.8
server01 64.2
server02 32.1
JOIN、子查询、UNION / INTERSECT / EXCEPTFROM 子句只能引用单张表> SHOW RETENTION POLICIES
> SHOW RETENTION POLICIES ON mydb
name duration shardGroupDuration replicaN default
---- -------- ------------------ -------- -------
autogen 168h0m0s 168h0m0s 1 true
thirty_days 720h0m0s 24h0m0s 1 false
| 字段 | 说明 |
|---|---|
name |
保留策略名称 |
duration |
数据保留时长(人类可读) |
shardGroupDuration |
分片周期 |
replicaN |
副本数(当前固定为 1) |
default |
是否为该数据库的默认策略 |
语法:
CREATE RETENTION POLICY <name> ON <db>
DURATION <duration>
REPLICATION <n>
[SHARD DURATION <shard_duration>]
[DEFAULT]
Duration 支持的时间单位: s(秒)、m(分钟)、h(小时)、d(天)、w(周)
> CREATE RETENTION POLICY thirty_days ON mydb
DURATION 30d
REPLICATION 1
SHARD DURATION 1d
DEFAULT
> CREATE RETENTION POLICY one_year ON mydb
DURATION 365d
REPLICATION 1
SHARD DURATION 7d
语法:
ALTER RETENTION POLICY <name> ON <db>
DURATION <duration>
REPLICATION <n>
[SHARD DURATION <shard_duration>]
[DEFAULT]
> ALTER RETENTION POLICY autogen ON mydb
DURATION 90d
REPLICATION 1
SHARD DURATION 1d
DEFAULT
分片(Shard)是存储引擎按时间范围自动划分的最小存储单元,通常由系统自动管理。
> SHOW SHARDS
> SHOW SHARDS ON mydb
id database retention_policy start_time end_time expiry_time
-- -------- ---------------- ---------- -------- -----------
1001 mydb autogen 2024-05-16 00:00 UTC 2024-05-23 00:00 UTC 2024-05-23 00:00 UTC
1002 mydb autogen 2024-05-23 00:00 UTC 2024-05-30 00:00 UTC 2024-05-30 00:00 UTC
| 字段 | 说明 |
|---|---|
id |
分片唯一 ID |
database |
所属数据库 |
retention_policy |
所属保留策略 |
start_time |
分片时间范围起始 |
end_time |
分片时间范围结束 |
expiry_time |
数据过期时间 |
说明: 删除和压缩分片的操作通过管理接口执行,详见运维与监控。
> CREATE USER alice WITH PASSWORD 'secure_pass'
> CREATE USER admin WITH PASSWORD 'admin_pass' WITH ALL PRIVILEGES
语法:
GRANT <READ | WRITE | ALL [PRIVILEGES]> ON <database> TO <user>
> GRANT READ ON mydb TO alice
> GRANT WRITE ON mydb TO bob
> GRANT ALL PRIVILEGES ON mydb TO charlie
语法:
REVOKE <READ | WRITE | ALL [PRIVILEGES]> ON <database> FROM <user>
> REVOKE READ ON mydb FROM alice
> REVOKE ALL PRIVILEGES ON mydb FROM bob
| 权限 | SQL 操作 | 写入操作 |
|---|---|---|
READ |
SELECT、SHOW 类查询 | — |
WRITE |
CREATE / DROP / ALTER 类 DDL | 行协议写入 |
ALL |
全部读写操作 | 全部 |
认证缓存: NanoDB 对认证结果缓存 5 分钟(bcrypt 验证结果),密码修改后缓存自动失效。
curl http://localhost:3000/health
# 返回:health
适用于负载均衡器探活和容器健康检查。
curl http://localhost:3000/memory
# 返回当前已分配的堆内存字节数,如:52428800
NanoDB 使用 mimalloc 分配器并通过 TrackedAlloc 实时追踪堆内存。
curl http://localhost:3000/metrics
以 Prometheus 文本格式暴露运行时指标,可直接接入 Prometheus + Grafana:
| 指标名 | 类型 | 标签 | 说明 |
|---|---|---|---|
http_requests_total |
Counter | method, path, status |
HTTP 请求总数 |
http_requests_duration_seconds |
Histogram | method, path, status |
请求延迟分布 |
延迟桶边界(秒):0.005, 0.01, 0.025, 0.05, 0.1, 0.25, 0.5, 1, 2, 3, 5, 10
对指定分片触发 TSM 文件合并压缩,减少文件碎片、释放磁盘空间:
curl "http://localhost:3000/compact?shard_id=1001"
NanoDB 后台每 10 秒自动执行定期压缩,通常无需手动触发。
curl "http://localhost:3000/drop/shard?db=mydb&shard_id=1001"
curl http://localhost:3000/shutdown
# 返回:OK
触发后,服务完成当前请求后安全停止,存储引擎和 WAL 正常关闭。
在 nanocli 交互式 Shell 中支持以下客户端命令:
| 命令 | 说明 |
|---|---|
use <database> |
切换当前数据库 |
help |
查看帮助 |
exit / quit / Ctrl+D |
退出客户端 |
所有非以上命令的输入均作为 SQL 语句发送执行。
| 错误信息 | 原因 | 解决方案 |
|---|---|---|
authentication required |
未提供认证信息 | 连接时添加 -username 和 -password |
access denied |
用户权限不足 | 使用管理员账号执行 GRANT 授权 |
| 数据库 'xx' 已存在 | CREATE DATABASE 重复 |
改用 CREATE DATABASE IF NOT EXISTS |
| 数据库 'xx' 不存在 | 目标数据库不存在 | 先 SHOW DATABASES 确认名称 |
PRIMARY KEY 只能有 1 个 |
建表指定了多个主键 | 只保留一个 PRIMARY KEY 定义 |
CREATE TABLE 必须指定 PRIMARY KEY |
建表未指定主键 | 在列定义或表约束中加入 PRIMARY KEY |
| 不允许使用 JOIN 操作 | 查询包含 JOIN | 当前不支持多表连接 |
| 创建用户失败: user already exists | 用户名重复 | 换用不同用户名 |
| retention policy already exists | 策略名重复 | 改用 ALTER RETENTION POLICY 修改 |
WHERE 子句中 time 列支持字符串写法,引擎自动转为 UTC 纳秒:
-- 以下三种写法等价
WHERE time > 1716422400000000000
WHERE time > '2024-05-23T00:00:00Z'
WHERE time > '2024-05-23'
, 分隔,无空格i 后缀(count=10i),否则被解析为浮点msg="hello")> CREATE DATABASE iot
> use iot
Using database iot
> CREATE RETENTION POLICY iot_30d ON iot
DURATION 30d REPLICATION 1 SHARD DURATION 1d DEFAULT
> CREATE TABLE sensor_data (
time BIGINT NOT NULL,
device_id VARCHAR,
location VARCHAR,
temperature DOUBLE,
humidity DOUBLE,
online BOOLEAN,
PRIMARY KEY (device_id)
)
> SELECT time, device_id, temperature, humidity
FROM sensor_data
WHERE location = 'room_a' AND time > '2024-05-23'
ORDER BY time DESC LIMIT 100
> CREATE DATABASE monitor
> use monitor
Using database monitor
> CREATE TABLE http_requests (
time BIGINT NOT NULL,
service VARCHAR,
endpoint VARCHAR,
status_code BIGINT,
duration_ms DOUBLE,
PRIMARY KEY (service)
)
> SELECT service, endpoint, MAX(duration_ms) AS p99_ms
FROM http_requests
WHERE time > '2024-05-23T09:00:00Z'
GROUP BY service, endpoint
ORDER BY p99_ms DESC
service endpoint p99_ms
------- -------- ------
api /v1/orders 523.4
api /v1/users 312.1
web /dashboard 198.7
> SELECT service, COUNT(*) AS error_count
FROM http_requests
WHERE status_code >= 500 AND time > '2024-05-23'
GROUP BY service
ORDER BY error_count DESC
| 时间 | 纳秒值 |
|---|---|
| 1 秒 | 1,000,000,000 |
| 1 分钟 | 60,000,000,000 |
| 1 小时 | 3,600,000,000,000 |
| 1 天 | 86,400,000,000,000 |
| 7 天(autogen 默认) | 604,800,000,000,000 |
| 30 天 | 2,592,000,000,000,000 |