QuestDB时序数据库快速入门

简介

QuestDB是一个开源的高性能时序数据库,专门用于处理时间序列相关的数据存储与查询;

QuestDB使用列式存储模型。数据存储在表中,每列存储在其自己的文件和其自己的本机格式中。新数据被附加到每列的底部,以便能够按照与摄取数据相同的顺序有机地检索数据。

随着业务快速发展,使得海量数据在传统关系型数据库上性能瓶颈问题,转移到QuestDB时序数据库上后得到性能的极大提升,解决了海量数据高性能快速读写与简易管理问题;

QuestDB能够支持快速增长的时间序列高基数数据,提供强大的极速查询性能,安装维护简单并兼容SQL语法,从而上手使用学习难度低,并且支持RestaApi数据摄取与查询接口,对系统应用服务开发提供良好支持,非常方便系统服务的集成开发与应用;QuestDB采用Java和C++ 从头开始构建,没有依赖项,零垃圾回收;

官方说明:

QuestDB 是一个专门研究 时间序列的开源列式数据库。它提供同类领先的摄取吞吐量和快速 SQL 查询,并且操作简单。QuestDB 有助于降低运营成本并克服摄取瓶颈,并且可以大大简化整体入口基础设施。凭借对 InfluxDB Line Protocol 和 PostgreSQL Wire Protocol 等摄取协议、第三方工具和语言客户端的广泛官方支持,可以快速启动。

QuestDB在默认原生版本(vanilla)配置下,在知名数据库测试机构benchANT的时序数据库排行榜 Time Series: DevOps 场景写入吞吐量、存储占用、查询响应、成本效益等多项性能表现的第二名(本文编写时间前)。

benchANT 是国际知名的数据库评测机构,以可靠、独立及透明的方法对各种数据库进行性能评测。 benchANT 榜单收录了常见的关系型数据库、NoSQL 数据库、NewSQL 数据库及时序数据库等,通过使用固定的测试负载、相同的测试机器来保证测试结果的公平性。

榜单链接:https://benchant.com/de/ranking/datenbank-ranking

主要功能

1.大规模数据写入:默认使用4个线程,每秒处理速度接近100W行,有极强吞吐性能

2.支持SQL分析:支持SQL语法,可以采用PostgreSQL协议线路客户端查询数据

3.支持数据分区:支持自动对数据按时间进行分区存储,如按小时、天、周、月、年等;

4.提供web控制台:安装后可以在自带的WEB控制台界面管理数据表与数据

5.支持多种数据访问方式:提供基于http的RestAPI,无需集成驱动包;同时支持Postgres协议、InfluxDB协议数据传输;降低集成开发难度;支持csv数据导入;

6.支持多种时间序列SQL扩展:支持按时间间隔、时间范围、窗口时间排序、多表关联序列查询等扩展用法

更多功能请关注官网文档

安装与使用

官网地址:QuestDB | High performance time series

官网文档:Introduction | QuestDB

官方下载:Download QuestDB | QuestDB

github:https://github.com/questdb/questdb

linux环境安装

官方安装指南:Quick start | QuestDB

测试服务器硬件环境: CPU(E5-2609 v3 @ 1.90GHz) *8核,内存*16G,硬盘 > 70G

#下载安装包
wget https://github.com/questdb/questdb/releases/download/7.3.7/questdb-7.3.7-rt-linux-amd64.tar.gz
#解压包
tar -zxvf questdb-7.3.7-rt-linux-amd64.tar.gz
#移动目录
mv questdb-7.3.7-rt-linux-amd64 /opt/questdb-7.3.7
#进入目录
cd /opt/questdb-7.3.7
#创建数据目录
mkdir -p data
#启动数据库
./bin/questdb.sh start -d ./data -t questdb
#查询数据库状态
./bin/questdb.sh status -d ./data
#关闭数据库
./bin/questdb.sh stop -d ./data -t questdb

注意:此处安装开源版本是以单机部署;只有企业版才支持集群、权限管理等功能;

访问WEB控制台

QuestDB提供了WebUI版的控制台客户端界面,可通过IP+端口在浏览器中访问,如:http://localhost:9000

