能,QuestDB 可作为 Go 服务主时序后端,需通过 PostgreSQL 协议(非 HTTP)连接,使用 pgx 批量写入,严格遵循 timestamp 命名、SYMBOL 类型选型及 symbol 容量配置。

QuestDB 能不能直接当 Go 服务的主时序后端?
能,但得绕开它默认的 HTTP/REST 接口走 PostgreSQL wire protocol——QuestDB 从 v6.0 开始原生兼容 PG 协议,Go 生态里用 pgx 或 database/sql + pgxpool 就能直连,性能比 REST 插入高 5–10 倍,且支持批量写、prepared statement、连接池复用。
别用 http.Post 往 /imp 端点发 CSV,那是给一次性导入或调试用的;实时高频写入必须走 PG 协议。
- QuestDB 默认监听
PG_PORT=8812(不是 5432),配置文件是conf/server.conf里的pg.port - 连接字符串格式:
postgres://admin:password@localhost:8812/qdb?sslmode=disable—— 注意sslmode=disable必须显式加,否则pgx默认启 SSL 导致连接失败 - 数据库名(
qdb)是固定值,QuestDB 不支持创建多个 DB,所有表都在这个逻辑库下
建宽表要注意哪些字段类型和约束?
QuestDB 对宽表(>100 列)友好,但列类型选错会直接拒绝插入或静默截断。核心原则:不用 STRING 存数值,不用 TIMESTAMP 当主键,时间列必须叫 timestamp(小写)且设为 DESIGNATED TIMESTAMP。
例如一个设备监控宽表:
立即学习“go语言免费学习笔记(深入)”;
CREATE TABLE metrics ( timestamp TIMESTAMP, device_id SYMBOL, cpu_usage DOUBLE, mem_used_bytes LONG, status SYMBOL, tags STRING, -- 少量 JSON 或 KV 文本可用 STRING,但别放高频查询字段 region SYMBOL, ... -- 其他 90+ 列 ) TIMESTAMP(timestamp) PARTITION BY DAY;
-
SYMBOL替代STRING存枚举类字段(如device_id、region),内存占用低、查询快;但 symbol cache size 默认 256MB,超限会 OOM,需调cairo.sql.symbol.capacity -
DOUBLE和LONG直接对应 Go 的float64/int64,避免用INT(只支持 32 位)存计数器 -
tags STRING这类不定长文本字段建议放最后,不影响前面列的向量化读取效率
Go 里怎么高效批量写入宽表?
单行 INSERT 在 QuestDB 里很慢,必须用 COPY 或 prepared insert + batch。推荐 pgx.Batch:它把多条 insert 合成一个网络包发过去,实测 1000 行/秒 → 15000 行/秒(本地 SSD,16 核)。
关键代码片段:
batch := &pgx.Batch{}
for _, m := range metrics {
batch.Queue(
"INSERT INTO metrics VALUES ($1, $2, $3, $4, $5, $6, $7)",
m.Timestamp, m.DeviceID, m.CPU, m.Mem, m.Status, m.Tags, m.Region,
)
}
br := pool.SendBatch(ctx, batch)
_, err := br.Exec()
if err != nil {
// 注意:QuestDB 返回错误如 "column 'xxx' not found" 是大小写敏感的,检查字段名是否全小写
}- 每 batch 最好控制在 100–1000 条之间;太大(>5000)可能触发 QuestDB 的
max-table-row-count限流 - 所有参数必须按
CREATE TABLE定义顺序传,不能跳列;NULL值要传 Go 的nil,不是空字符串 - 如果某列允许 NULL,但 Go struct 字段是
string非指针,得包装成sql.NullString,否则空值会变成空字符串写入
为什么查询变慢了?先查这三件事
QuestDB 查询快的前提是数据落在内存或 SSD 本地,一旦涉及跨 partition scan、symbol 字典膨胀、或没走 designated timestamp 过滤,延迟就上去了。
- 执行
EXPLAIN看是否用了TimestampIndex:如果 plan 里出现Full table scan,说明WHERE timestamp > ?没生效——确认 WHERE 条件用的是小写timestamp字段,且值是 nanosecond 级int64(QuestDB 内部用 ns 时间戳) - 查 symbol 字典大小:
SELECT count() FROM tables() WHERE name = 'metrics'看symbolMapCount,如果单个SYMBOL列 > 100 万 distinct 值,考虑拆分或改用STRING+ 建索引(但索引只对STRING有效) - 确认分区是否健康:
SELECT * FROM partitions WHERE tableName = 'metrics',如果active是 false,说明该 day 分区已 close,新写入会进新分区,但历史查询仍可跨分区合并
QuestDB 的“宽”和“快”是绑定在 schema 设计与协议使用方式上的,不是配完就能自动生效。最常被忽略的是 timestamp 字段命名强制小写、symbol 容量不调、以及误用 REST 接口扛实时流量。


















