Article

时间序列数据库QuestDB

更新于:2026-07-16

第一章:QuestDB 概述与核心特性

1.1 什么是 QuestDB

概念名称说明注意事项
QuestDB 定义QuestDB 是一个开源的高性能时序数据库(Time-Series Database),专为快速摄取和查询时间序列数据而设计,采用列式存储与向量化执行引擎。虽支持 SQL,但并非传统 OLTP 数据库,不适用于高并发事务处理。
开源协议基于 Apache 2.0 许可证开源,允许商业使用与修改。社区版功能完整,企业版提供额外运维工具(如 Web Console 增强、监控集成等)。
核心语言与架构使用 Java 和 C++ 编写,底层基于 SIMD(单指令多数据)优化和零拷贝内存管理。高性能依赖现代 CPU 架构(如 AVX2),老旧硬件可能无法发挥全部性能。
主要接口支持 InfluxDB Line Protocol (ILP)、PostgreSQL Wire Protocol、REST API、Web Console。所有写入最终统一为内部表结构,不同协议仅是入口方式不同。

1.2 QuestDB 的核心优势与适用场景

优势/场景名称说明注意事项
极致写入性能单节点可实现每秒数百万点的写入吞吐(如 400万+ points/sec),得益于无锁架构和列式追加写入。写入性能高度依赖时间戳有序性;乱序写入会触发重排,影响性能。
低延迟查询向量化执行引擎 + 列存 + 时间分区,使聚合查询响应在毫秒级。查询复杂度仍受限于数据量和过滤条件;不支持复杂 JOIN。
简化数据模型自动识别时间戳列,符号(Symbol)类型高效压缩高基数字符串(如设备ID、股票代码)。Symbol 类型需预先声明,不能动态变更;默认最大 distinct 值为 256(可配置)。
适用场景 - IoT适合海量传感器数据实时采集、存储与分析(如智能电表、车联网)。需配合消息队列(如 Kafka)做缓冲,避免突发流量压垮服务。
适用场景 - 金融适用于高频交易日志、行情 Tick 数据存储与回测。不支持 ACID 事务,不可用于账户余额等强一致性场景。
适用场景 - DevOps 监控可替代 Prometheus 远程存储,长期保存指标数据并支持 SQL 分析。需通过 Telegraf 或自定义 exporter 推送数据。

1.3 QuestDB 与其他时序数据库对比(InfluxDB、TimescaleDB 等)

对比维度QuestDBInfluxDB (OSS 2.x)TimescaleDB
存储模型列式存储,原生时序优化自研 TSM 引擎(列式)基于 PostgreSQL 的行存 + 自动分块(hypertable)
查询语言标准 SQL(扩展 LATEST BY、SAMPLE BY)Flux(函数式)或 InfluxQL(类 SQL)完全兼容 PostgreSQL SQL
写入协议ILP、PostgreSQL Wire、RESTLine Protocol、HTTP APIPostgreSQL 协议
性能特点写入极快,查询低延迟写入快,但 Flux 学习曲线陡峭写入中等,复杂查询能力强(继承 PG 生态)
高可用社区版仅单机;企业版支持主从复制(实验性)2.x 版本无内置集群(需企业版或自建)支持流复制、Patroni 集群,成熟 HA 方案
扩展性插件生态较弱,依赖外部系统集成有丰富 Telegraf 插件生态可直接使用 PostgreSQL 扩展(如 PostGIS、pg_cron)
适用人群追求极致性能、简单部署的开发者已使用 TICK Stack 的用户需要 SQL 兼容性与关系模型的团队
注意事项不支持 UPDATE / DELETE(仅 TTL 删除)2.x 资源占用高,社区活跃度下降写入性能低于专用 TSDB,需合理配置 chunk

第二章:安装与部署

2.1 支持的平台与系统要求

项目说明注意事项
操作系统Linux(x86_64, aarch64)、macOS(Intel & Apple Silicon)、Windows(通过 WSL2 或 Docker)官方不提供原生 Windows 二进制包;生产环境推荐 Linux。
CPU 架构x86_64(支持 AVX2 指令集)、ARM64(如 AWS Graviton)若 CPU 不支持 AVX2,需使用兼容模式(性能下降约 30%)。
内存要求最低 2 GB RAM,推荐 4 GB 以上内存用于缓存符号字典和 WAL 缓冲区;数据量大时建议 ≥8 GB。
磁盘空间至少 1 GB 可用空间(不含数据)数据存储路径需高性能 SSD;HDD 会显著降低写入吞吐。
Java 运行时自带嵌入式 JRE(无需单独安装)不依赖系统 Java;但若自行编译源码则需 JDK 17+。
网络端口默认使用 9000(HTTP/REST)、8812(PostgreSQL Wire)、9009(ILP TCP)、9003(Web Console)需确保防火墙开放对应端口;可修改配置文件自定义。

2.2 本地安装方式(Docker / Binary / Homebrew)