启动后QuestDB会启动如下端口:

  • 9000:REST API和 Web 控制台
  • 9009:InfluxDB线路协议
  • 8812:Postgres 有线协议
  • 9003:最小健康服务器

注:相关端口与配置可通过 questdb-7.3.7/data/conf/server.conf 中进行更改;

基础使用

官方提供了几个场景类型的示例数据,如:天气、金融等示例数据集;

官方示例数据:https://github.com/questdb/sample-datasets

示例数据集

该数据集约160K行,为美国芝加哥气象站传感器数据;

示例数据:https://github.com/questdb/sample-datasets/tree/main/chicago_sensors

下载:https://github.com/questdb/sample-datasets/blob/main/chicago_sensors/chicago_weather_stations.csv

创建数据表

注意:questDb没有database数据库独立实例,默认为qdb,因此只需要建数据表即可;

CREATE TABLE IF NOT EXISTS chicago_weather_stations (MeasurementTimestamp TIMESTAMP,StationName SYMBOL,AirTemperature DOUBLE,WetBulbTemperature DOUBLE,Humidity INT,RainIntensity DOUBLE,IntervalRain DOUBLE,TotalRain DOUBLE,PrecipitationType INT,WindDirection INT,WindSpeed DOUBLE,MaximumWindSpeed DOUBLE,BarometricPressure DOUBLE,SolarRadiation INT,Heading INT,BatteryLife DOUBLE,MeasurementTimestampLabel STRING,MeasurementID STRING
) timestamp(MeasurementTimestamp) PARTITION BY MONTH WAL
DEDUP UPSERT KEYS(MeasurementTimestamp, StationName);

简要说明:MeasurementTimestamp为数据库引擎时序分区字段,此表需要基于MONTH(月)时间分区,必需要有timestamp字段以进行数据写入时自动表分区;

导入csv数据集

curl -F data=@chicago_water_sensors.csv "http://localhost:9000/imp?name=chicago_water_sensors"

SQL演示

官方文档提供了大量SQL使用说明,参阅:SQL execution order | QuestDB

在官方的建议里,一旦创建数据表后,尽可能不要改变表结构,因为大量数据分区存储后,会在变更表过程中造成较大的性能开销,和冷热数据处理;

本章描述几个常见SQL用法;

创建表

-- 创建表,IF NOT EXISTS 如果表不存在则创建;
-- PARTITION BY MONTH  表示按MONTH(月)分区存储;
-- WAL表示支持预写入内存再刷新到磁盘,用于数据并发写入;
CREATE TABLE IF NOT EXISTS test_demo (
id INT,
name STRING,
value STRING,
ts TIMESTAMP
)  TIMESTAMP(ts) PARTITION BY MONTH WAL;

增改查

-- 插入
INSERT INTO test_demo (id, name, value ,ts) VALUES(100,'test','abc', to_timestamp('2023-12-30T00:00:00', 'yyyy-MM-ddTHH:mm:ss'));
INSERT INTO test_demo (id, name, value ,ts) VALUES(101,'test','abc', now());
-- 更改(在questdb表中所有数据落库后,尽量不要update改变记录,目前不提供表记录删除,但支持清空表)
update test_demo set id=102 where ts = to_timestamp('2023-12-30T00:00:00', 'yyyy-MM-ddTHH:mm:ss')
-- 查询
select * from test_demo

表删除

-- 删除表指定月分区
ALTER TABLE test_demo DROP PARTITION LIST '2013-12';
-- 清空表(截断)
TRUNCATE TABLE test_demo
-- 删除表
DROP TABLE test_demo

show用法

-- 显示所有表
SHOW TABLES;
-- 显示指定表字段
SHOW COLUMNS FROM test_demo;
-- 显示指定表分区
SHOW PARTITIONS FROM test_demo;
-- 显示参数
SHOW PARAMETERS;
-- 显示版本
SHOW SERVER_VERSION;

开发测试

本示例项目依赖Maven + JDK17,此处不再单独描述创建项目过程,请自行准备运行环境与工程;

开发环境硬件:CPU (I5-7500 3.40GHz) * 4核,内存 * 24G

RestAPI请求

QuestDB支持REST API开发模式,可基于标准HTTP功能响应请求,因此可通过http客户端访问。

java示例

