QuestDB LATEST ON / LATEST BY 查询十几秒慢,核心原因
几十亿行,
LATEST ON timestamp PARTITION BY device_id,pname慢到十几秒,90% 都是下面 4 个坑QuestDB Co...
1、device_id /pname 没有设置成SYMBOL(头号元凶)
如果你的device_id、pname是STRING/VARCHAR而不是SYMBOL:LATEST ON必须扫描全表,找出全部不重复设备,再反向扫描时间,几十亿数据直接十几秒。
✅正确建表:
sql
CREATE TABLE flow_meter_records ( timestamp TIMESTAMP, device_id SYMBOL CAPACITY 50000 CACHE, pname SYMBOL CAPACITY 200 CACHE, value DOUBLE ) TIMESTAMP(timestamp) PARTITION BY DAY WAL;SYMBOL:重复多的字符串,内部字典编码,LATEST ON可以反向分区扫描,找到每个设备最新行立刻停止,毫秒级返回QuestDB Co...。- ❌不要用 STRING 做 PARTITION BY 字段。
⚠️已经建好的表不能直接改列类型,只能新建表迁移数据。
2、不要不带时间条件查全部历史分区
几十亿,几百个历史分区,LATEST ON会从最早分区开始扫。
优化:加一个大时间窗口 WHERE,告诉数据库不用扫描很早的旧分区
sql
SELECT * FROM flow_meter_records LATEST ON (timestamp) PARTITION BY device_id,pname WHERE timestamp > dateadd('d',‑90,now());只扫描最近 90 天分区,跳过几年前冷分区,速度直接几倍提升。 业务上设备最新数据一定发生在最近几十天,很久没有上报的设备直接过滤掉。
3、Designated Timestamp 丢失
timestamp必须是表的TIMESTAMP()指定字段,不能套 cast 函数。套cast(timestamp as timestamp)会破坏分区裁剪,变成全扫描GitHub。
❌错误:
sql
LATEST ON (cast(timestamp as timestamp)) PARTITION BY device_id,pname✅正确:
sql
LATEST ON (timestamp) PARTITION BY device_id,pname4、老版本 QuestDB Bug
老版本LATEST ON高基数设备性能差,尽量升级到新版本。
两种业务方案(水务项目实际落地)
方案 A:查询所有设备最新值(用上面优化后的 SQL)
sql
SELECT * FROM flow_meter_records LATEST ON (timestamp) PARTITION BY device_id,pname WHERE timestamp > dateadd('d',‑90,now());前提:device_id、pname是SYMBOL类型。
方案 B(强烈推荐,生产环境):维护一张最新值快照表
几十亿原始表永远不要频繁跑
LATEST ON做接口查询!
- 定时任务(1 分钟 / 5 分钟)跑一次 LATEST ON,把每个设备最新结果写入一张小表
flow_meter_latest。 - Java 接口直接查询这张小快照表,毫秒返回,0 压力。
- 原始大表只做历史查询、报表统计。
示例:
sql
-- 定时任务执行,覆盖更新最新状态 INSERT INTO flow_meter_latest SELECT * FROM flow_meter_records LATEST ON (timestamp) PARTITION BY device_id,pname WHERE timestamp > dateadd('d',‑90,now()) ON CONFLICT(device_id,pname) DO UPDATE;接口直接查:
sql
SELECT * FROM flow_meter_latest where device_id = ?;排查命令,看是不是扫描大量分区
sql
EXPLAIN SELECT * FROM flow_meter_records LATEST ON (timestamp) PARTITION BY device_id,pname WHERE timestamp > dateadd('d',‑90,now());看输出,确认有partition pruning(分区裁剪生效)。
补充:查单个设备的最新一条
不要用 LATEST ON,直接排序取第一条:
sql
SELECT * FROM flow_meter_records WHERE device_id='DEV001' AND pname='累计流量' ORDER BY timestamp DESC LIMIT 1;device_id 为 SYMBOL,这个查询会非常快。
总结
device_id、pname务必设置为SYMBOL,不要用 string;LATEST ON查询必须加时间条件,裁剪旧分区;- 高频接口不要直接查几十亿大表,维护一张小的最新快照表;
- designated timestamp 不要套 cast 函数。