安装方式操作细节注意事项
Docker 安装执行命令:docker run -p 9000:9000 -p 8812:8812 -p 9009:9009 questdb/questdb首次运行会自动拉取镜像;数据默认存储在容器内(非持久化),建议挂载卷:-v "$(pwd)/qdb-data:/var/lib/questdb"
二进制安装(Linux/macOS)1. 下载 tar.gz 包(https://questdb.io/download)
2. 解压:tar -xzf questdb-*.tar.gz
3. 进入目录执行 ./questdb.sh start
启动脚本位于 bin/ 目录;需赋予执行权限(chmod +x);适用于无 Docker 环境。
Homebrew 安装(macOS)执行命令:brew install questdb,然后启动:questdb start仅支持 Intel 和 Apple Silicon;版本可能略滞后于官方发布。
Windows 安装推荐使用 Docker Desktop 或 WSL2 + 二进制包不支持直接运行 .exe;WSL2 需启用 systemd 或手动启动脚本。
验证安装访问 http://localhost:9000 或执行 curl http://localhost:9000/status若返回 JSON 格式的服务器状态信息,表示服务正常。

2.3 服务启动与基本配置

操作名称操作细节注意事项
启动服务在二进制目录执行:./questdb.sh start,或 Docker 中已自动启动启动后进程在后台运行;日志输出到 logs/ 目录。
停止服务执行:./questdb.sh stop强制 kill 可能导致 WAL 未刷盘,建议优雅停止。
配置文件位置conf/server.conf(二进制包)或挂载卷中的 conf/ 目录(Docker)修改配置后需重启服务生效。
常用配置项http.bind.to=0.0.0.0:9000pg.enabled=trueline.tcp.enabled=truehttp.auth.enabled=false默认仅绑定 localhost;生产环境需修改 bind 地址并启用认证。
数据目录默认为 rootDir/db/(相对路径)或通过 -d 参数指定可在启动时指定:./questdb.sh -d /data/questdb start
日志级别设置在 conf/log.properties 中修改:io.questdb.level=INFO调试时可设为 DEBUG,但会显著增加 I/O。

2.4 Web 控制台与 REST API 访问

访问方式用途代码示例 / 操作说明注意事项
Web Console图形化 SQL 查询界面,支持结果可视化浏览器访问 http://localhost:9000,默认无需登录(若未启用 auth)仅支持 SELECT 查询;不可执行 DDL/DML(如 CREATE、INSERT)。
REST API - 查询通过 HTTP 执行 SQL 查询curl -G --data-urlencode "query=SELECT * FROM trades LIMIT 5" http://localhost:9000/exec返回 JSON 格式;需 URL 编码查询语句;支持分页(count、cursor)。
REST API - 导入 CSV批量导入结构化数据curl -F data=@trades.csv -F schema="timestamp, symbol, price" -F name=trades http://localhost:9000/importCSV 首行为列名;时间戳列需为 ISO8601 或 Unix 微秒;表会自动创建。
REST API - 状态检查获取服务运行状态curl http://localhost:9000/status返回字段包括 version、uptime、memory usage 等。
PostgreSQL Wire 协议使用 psql 或任意 PG 客户端连接psql -h localhost -p 8812 -U admin -d qdb,密码为空(默认)仅支持有限 SQL 子集;不支持事务、存储过程等 PG 高级特性。
ILP TCP 写入测试快速测试写入`echo “trades,sym=ETH price=2500 1700000000000000”nc localhost 9009`

第三章:数据模型与表结构

3.1 表类型(普通表 vs 分区表)

表类型说明代码示例注意事项
普通表(Non-partitioned Table)无时间分区,所有数据存储在一个目录中CREATE TABLE events (ts TIMESTAMP, msg STRING);适用于小规模或非时序数据;不支持 TTL 自动清理。
分区表(Partitioned Table)按时间戳列自动按天/月/年等粒度分区存储CREATE TABLE trades (ts TIMESTAMP, symbol SYMBOL, price DOUBLE) TIMESTAMP(ts) PARTITION BY DAY;必须指定 TIMESTAMP 列;PARTITION BY 支持 DAY、MONTH、YEAR;分区提升查询剪枝效率。
自动分区行为写入时根据时间戳值自动归入对应分区目录分区粒度一旦设定不可更改;需在 CREATE TABLE 时指定。
分区目录结构/// 存储(如 trades/2024-01-01/手动删除分区目录可快速清理历史数据(需先停止写入)。
性能影响分区表在时间范围查询中显著减少 I/OSELECT * FROM trades WHERE ts > '2024-01-01';过细分区(如 HOUR)会增加文件句柄开销;推荐 DAY 或 MONTH。

3.2 时间戳列与分区策略

概念/操作说明代码示例注意事项
时间戳列定义必须使用 TIMESTAMP 类型,并通过 TIMESTAMP(ts) 指定为时间列CREATE TABLE logs (ts TIMESTAMP, level STRING) TIMESTAMP(ts);一张表只能有一个时间戳列;未显式指定则无法使用时间函数和分区。
时间戳单位支持微秒(默认)、纳秒(需显式声明)CREATE TABLE ticks (ts TIMESTAMP NANOS, px DOUBLE) TIMESTAMP(ts);ILP 写入时时间戳单位为纳秒;REST/SQL 默认为微秒。
分区策略选项PARTITION BY 支持:NONE、HOUR、DAY、MONTH、YEARCREATE TABLE sensor (ts TIMESTAMP, val DOUBLE) TIMESTAMP(ts) PARTITION BY MONTH;NONE 等价于普通表;HOUR 仅建议用于极高频写入场景。
分区对 TTL 的作用TTL(Time-To-Live)基于分区粒度自动删除过期数据ALTER TABLE sensor SET ttl = '7d';TTL 值必须是分区粒度的整数倍(如 DAY 分区不能设 12h TTL)。
乱序写入处理允许有限乱序(由 cairo.o3.max.lag 控制)超出 O3 最大延迟窗口的乱序数据会被拒绝或触发重排,影响性能。

3.3 符号(Symbol)类型详解

概念/参数说明代码示例注意事项
Symbol 类型定义高效存储低基数字符串(如设备ID、股票代码),内部映射为整数CREATE TABLE trades (ts TIMESTAMP, sym SYMBOL, price DOUBLE) TIMESTAMP(ts);不是普通字符串;不能直接 LIKE 或正则匹配(需 CAST(sym AS STRING))。
最大不同值限制默认最多 256 个 distinct Symbol 值CREATE TABLE events (cat SYMBOL CAPACITY 1024) ...可通过 CAPACITY 指定(如 64, 128, 256, 512, …, 1073741824);必须是 2 的幂。
缓存行为Symbol 字典常驻内存,加速 JOIN 和 GROUP BYSELECT sym, avg(price) FROM trades GROUP BY sym;高基数 Symbol(如用户ID)会导致内存爆炸,应改用 STRING。
写入性能优势比 STRING 快 2–5 倍,磁盘占用减少 50%+仅适用于已知枚举值或有限集合的字段。
与 ILP 兼容性ILP 中 tag 自动转为 Symboltrades,sym=ETH price=2500 1700000000000000若同一 tag 出现超 CAPACITY 个不同值,后续值将被丢弃或报错(取决于配置)。

3.4 列约束与数据类型支持

数据类型说明有效取值范围 / 格式注意事项
TIMESTAMP时间戳,微秒或纳秒精度'2024-01-01T12:00:00.000000Z' 或 Unix 微秒必须指定一列为时间列才能启用时序特性。
SYMBOL枚举式字符串(见 3.3)任意字符串(受 CAPACITY 限制)不支持 NULL;空字符串视为有效值。
STRING可变长 UTF-8 字符串最大长度约 2GB(理论)支持 NULL;适合高基数或全文内容。
DOUBLE64 位浮点数IEEE 754 双精度默认数值类型;支持 NaN、Infinity。
FLOAT32 位浮点数IEEE 754 单精度精度较低,节省空间;不推荐用于金融计算。
LONG64 位有符号整数-9,223,372,036,854,775,808 到 9,223,372,036,854,775,807适合计数器、ID 等。
INT32 位有符号整数-2,147,483,648 到 2,147,483,647节省内存;超出范围会溢出。
SHORT16 位有符号整数-32,768 到 32,767极少使用。
BYTE8 位有符号整数-128 到 127极少使用。
BOOLEAN布尔值true / false存储为单字节。
BINARY二进制数据(实验性)社区版支持有限;不推荐生产使用。
列约束QuestDB 不支持 PRIMARY KEY、FOREIGN KEY、NOT NULL(除 Symbol 外)所有列默认允许 NULL(Symbol 除外);无索引概念(靠分区和列存优化)。
自动类型推断REST / CSV 导入时自动推断类型时间戳需符合 ISO8601 或 Unix 微秒;否则可能被识别为 STRING。

第四章:数据写入

4.1 使用 ILP(Influx Line Protocol)写入

方法/要素语法 / 格式用途代码示例注意事项
ILP 基本格式<measurement>,<tag_key>=<tag_value> <field_key>=<field_value> <timestamp>高性能写入时序数据点trades,sym=ETH price=2500.5,qty=10i 1700000000000000字段值默认为 DOUBLE;整数需加后缀 i;时间戳单位为纳秒。
时间戳省略省略时间戳部分使用服务器当前时间sensors,device=dev01 temp=23.5仅适用于实时流写入;不推荐用于回填历史数据。
多字段写入单行包含多个 field一次写入多个指标cpu,host=server01 usage_idle=90.0,usage_user=8.5,usage_system=1.5 1700000000000000000字段间用逗号分隔,无空格;顺序无关。
Tag 与 Symbol 映射所有 tag 自动转为 SYMBOL 列高效存储分类维度logs,level=error msg="disk full" → level 列为 SYMBOL同一 tag 超过 CAPACITY 限制将被拒绝或丢弃(见 3.3)。
TCP 写入方式通过端口 9009 发送纯文本低延迟、高吞吐echo "trades,sym=BTC price=45000 1700000000000000" | nc localhost 9009支持批量多行(每行一个点);连接可复用。
HTTP 写入方式POST 到 /write,Content-Type: text/plain兼容 InfluxDB 客户端curl -i -X POST 'http://localhost:9000/write?precision=n' --data-raw 'trades,sym=ETH price=2500 1700000000000000000'precision 参数:n=纳秒(默认),u=微秒,ms=毫秒等。
表自动创建若表不存在,ILP 写入自动建表快速原型开发表结构由首次写入的 tag/field 推断;后续 schema 不可变。

4.2 使用 PostgreSQL Wire Protocol 写入

方法/要素语法 / 格式用途代码示例注意事项
连接参数host=localhost, port=8812, database=qdb, user=admin使用标准 PG 客户端连接 QuestDBpsql -h localhost -p 8812 -U admin -d qdb密码默认为空;支持任意语言的 PostgreSQL 驱动(如 psycopg2、pgx)。
CREATE TABLE标准 SQL DDL显式定义表结构(推荐)CREATE TABLE ticks (ts TIMESTAMP, symbol SYMBOL, bid DOUBLE) TIMESTAMP(ts) PARTITION BY DAY;必须指定 TIMESTAMP 列才能启用时序特性。
INSERT 语句标准 SQL INSERT单条或批量插入INSERT INTO ticks VALUES (now(), 'BTC', 45000.0); INSERT INTO ticks SELECT * FROM another_table;支持 now() 函数;不支持 ON CONFLICT、RETURNING 等 PG 特性。
批量插入(COPY)使用 COPY FROM STDIN高吞吐批量导入COPY ticks FROM STDIN WITH (FORMAT CSV); 随后发送 CSV 数据流需客户端支持;比逐条 INSERT 快 5–10 倍。
参数化查询使用预编译语句防止注入、提升性能PREPARE ins AS INSERT INTO ticks VALUES ($1, $2, $3); EXECUTE ins(now(), 'ETH', 2500.0);并非所有驱动都支持;建议使用绑定变量。
事务支持不支持 BEGIN/COMMIT所有写入立即生效;无法回滚。

4.3 使用 REST API 批量导入 CSV/JSON

方法/要素语法 / 格式用途代码示例注意事项
CSV 导入端点POST /import,multipart/form-data从文件批量导入结构化数据curl -F data=@trades.csv -F name=trades http://localhost:9000/importCSV 首行为列名;时间戳需为 ISO8601 或 Unix 微秒。
指定 Schema通过 schema 参数显式定义列类型避免类型推断错误curl -F data=@data.csv -F name=sensor -F schema="ts TIMESTAMP, dev SYMBOL, val DOUBLE" http://localhost:9000/import列顺序必须与 CSV 一致;类型名区分大小写。
自动建表若表不存在,根据 CSV 或 schema 自动创建快速导入表一旦创建,后续导入必须兼容 schema。
JSON 导入不直接支持 JSON;需转为 CSV 或用 ILP官方推荐:JSON 数据先转换为 ILP 或 CSV 再导入。
导入响应返回 JSON 格式结果获取导入统计{"rowCount":1000,"errorCount":0}errorCount > 0 表示部分行失败(如类型不匹配)。
大文件处理支持 GB 级 CSV 流式解析高效批量加载内存占用低;但单次请求不宜超过 10GB(受 HTTP 超时限制)。

4.4 写入性能调优建议

调优项操作细节代码/配置示例注意事项
保证时间戳有序按时间升序写入乱序写入触发 O3 重排,性能下降 50%+;若无法避免,增大 cairo.o3.max.lag
使用 ILP TCP 而非 HTTP减少协议开销nc localhost 9009 < data.ilpTCP 连接可复用;HTTP 每次请求有额外解析成本。
批量写入单次发送多行(ILP)或多行 INSERT(PG)ILP: 每次发送 5k–50k 行;PG: 使用 COPY 或批量 INSERT小包频繁写入会触发过多 I/O;建议每批 ≥1000 点。
调整 WAL 设置控制写前日志刷盘频率server.conf 中:wal.enabled=truewal.sync.on.write=false关闭 sync 可提升吞吐,但宕机可能丢最近数据;生产环境建议保留 WAL。
分区粒度合理避免过细分区PARTITION BY DAY(而非 HOUR)每个分区对应目录和文件句柄;HOUR 分区在高基数下易耗尽 fd。
Symbol CAPACITY 预估避免运行时扩容CREATE TABLE ... sym SYMBOL CAPACITY 4096Symbol 字典扩容需重建,影响写入;建议预留 2–5 倍余量。
磁盘与 CPU 优化使用 NVMe SSD + AVX2 CPUHDD 写入延迟高;无 AVX2 的 CPU 性能损失显著。
监控写入瓶颈查看 /status 和日志curl http://localhost:9000/status关注 o3.partition.active、wal.queue.size 等指标。

第五章:数据查询

5.1 SQL 查询语法基础

语法要素说明代码示例注意事项
SELECT 基本查询支持标准 SELECT 语句SELECT ts, symbol, price FROM trades LIMIT 10;所有列名区分大小写;默认按物理存储顺序返回(非时间序)。
WHERE 过滤支持等值、范围、IN、IS NULL 等SELECT * FROM logs WHERE level = 'error'; SELECT * FROM trades WHERE price > 1000;不支持正则或 LIKE(除非 CAST SYMBOL AS STRING);字符串需用单引号。
ORDER BY可按任意列排序SELECT * FROM trades ORDER BY ts DESC LIMIT 5;排序会显著增加内存和延迟;建议配合 LIMIT 使用。
LIMIT / OFFSET分页控制SELECT * FROM trades LIMIT 100 OFFSET 200;OFFSET 性能差,不适用于深度分页;推荐基于时间戳游标分页。
列别名支持 AS 重命名SELECT price AS px, ts FROM trades;别名可在 ORDER BY 中使用,但不能在 WHERE 中使用(SQL 标准行为)。
注释支持 --/* */-- This is a comment /* multi-line */Web Console 和 REST API 均支持。
不支持特性不支持子查询(部分场景除外)、窗口函数、CTE、HAVING(需用嵌套替代)复杂分析建议在应用层处理或导出到分析引擎。

5.2 时间范围过滤与时间函数

时间函数/操作语法用途代码示例注意事项
时间字面量ISO8601 或 RFC3339 格式指定绝对时间WHERE ts > '2024-01-01T00:00:00.000000Z'必须带时区(Z 表示 UTC);微秒精度。
now() 函数now()获取服务器当前时间WHERE ts > now() - 1h返回 TIMESTAMP 类型;常用于实时窗口查询。
时间间隔运算+ / - + 时间单位计算相对时间ts > now() - 5m ts < '2024-01-01' + 1d支持单位:s(秒)、m(分)、h(小时)、d(天)、w(周)、M(月)、y(年)。
date_trunc()date_trunc('unit', ts)截断时间到指定粒度SELECT date_trunc('hour', ts), avg(price) FROM trades GROUP BY 1;unit 支持:second、minute、hour、day、week、month、year。
to_timestamp()to_timestamp(str, format)字符串转时间戳to_timestamp('2024-01-01 12:00', 'yyyy-MM-dd HH:mm')format 遵循 Java SimpleDateFormat;慎用,性能较低。
时间分区剪枝自动优化WHERE ts BETWEEN '2024-01-01' AND '2024-01-02'查询仅扫描相关分区;仅对分区表生效;WHERE 条件需直接作用于时间列。

5.3 聚合查询与 downsample

聚合方法语法用途代码示例注意事项
标准聚合函数COUNT, SUM, AVG, MIN, MAX基础统计SELECT symbol, avg(price) FROM trades GROUP BY symbol;支持所有数值类型;STRING/SYMBOL 仅支持 COUNT。
SAMPLE BYSAMPLE BY interval [FILL(...)]按固定时间窗口降采样SELECT ts, avg(price) FROM trades SAMPLE BY 1h FILL(linear);必须与时间列一起使用;是 QuestDB 特有语法(非标准 SQL)。
FILL 策略FILL(prev), FILL(linear), FILL(null), FILL(constant)处理空窗口SAMPLE BY 1m FILL(prev)prev = 前向填充;linear = 线性插值;constant 需指定值(如 FILL(0))。
分组与时间对齐GROUP BY + date_trunc手动降采样SELECT date_trunc('minute', ts), avg(price) FROM trades GROUP BY 1;不保证连续时间轴;空窗口不会输出(与 SAMPLE BY 不同)。
多维聚合GROUP BY col1, col2多维度统计SELECT symbol, date_trunc('hour', ts), avg(price) FROM trades GROUP BY 1, 2;Symbol 列聚合效率高(得益于字典编码)。
性能提示避免 SELECT * 在聚合中聚合时只选择必要列可减少内存和 I/O。

5.4 最新值查询(LATEST BY)

语法要素说明代码示例注意事项
LATEST BY 基本用法获取每个分组的最新记录SELECT * FROM trades LATEST BY symbol;按指定列分组,返回每组时间戳最大的一行。
多列 LATEST BY支持多列组合分组SELECT * FROM events LATEST BY source, category;等价于”按 (source, category) 联合去重取最新”。
与 WHERE 结合先过滤再取最新SELECT * FROM trades WHERE ts > now() - 1d LATEST BY symbol;过滤在 LATEST BY 之前执行,提升性能。
与时间范围结合限定时间窗口内最新SELECT * FROM trades WHERE ts BETWEEN '2024-01-01' AND '2024-01-02' LATEST BY symbol;常用于每日快照场景。
性能优势利用时间序和索引跳转ROW_NUMBER() OVER (PARTITION BY ... ORDER BY ts DESC) 快 10–100 倍(后者不支持)。
限制不能与 ORDER BY、LIMIT 混用(除隐式外)LATEST BY 自身已定义输出顺序;额外排序无效。

5.5 JOIN 与子查询支持情况

特性支持情况代码示例注意事项
INNER JOIN✅ 支持(有限)SELECT t1.ts, t1.price, t2.name FROM trades t1 JOIN assets t2 ON t1.symbol = t2.symbol;仅支持等值 JOIN;右表必须是小表(广播 JOIN);大表 JOIN 性能极差。
LEFT JOIN✅ 支持SELECT ... FROM trades LEFT JOIN meta ON trades.sym = meta.id;同上,右表需小;不支持 RIGHT/FULL OUTER JOIN。
JOIN 条件限制仅支持单列等值ON a.x = b.y不支持复合条件(如 a.x = b.y AND a.z = b.w)或表达式。
子查询(FROM)✅ 支持派生表SELECT * FROM (SELECT symbol, avg(price) p FROM trades GROUP BY symbol) t WHERE p > 1000;可用于封装聚合结果;但不能嵌套过深。
子查询(WHERE)❌ 不支持 IN/EXISTS 子查询无法写 WHERE id IN (SELECT ...);需改用 JOIN 或应用层处理。
自连接⚠️ 理论支持,但不推荐SELECT a.ts, b.ts FROM trades a JOIN trades b ON a.symbol = b.symbol WHERE a.ts < b.ts;极易导致 OOM;QuestDB 非为复杂关系查询设计。
替代方案建议应用层关联或预聚合对于复杂分析,建议导出到 DuckDB、Pandas 或 ClickHouse。

第六章:性能优化与运维

6.1 分区管理与 TTL 设置

操作/配置项操作细节代码示例 / 配置注意事项
查看表分区列出所有分区目录或通过系统表SELECT * FROM tables();(查看 partitionBy 字段)或直接检查 db/<table>/ 目录分区信息不直接暴露为 SQL 表;需结合文件系统或元数据推断。
手动删除分区删除对应分区目录(需停写)rm -rf db/trades/2023-12-01/必须先停止对该表的写入,否则可能损坏 WAL 或导致查询异常。
设置 TTL(Time-To-Live)自动删除过期分区ALTER TABLE trades SET ttl = '30d';TTL 值必须是分区粒度的整数倍(如 DAY 分区不能设 12h);最小单位为秒(s)、分(m)、小时(h)、天(d)。
查看 TTL 设置查询表元数据SELECT name, ttl FROM tables() WHERE name = 'trades';返回值为微秒(如 30d = 2592000000000 µs)。
禁用 TTL清除自动清理策略ALTER TABLE trades SET ttl = null;原有已设置的 TTL 规则立即失效。
TTL 执行时机后台定时任务扫描默认每 10 分钟检查一次;可通过 cairo.check.partition.read.only.delay 调整。
分区对查询影响查询自动跳过无关分区SELECT * FROM trades WHERE ts > '2024-01-01';仅当 WHERE 条件直接作用于时间列时生效;函数包裹(如 date_trunc)可能失效。

6.2 WAL(Write-Ahead Log)机制与恢复

概念/操作说明配置/命令示例注意事项
WAL 作用保证写入持久性,支持崩溃恢复默认启用:wal.enabled=true(server.conf)所有 ILP 和 PG 写入先写 WAL,再异步刷入主存储。
WAL 文件位置存储在 <root>/wal/<table>/ 目录每个表独立 WAL;按 segment 分片(默认 32MB)。
强制刷盘控制 WAL 同步策略wal.sync.on.write=true(默认 false)设为 true 可防宕机丢数据,但显著降低吞吐(每次写触发 fsync)。
WAL 回放(恢复)服务重启时自动应用未刷盘日志启动日志显示 replaying WAL for table…;恢复期间表只读。
手动清理 WAL正常运行时自动归档/删除不要手动删除 wal/ 目录;可能导致数据丢失或启动失败。
WAL 状态监控查看队列积压curl http://localhost:9000/status → wal.queue.size若持续增长,说明磁盘 I/O 瓶颈或 CPU 不足。
禁用 WAL提升写入性能(不推荐生产)wal.enabled=false宕机可能丢失最近数秒至分钟级数据;仅用于测试或可容忍丢失场景。

6.3 内存与磁盘配置调优

配置项用途配置示例(server.conf)注意事项
cairo.sql.map.page.size控制内存映射页大小cairo.sql.map.page.size=4194304(4MB)默认 2MB;增大可减少 page fault,但增加内存占用。
cairo.o3.max.lag允许的最大乱序时间窗口cairo.o3.max.lag=120000000(120 秒,单位微秒)乱序写入超过此值将被拒绝;调大可容忍更多乱序,但增加内存缓冲。
cairo.cache.column.indexes是否缓存列索引cairo.cache.column.indexes=true对 Symbol 列聚合有帮助;默认 true。
cairo.cache.symbol.keys缓存 Symbol 字典键cairo.cache.symbol.keys=true减少重复解析开销;建议保持开启。
磁盘 I/O 优化使用 SSD + ext4/xfs避免使用 NFS 或网络存储;QuestDB 重度依赖本地低延迟 I/O。
内存分配JVM 堆外内存为主启动脚本中 -XX:MaxDirectMemorySize=8G主要内存消耗在堆外(列缓存、WAL buffer);JVM 堆可保持默认(1–2GB)。
文件描述符限制高分区/高并发需调高ulimit -n 65536每个分区、WAL segment、连接均消耗 fd;默认 1024 不足。

6.4 监控指标与日志分析

监控方式说明示例 / 路径注意事项
REST /status 接口获取核心运行指标curl http://localhost:9000/status返回 JSON,含 version、uptime、memory、WAL queue、O3 active partitions 等。
日志文件位置记录启动、错误、查询等logs/questdb.log默认 INFO 级别;调试时可改 log.properties 为 DEBUG。
慢查询日志自动记录超时查询需启用:pg.select.timeout=30000(毫秒)超时查询会中断并记录到日志;无专用 slowlog 文件。
关键日志关键词用于告警或分析O3 rollback, WAL replay, out of memory, partition closed出现 O3 rollback 表示乱序严重;WAL replay 表示恢复过程。
Prometheus 指标导出通过第三方 exporter社区项目:questdb-prometheus-exporter官方暂未内置 /metrics 端点;需额外部署 exporter。
Web Console 监控实时执行 EXPLAINEXPLAIN SELECT * FROM trades WHERE ts > now() - 1h;显示查询计划,包括是否使用分区剪枝、全表扫描等。
系统资源监控结合 top / iostat / vmstat关注 iowait(磁盘瓶颈)、CPU user%(向量化计算负载)、direct memory usage。

第七章:高可用与扩展

7.1 单机 vs 集群部署模式

部署模式说明适用场景注意事项
单机模式(Standalone)默认部署方式,所有数据存储于单节点开发测试、中小规模生产(如 ≤10 亿点/天)官方社区版仅支持此模式;简单、低运维成本、性能极致。
集群模式(QuestDB Enterprise)支持主从复制、读写分离(实验性)高可用要求场景(如金融、核心监控)仅企业版提供;需额外许可证;社区版无法启用。
无共享架构(Shared-Nothing)每个节点独立存储,无分布式协调QuestDB 不支持自动分片(sharding);无法水平扩展写入吞吐。
读扩展方案应用层路由只读查询到副本通过外部工具实现(如 pgpool-II)需手动同步数据(如 rsync + WAL replay),非原生支持。
写扩展限制无法跨节点分片写入所有写入必须指向单一主节点;高吞吐依赖单机垂直扩展(CPU/SSD)。
推荐架构单机 + 备份 + 监控绝大多数用户场景通过定期快照(snapshot)和 WAL 归档实现灾难恢复。

7.2 数据复制与故障转移(当前限制说明)

功能项支持情况说明注意事项
主从复制(Replication)⚠️ 企业版实验性支持基于 WAL 日志异步复制到只读副本社区版完全不支持;副本不可写;延迟取决于网络与负载。
自动故障转移(Failover)❌ 不支持无内置 leader election 或 VIP 切换需依赖外部工具(如 HAProxy + 自定义健康检查脚本)。
手动故障恢复✅ 可行(社区版)1. 停止主节点;2. 将备份数据复制到新节点;3. 启动服务恢复时间取决于数据量;WAL 可用于重放未刷盘数据。
数据一致性最终一致(企业版)异步复制,可能丢失最近写入RPO > 0;不满足强一致性要求。
备份策略✅ 支持物理快照cp -r db/ backup_$(date +%s)/(需停写或冻结文件系统)推荐使用 LVM snapshot 或 ZFS send/receive 实现在线备份。
PITR(时间点恢复)⚠️ 有限支持结合 WAL + 快照可恢复到最近 checkpoint无内置工具;需手动解析 WAL segment(高级操作,风险高)。
社区版高可用建议应用层重试 + 多活写入队列写入前经 Kafka/Pulsar 缓冲,主库宕机时切换消费者数据仍写入单实例,但避免直接丢弃。

7.3 与 Kafka / Telegraf / Prometheus 集成

集成组件集成方式配置/代码示例注意事项
Kafka → QuestDB使用 Kafka Connect 或自定义消费者1. 启用 ILP TCP(端口 9009);2. 编写消费者将 JSON 转为 ILP 并发送:"trades,sym=BTC price=45000 1700000000000000"推荐使用 QuestDB 官方 Kafka Connector(GitHub 社区项目);确保时间戳有序。
Telegraf → QuestDB配置输出插件为 InfluxDB在 telegraf.conf 中:[[outputs.influxdb_v2]]urls = ["http://questdb:9000"]precision = "ns"必须使用 InfluxDB v2 输出插件并指向 QuestDB 的 /write 端点;precision 设为 ns。
Prometheus → QuestDB作为远程存储(Remote Write)1. 启用 QuestDB ILP HTTP;2. 在 prometheus.yml 中:remote_write: - url: "http://questdb:9000/write?precision=ms"Prometheus 时间戳单位为毫秒,需加 ?precision=ms;标签自动转为 Symbol。
QuestDB → Grafana通过 PostgreSQL 数据源连接在 Grafana 添加 PG 数据源:Host: questdb:8812、Database: qdb、User: admin支持 SQL 查询面板;不支持变量自动补全(因无 information_schema 完整支持)。
日志采集(Filebeat/Fluentd)转为 ILP 后写入自定义 pipeline 将日志字段映射为 tag/field时间戳字段需提取为纳秒时间戳;避免高基数字段设为 Symbol。
批量回填历史数据使用 REST /import + CSVcurl -F data=@history.csv -F name=metrics http://questdb:9000/import确保 CSV 时间戳列格式正确;大文件分批次导入防超时。

第八章:实战案例

8.1 IoT 设备数据采集与可视化

步骤/组件操作细节代码/配置示例注意事项
表结构设计按设备 ID 分组,存储传感器值CREATE TABLE sensors (ts TIMESTAMP, device_id SYMBOL CAPACITY 1024, metric SYMBOL, value DOUBLE) TIMESTAMP(ts) PARTITION BY DAY;device_id 和 metric 使用 SYMBOL 提升压缩率与查询速度;避免将设备 ID 存为 STRING。
数据写入(边缘设备)通过 HTTP ILP 推送POST /write?precision=ms,Body: sensors,device_id=sensor_001,metric=temp value=23.5 1700000000000时间戳单位为毫秒(因多数 IoT SDK 输出 ms);需加 ?precision=ms
批量写入(网关聚合)边缘网关缓存后批量发送每 5 秒拼接多行 ILP 通过 TCP 发往 9009 端口减少连接开销;确保时间戳有序(按采集时间排序)。
实时查询最新状态获取每个设备最新读数SELECT * FROM sensors LATEST BY device_id, metric;利用 LATEST BY 高效实现”设备状态快照”。
降采样趋势分析按小时聚合平均值SELECT ts, device_id, avg(value) FROM sensors SAMPLE BY 1h FILL(prev) WHERE metric = 'temp';FILL(prev) 避免图表断点;适用于 Grafana 可视化。
可视化(Grafana)添加 PostgreSQL 数据源,写 SQL 面板SELECT ts as time, value FROM sensors WHERE device_id = '$device' AND metric = 'temp' ORDER BY ts;Grafana 变量 $device 需手动定义;time 列必须别名为 time。
异常阈值告警应用层轮询或规则引擎查询:SELECT device_id, max(value) FROM sensors WHERE ts > now() - 5m GROUP BY device_id;QuestDB 无内置告警;需结合 Prometheus Alertmanager 或自定义脚本。

8.2 金融行情 Tick 数据存储与回测

步骤/组件操作细节代码/配置示例注意事项
表结构设计存储逐笔成交(Tick)CREATE TABLE ticks (ts TIMESTAMP NANOS, symbol SYMBOL, side SYMBOL, price DOUBLE, size LONG) TIMESTAMP(ts) PARTITION BY DAY;时间戳精度设为 NANOS(纳秒);symbol 和 side 用 SYMBOL 节省空间。
高频写入优化使用 ILP TCP 直连客户端持续发送:ticks,symbol=AAPL,side=BUY price=190.25,size=100i 1700000000000000000单连接复用;每批 ≥1000 行;确保时间戳严格递增。
VWAP 计算(成交量加权均价)按分钟聚合SELECT ts, sum(price * size) / sum(size) AS vwap FROM ticks SAMPLE BY 1m WHERE symbol = 'BTC';利用 SAMPLE BY 保证时间轴连续;避免使用 GROUP BY + date_trunc(可能漏空窗)。
回测数据提取导出指定时间段全量 TickCOPY (SELECT * FROM ticks WHERE symbol = 'ETH' AND ts BETWEEN '2024-01-01' AND '2024-01-02') TO '/data/eth_20240101.csv';COPY TO 需启用(默认关闭);路径需在 server.conf 中配置 http.min.enabled=true 并授权。
最新买卖价(模拟 Level1)假设 side 区分买卖SELECT side, price FROM ticks LATEST BY symbol, side WHERE symbol = 'BTC';实际 Level2 需订单簿重建,QuestDB 不直接支持;此为简化模型。
性能保障分区 + SSD + AVX2 CPU日均 10 亿 Tick 需 ≥32GB RAM、NVMe SSD;避免乱序写入。
数据生命周期设置 90 天 TTLALTER TABLE ticks SET ttl = '90d';符合法规与成本控制;自动清理旧分区。

8.3 日志监控与异常检测

步骤/组件操作细节代码/配置示例注意事项
表结构设计存储结构化日志CREATE TABLE app_logs (ts TIMESTAMP, service SYMBOL, level SYMBOL, message STRING, trace_id STRING) TIMESTAMP(ts) PARTITION BY HOUR;按 HOUR 分区适应高吞吐日志;service/level 用 SYMBOL 加速过滤。
日志采集(Telegraf)配置 tail 输入 + InfluxDB 输出[[inputs.tail]] files = ["/var/log/app/*.log"] data_format = "json" [[outputs.influxdb_v2]] urls = ["http://questdb:9000"]JSON 日志需含 ts、service、level 字段;Telegraf 自动转为 ILP。
错误日志统计每分钟 error/warn 计数SELECT ts, count(*) FROM app_logs WHERE level IN ('error', 'warn') SAMPLE BY 1m FILL(0);FILL(0) 确保无错误时显示 0,便于告警基线计算。
异常突增检测应用层对比滑动窗口查询最近 5 分钟 vs 前 5 分钟:SELECT count(*) FROM app_logs WHERE level='error' AND ts > now() - 5m;QuestDB 无内置 anomaly detection;需外部服务(如 Python 脚本)调用 REST API 分析。
关联追踪按 trace_id 查询全链路SELECT ts, service, message FROM app_logs WHERE trace_id = 'abc123' ORDER BY ts;trace_id 为 STRING(高基数),不建议设为 SYMBOL。
日志保留策略7 天 TTLALTER TABLE app_logs SET ttl = '7d';日志数据量大,短 TTL 控制成本。
可视化(Grafana)构建服务健康仪表盘面板 1:错误率(error / total);面板 2:各服务日志量热力图使用变量 $service 动态过滤;避免 SELECT * 导致内存溢出。