package com.example.questdb.restapi;import org.junit.jupiter.api.Test;
import java.net.URI;
import java.net.URLEncoder;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import java.nio.charset.StandardCharsets;
/*** @Description 通过questDb的restApi接口执行sql脚本* @Version V1.0*/
public class HttpTest {//查询@Testpublic void query() throws Exception {java.net.http.HttpClient client = java.net.http.HttpClient.newHttpClient();String url = "http://192.168.1.3:9000/exec?";//URL 编码的查询文本String queryStr = "query=" + URLEncoder.encode("select * from test_demo", StandardCharsets.UTF_8);//计算行数并返回该值String countStr = "&count=true";//返回前多少行String limitStr = "&limit=2";//拼装url = url + queryStr + countStr + limitStr;System.out.println("请求:" + url);HttpRequest request = HttpRequest.newBuilder().uri(new URI(url)).header("Content-Type", "application/x-www-form-urlencoded;charset=utf-8").header("Statement-Timeout", "5000").GET().build();HttpResponse<String> response = client.send(request, HttpResponse.BodyHandlers.ofString());String body = response.body();System.out.println("响应:" + body);}//插入@Testpublic void insert() throws Exception {java.net.http.HttpClient client = java.net.http.HttpClient.newHttpClient();String url = "http://192.168.1.3:9000/exec?";//URL 编码的查询文本String queryStr = "query=" + URLEncoder.encode("INSERT INTO test_demo (id, name, value ,ts) VALUES(104,'test','abc', now())", StandardCharsets.UTF_8);url = url + queryStr;System.out.println("请求:" + url);HttpRequest request = HttpRequest.newBuilder().uri(new URI(url)).header("Content-Type", "application/x-www-form-urlencoded;charset=utf-8").header("Statement-Timeout", "5000").GET().build();HttpResponse<String> response = client.send(request, HttpResponse.BodyHandlers.ofString());String body = response.body();System.out.println("响应:" + body);}//更改@Testpublic void update() throws Exception {java.net.http.HttpClient client = java.net.http.HttpClient.newHttpClient();String url = "http://192.168.1.3:9000/exec?";//URL 编码的查询文本String queryStr = "query=" + URLEncoder.encode("UPDATE test_demo SET name='test4' WHERE id=104", StandardCharsets.UTF_8);url = url + queryStr;System.out.println("请求:" + url);HttpRequest request = HttpRequest.newBuilder().uri(new URI(url)).header("Content-Type", "application/x-www-form-urlencoded;charset=utf-8").header("Statement-Timeout", "5000").GET().build();HttpResponse<String> response = client.send(request, HttpResponse.BodyHandlers.ofString());String body = response.body();System.out.println("响应:" + body);}
}

数据表记录

InfluxDB协议

QuestDB TCP接收器使用 InfluxDB 线路协议作为序列化和传输格式。另一方面,InfluxDB使用HTTP协议作为传输,QuestDB主要使用InfluxDB线路协议作为数据序列化格式。因此,现有的InfluxDB客户端库将无法与QuestDB一起使用,必需使用QuestDB官方java驱动包。

pom导入包

<dependency><groupId>org.questdb</groupId><artifactId>questdb</artifactId><version>7.3.7</version>
</dependency>

java测试示例

package com.example.questdb.influxdb;import io.questdb.client.Sender;
import org.junit.jupiter.api.Test;
import java.time.temporal.ChronoUnit;
/*** @Description QuestDB可基于InfluxDB协议通过tcp高性能提交数据* https://questdb.io/docs/reference/clients/java_ilp/* @Version V1.0*/
public class InfluxDBTest {//仅支持数据插入,不支持查询与更新@Testpublic void insert(){try (Sender sender = Sender.builder().address("192.168.1.3:9009").build()) {long times = System.currentTimeMillis();//如果表不存在,则自动创建,并且 InfluxDB Line Protocol协议发送数据是基于PARTITION BY DAY分区sender.table("test_demo").longColumn("id", 103).stringColumn("name", "influxDBTest").stringColumn("value", "abc").at(times, ChronoUnit.MILLIS);sender.close();}}
}

数据表记录

Postgre协议

QuestDB支持Postgres传输协议。因此,QuestDB能够运行大多数Postgres查询。这意味着您可以将您最喜欢的Postgres客户端或驱动程序与QuestDB结合使用,无需额外成本。

注意:Postgres使用的存储模型与QuestDB使用的存储模型根本不同,因此Postgres的某些功能不适用于QuestDB。

pom导入包

<dependency><groupId>org.postgresql</groupId><artifactId>postgresql</artifactId><version>42.6.0</version>
</dependency>

java示例

package com.example.questdb.postgres;import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Test;
import java.sql.*;
import java.util.Properties;
/*** @Description  QuestDB支持Postgres协议运行SQL脚本* https://questdb.io/docs/reference/api/postgres/* @Version V1.0*/
public class PostgresTest {static Properties properties = new Properties();static Connection connection;//初始化连接@BeforeAllpublic static void init() throws Exception {//默认adminproperties.setProperty("user", "admin");//默认questproperties.setProperty("password", "quest");//禁用ssl,不支持ssl协议properties.setProperty("sslmode", "disable");//默认数据库qdbconnection = DriverManager.getConnection("jdbc:postgresql://192.168.1.3:8812/qdb", properties);}//插入数据@Testpublic void insert() throws Exception {connection.setAutoCommit(false);try (PreparedStatement preparedStatement = connection.prepareStatement("INSERT INTO test_demo (id, name, value ,ts) VALUES (?, ?, ?, ?)")) {preparedStatement.setInt(1, 104);preparedStatement.setString(2, "postgresTest");preparedStatement.setString(3, "abc");preparedStatement.setTimestamp(4, new Timestamp(System.currentTimeMillis()));preparedStatement.execute();}connection.commit();connection.close();}//查询数据@Testpublic void select() throws Exception {try (PreparedStatement preparedStatement = connection.prepareStatement("SELECT * FROM test_demo")) {try (ResultSet rs = preparedStatement.executeQuery()) {while (rs.next()) {System.out.println(rs.getObject(1));System.out.println(rs.getObject(2));System.out.println(rs.getObject(3));System.out.println(rs.getObject(4));System.out.println("------------------------------------");}}connection.close();}}//更新数据@Testpublic void update() throws Exception {try (PreparedStatement preparedStatement = connection.prepareStatement("UPDATE test_demo SET name='postgresTest5' WHERE id=104")) {preparedStatement.execute();}connection.close();}
}

数据表记录

性能测试

QuestDB可以基于InfluxDB协议实现大批量数据高吞吐插入;

本示例通模拟过将约3亿条各城市10年的气象数据,通过InfluxDB协议快速插入到QuestDB数据表中,在基于此规模数据的存储表上进行SQL查询与聚合性能测试;

创建数据表

CREATE TABLE IF NOT EXISTS weather_behavior (
city SYMBOL,
site STRING,
air INT,
temperature DOUBLE,
humidity INT,
windLevel INT,
createTime TIMESTAMP
)  TIMESTAMP(createTime) PARTITION BY MONTH WAL;

createTime为时间序列字段,基于时间序列创建月度分区存储数据,并开启WAL预写入,提升多线程下并发请求能力;

InfluxDB写入示例

使用java开发,基于influxDB协议将数据写入questDB表,开发前请在pom.xml中配置questdb-7.3.7包引入;

java示例

package com.example.questdb;import io.questdb.client.Sender;
import org.apache.commons.lang3.RandomUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.time.DateUtils;
import java.math.BigDecimal;
import java.time.Instant;
import java.time.temporal.ChronoUnit;
import java.util.Calendar;
import java.util.Date;
/*** @Description 向questdb发送模拟各省市的天气采集数据* @Version V1.0*/
public class WeatherToQuestdbExample {static String cityNames = "海门,鄂尔多斯,招远,舟山,齐齐哈尔,盐城,赤峰,青岛,乳山,金昌,泉州,莱西,日照,胶南,南通,拉萨," +"云浮,梅州,文登,上海,攀枝花,威海,承德,厦门,汕尾,潮州,丹东,太仓,曲靖,烟台,福州,瓦房店,即墨,抚顺,玉溪,张家口," +"阳泉,莱州,湖州,汕头,昆山,宁波,湛江,揭阳,荣成,连云港,葫芦岛,常熟,东莞,河源,淮安,泰州,南宁,营口,惠州,江阴,蓬莱," +"韶关,嘉峪关,广州,延安,太原,清远,中山,昆明,寿光,盘锦,长治,深圳,珠海,宿迁,咸阳,铜川,平度,佛山,海口,江门,章丘," +"肇庆,大连,临汾,吴江,石嘴山,沈阳,苏州,茂名,嘉兴,长春,胶州,银川,张家港,三门峡,锦州,南昌,柳州,三亚,自贡,吉林," +"阳江,泸州,西宁,宜宾,呼和浩特,成都,大同,镇江,桂林,张家界,宜兴,北海,西安,金坛,东营,牡丹江,遵义,绍兴,扬州,常州," +"潍坊,重庆,台州,南京,滨州,贵阳,无锡,本溪,克拉玛依,渭南,马鞍山,宝鸡,焦作,句容,北京,徐州,衡水,包头,绵阳,乌鲁木齐," +"枣庄,杭州,淄博,鞍山,溧阳,库尔勒,安阳,开封,济南,德阳,温州,九江,邯郸,临安,兰州,沧州,临沂,南充,天津,富阳,泰安," +"诸暨,郑州,哈尔滨,聊城,芜湖,唐山,平顶山,邢台,德州,济宁,荆州,宜昌,义乌,丽水,洛阳,秦皇岛,株洲,石家庄,莱芜,常德," +"保定,湘潭,金华,岳阳,长沙,衢州,廊坊,菏泽,合肥,武汉,大庆";/*** 主程入口* questdb参考:https://questdb.io/docs/reference/clients/java_ilp/* 注意:客户端仅支持 TCP。它不支持UDP作为传输。* @param args*/public static void main(String[] args) throws Exception {//生产者发送消息Sender sender = Sender.builder().address("192.168.110.36:9009").build();String table = "weather_behavior";String [] citys = StringUtils.split(cityNames, ",");long i = 1;System.out.println("开始执行数据导入,time:" + new Date());//从指定时间开始生成10年的假数据,10年约315532800秒,模拟每秒产生1条数据,平均每天产生86400条,一年约3153万条,起始时间:2014-01-01 00:00:00,结束时间:2024-01-01 00:00:00Date dateTime = DateUtils.parseDate("2014-01-01 00:00:00", "yyyy-MM-dd HH:mm:ss");java.util.Calendar calendar = Calendar.getInstance();calendar.setTime(dateTime);while(true){String city  = citys[RandomUtils.nextInt(0, citys.length)] ;String site = String.format("%s-%s",city,RandomUtils.nextInt(0, 100));int air = RandomUtils.nextInt(10, 200);double temperature = RandomUtils.nextDouble(0.0, 40.00);BigDecimal temperatureBig = new BigDecimal(temperature);temperature = temperatureBig.setScale(2, BigDecimal.ROUND_HALF_UP).doubleValue();int humidity = 0;int windLevel = 0;if (temperature<0){humidity = RandomUtils.nextInt(0, 5);windLevel = RandomUtils.nextInt(0, 6);}else if (temperature>0 && temperature<15){humidity = RandomUtils.nextInt(5, 25);windLevel = RandomUtils.nextInt(0, 8);} else {humidity = RandomUtils.nextInt(25, 75);windLevel = RandomUtils.nextInt(0, 10);}//每循环递增1秒calendar.add(Calendar.SECOND, 1);//向questdb发送数据:地市,站点,PM2.5,温度,湿度,风速,采集时间sender.table(table)//symbol索引字段.symbol("city", city)//string UTF-16 编码字符的长度前缀序列,其长度存储为带符号的 32 位整数.stringColumn("site", site)//long 有符号整数,pm2.5 = 0~250.longColumn("air", air)//double 双精度 IEEE 754 浮点值。.doubleColumn("temperature", temperature).longColumn("humidity", humidity).longColumn("windLevel", windLevel)//设置分区数据时间戳取系统纳秒//.at(System.nanoTime(), ChronoUnit.NANOS);//指定业务时间.at(calendar.getTimeInMillis(), ChronoUnit.MILLIS);//每5000行提交一次if (i % 5000 == 0) {sender.flush();}//当达到10年总秒数,则退出if (i >= 315532800) {break;}i ++ ;}sender.close();System.out.println("批量数据导入成功,time:" + new Date());}
}

写入性能测试

IDEA控制台打印日志,单线程循环插入3亿多条记录,

每5000条批量提交一次,累计用时:12分52秒( 772s);平均每秒:315532800记录 / 772s用时 = 约408721记录(s);

略 ... 
2024-01-04T11:50:45.215547Z I i.q.c.l.t.PlainTcpLineChannel Send buffer size change from 65536 to 131072
开始执行数据导入,time:Thu Jan 04 19:50:47 CST 2024
批量数据导入成功,time:Thu Jan 04 20:03:39 CST 2024
略 ... 

从统计数据上来看,通过InfluxDB协议写入QuestDB性能极强,如果客户端是多线程写入,相信性能还会有一定提升;

在写入QuestDB所在服务器(虚拟机)为8核 CPU+16G内存,在大量写入过程中,CUP使用率较高均值在70%左右,内存使用比较少并且无较大波动,Java进程大约在1G上下(QuestDB采用Java和C++开发);

未插入数据前服务器磁盘空间已用为16G;

数据入表后磁盘空间已使用了51G,3亿数据增长了35G,推测questDB数据存储文件未做压缩,因此个人觉得占用磁盘空间偏大;

SQL查询与聚合

统计全表数量,查询已入库记录315532800条(3亿)多条

select count(1) from weather_behavior

按月建立分区后,查询分区信息,总共121个分区

SHOW PARTITIONS FROM weather_behavior;

查询pm大于150的城市上报次数

select city,count(1) from weather_behavior where air>=150 group by city limit 100;

查询2022年全年pm大于150的城市上报次数(前100条)

select city,count(1) as upNum from weather_behavior 
where air>=150 and 
createTime >= to_timestamp('2022-12-31T16:00:00', 'yyyy-MM-ddTHH:mm:ss') and createTime <= to_timestamp('2023-12-31T15:59:59', 'yyyy-MM-ddTHH:mm:ss')
group by city order by upNum desc limit 100;

查询2022年全年上报最多top100的城市(前100条)

select city,count(1) as upNum from weather_behavior 
where createTime >= to_timestamp('2022-12-31T16:00:00', 'yyyy-MM-ddTHH:mm:ss') and createTime <= to_timestamp('2023-12-31T15:59:59', 'yyyy-MM-ddTHH:mm:ss')
group by city order by upNum desc limit 100;

注:因该命中了questDB数据缓存分区,所以重复执行对2022年数据做聚合统计时,速度极快;

查询2022年全年pm最大值top100的城市(前100条)

select city,max(air) as aitNum from weather_behavior 
where createTime >= to_timestamp('2022-12-31T16:00:00', 'yyyy-MM-ddTHH:mm:ss') and createTime <= to_timestamp('2023-12-31T15:59:59', 'yyyy-MM-ddTHH:mm:ss')
group by city order by aitNum desc limit 10;

按2022年全年pm排名查询topN条记录,条件:每城市只取站点排名第一(前100条)

select * from (
select city, site,maxnum,row_number() OVER (partition by city order by maxnum desc) as ranking from (
select city, site, max(air) as maxnum from weather_behavior 
where createTime >= to_timestamp('2022-12-31T16:00:00', 'yyyy-MM-ddTHH:mm:ss') and createTime <= to_timestamp('2023-12-31T15:59:59', 'yyyy-MM-ddTHH:mm:ss')
group by city,site 
) tt ) ttt where ranking<2 order by city limit 100

按2022年全年计算指定城市每个月的PM2.5的最大值,最小值,平均值,总值,总记录数,月度排名,以及每个月递增的平均总记录数、月递增的平均值

select month,city,maxAir,minAir,avgAir,sumAir,airCount, 
row_number() OVER (partition by city order by avgAir desc) as ranking,
avg(airCount) over (PARTITION BY city ORDER BY month) moving_air_count,
avg(avgAir) over (PARTITION BY city ORDER BY month) moving_avg_air
from (select 
city,max(air) as maxAir, min(air) as minAir,avg(air) as avgAir, sum(air) as sumAir, count(1) as airCount, date_trunc('month',  dateadd('h', 8, createTime)) month 
from weather_behavior where city='济南' and 
createTime >= to_timestamp('2022-12-31T16:00:00', 'yyyy-MM-ddTHH:mm:ss') and createTime <= to_timestamp('2023-12-31T15:59:59', 'yyyy-MM-ddTHH:mm:ss')
group by city,month
) tt order by month desc

查询城市济南10年中每年的PM2.5的最大值,最小值,平均值,总值,总记录数(查全表3亿多条)

select 
city,max(air) as maxAir, min(air) as minAir,avg(air) as avgAir, sum(air) as sumAir, count(1) as airCount, date_trunc('year', dateadd('h', 8, createTime)) as year 
from weather_behavior where city='济南' 
group by city,year

总结

     QuestDB开箱即用,没有什么安装配置难度,官方文档编写详细易懂(纯英文,用插件翻译即可),学习与使用曲线低,很容易上手;QuestDB在性能方面有极强的优势,吞吐量大,数据写入速度快,基于InfuluxDB线路协议写入性能最佳,默认设置下可以达到每秒约40W条(本示例硬件条件下,不同环境可能会存在差异),PostgreSQL协议则最贴合项目开发过程中使用SQL语法,基于http请求则无需额外驱动包集成从而简化使用成本,因此可以根据业务需要与性能选择不同的技术方案;在基于时间维度建立分区后,存储的大量数据按时间序例,拆分存储在不同分区内,支持按小时、天、周、月、年建立分区,数据写入过程中按分区时间字段自动存储在时间维度分区中;在大规模数据下基于时间序例条件查询数据,查询速度快,如:在单表约3亿数据存储(121个月分区)规模上,按某一年度范围查询基本上在1秒以内响应,全表搜索并进行复杂聚合统计约在10秒内响应;因此在大规模存储数据下,相对于传统事务型数据库来说,有着跨越式的性能差距;可应用在历史数据存储、高基数时间序列等场景,如:监控指标数据、日志数据、历史订单数据、用户行为明细数据、物联网数据、游戏上报数据、业务备份数据等,适用于写入到表里后不在频繁变更以及基于时间序例追溯的数据;

       QuestDB在使用过程中,也存一些限制与不足,如:权限管理、支持对象存储、高可用集群等部份功能需要企业授权版才能支持,社区开源免费版不提供以上功能,因此使用者需要根据实际情况选择QuestDB版本;根据个人的体验,单实例运行性能是最好的,无需考虑集群过程中的数据交付与节点通讯等性能开销与维护事项,在中小企业中,如果不涉及到大量高并发查询使用(每次查询会使用较多硬件资源,高并发下会超成资源竞争,降低查询性能),在足够大的磁盘空间与合理的内存、CPU、分区等软硬件条件下,社区开源版已足够支持TB级的数据交付使用;QuestDB在对SQL的兼容性上已经能覆盖大部份标准查询,但可能还存在部份语法不能满足(使用过程中有遇到,未做详细记录),基本SQL开发是足够使用的;QuestDB不支持表行级数据delete删除操作,QuestDB则采取删除分区的方式来删除数据,不适用数据细颗粒度管理,官方建议数据一旦入库尽量不在做更新;

     更多使用体验与学习,请参考官方网站资源;

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/412952.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

智能光栅光片显微成像技术的LabVIEW解决方案

智能光栅光片显微成像技术的LabVIEW解决方案 在生物医学研究中&#xff0c;高效的成像技术对于捕捉细胞内罕见和复杂事件至关重要。智能光栅光片显微技术&#xff08;smartLLSM&#xff09;的出现&#xff0c;代表了LabVIEW软件在高端成像领域的革命性应用&#xff0c;这项技术…

算法学习系列(二十四):二分图

目录 引言一、二分图二、染色法三、匈牙利算法 引言 这个二分图作为平常我是不怎么知道的&#xff0c;但是在算法竞赛中还是能用得到的。本文主要介绍了染色法&#xff1a;用来判断如否为二分图&#xff0c;匈牙利算法&#xff1a;求出二分图最大匹配数。 一、二分图 二分图…

Linux例行性工作 at和crontab命令

1&#xff0c;例行性工作 例行性工作 —— 在某一时刻&#xff0c;必须要做的事情 —— 定时任务 &#xff08;比如&#xff1a;闹钟&#xff09; 例行性工作分为两种&#xff1a;“单一的例行性工作 at”和“循环的例行性工作 crontab” 2&#xff0c;单一执行的例行性工作 …

HTML--JavaScript--引入方式

啊哈~~~基础三剑看到第三剑&#xff0c;JavaScript HTML用于控制网页结构 CSS用于控制网页的外观 JavaScript用于控制网页的行为 JavaScript引入方式 引入的三种方式&#xff1a; 外部JavaScript 内部JavaScript 元素事件JavaScript 引入外部JavaScript 一般情况下网页最好…

真假转换之间 tr

文章目录 真假转换之间 tra-z小写全部转换为大写A-Z大写全部转换为小写貌似起名可以用这个移除文件中的所有空格更多信息 真假转换之间 tr Linux tr 命令用于转换或删除字符。 tr 命令可以从标准输入读取数据&#xff0c;经过字符串转译后&#xff0c;将结果输出到标准输出。…

AGV小车磁导航传感器CNS-MGS-080N磁条布置方法

AGV小车磁导航传感器CNS-MGS-080N通过检测磁场信号强度&#xff0c;来判断此磁条相对于传感器位置。CNS-MGS-080N传感器使用8个点检测信号&#xff0c;模拟16点传感器信号进行输出。 本文重点介绍AGV小车磁导航传感器CNS-MGS-080N磁条布置方法。 1、 磁条规格 磁条型号 CNS-M…

京东数据分析-2023年度大家电行业数据分析(含空调、冰箱、洗衣机、平板电视品类)

2023年度&#xff0c;京东平台上大家电市场的需求疲软&#xff0c;行业整体销售呈下滑趋势。 根据鲸参谋电商数据分析平台的相关数据显示&#xff0c;2023年大家电行业大盘的销量累计约为6100万&#xff0c;同比下滑约15%&#xff1b;年度销额累计1400亿&#xff0c;同比下滑约…

【ELK 学习】ElasticSearch

ELK&#xff1a;ElasticSearch存储&#xff0c;Logstash收集&#xff0c;Kibana展示 版本较多&#xff0c;使用时需要版本匹配&#xff0c;还需要和mysql版本匹配&#xff08;elastic官网给了版本对应关系&#xff09; 本次使用的版本es6.8.12 filebeat 轻量级的数据收集工具 …

设计模式——1_5 享元(Flyweight)

今人不见古时月&#xff0c;今月曾经照古人 ——李白 文章目录 定义图纸一个例子&#xff1a;可以复用的样式表绘制表格降本增效&#xff1f;第一步&#xff0c;先分析 变化和不变的地方第二步&#xff0c;把变化和不变的地方拆开来第三步&#xff1a;有没有办法共享这些内容完…

Android Studio下载及安装和Gradle的配置(非常详细)从零基础入门到精通,看完这一篇就够了

文章目录 下载安装修改Sdk的位置创建项目修改Gradle的位置查看AS版本工具栏–View项工具栏–Build下的功能说明Build Variants视图说明下载模拟器&#xff08;avd&#xff09;/安卓虚拟设备 屏幕熄灭功能关闭虚拟设备功能删除自己开发的应用软件将开发的应用运行到虚拟设备上。…

高精度AGV小车N/S极磁条导航传感器CNS-MGS-080N参数配置操作方法

高精度AGV小车N/S极磁条导航传感器CNS-MGS-080N主要运用于自主导航机器人、室内室外巡检机器人、自主导航运输车AGV(AGC)、自动手推车等自主导航设备&#xff0c;完成自主导航设备的预设运行路线检测及定位。基于预设磁轨迹的导航方式是自主移动平台如AGV、巡检机器人、无轨货架…

添加边界值分析测试用例

1.1创建项目成功后会自动生成封装好的函数&#xff0c;在这些封装好的函数上点击右键&#xff0c;添加边界值分析测试用例&#xff0c;如下图所示。 1.2生成的用例模版是不可以直接运行的&#xff0c;需要我们分别点击它们&#xff0c;让它们自动生成相应测试用例。如下图所示&…