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.rhkb.cn/news/244392.html

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

相关文章

uniapp中打包Andiord app,在真机调试时地图以及定位功能可以正常使用,打包成app后失效问题(高德地图)

踩坑uniapp中打包Andiord app&#xff0c;在真机调试时地图以及定位功能可以正常使用&#xff0c;打包成app后失效问题_uniapp真机调试高德地图正常 打包apk高德地图就不加载-CSDN博客 问题&#xff1a; 目前两个项目&#xff0c;一个项目是从另一个项目里面分割出来的一整套…

10个常考的前端手写题,你全都会吗?(下)

前言 &#x1f4eb; 大家好&#xff0c;我是南木元元&#xff0c;热爱技术和分享&#xff0c;欢迎大家交流&#xff0c;一起学习进步&#xff01; &#x1f345; 个人主页&#xff1a;南木元元 今天接着上篇再来分享一下10个常见的JavaScript手写功能。 目录 1.实现继承 ES5继…

【快刊录用】15天录用!含中科院1区TOP-4区,投必中!

2024年1月13日-20241月19日 进展喜讯 经核实&#xff0c;由我处Unionpub学术推荐的论文中&#xff0c;新增2篇论文录用、2篇上线见刊、5篇数据库检索&#xff1a; 录用通知 FA20977 FA20479 — 见刊通知 FA20245 FA20885 — 检索通知 FA20924 FA20799 FA20790 FA2…

【IEEE会议征稿】2024年第九届智能计算与信号处理国际学术会议(ICSP 2024)

2024年第九届智能计算与信号处理国际学术会议&#xff08;ICSP 2024&#xff09; 2024年第八届智能计算与信号处理国际学术会议&#xff08;ICSP 2024&#xff09;将在西安举行&#xff0c; 会期是2024年4月19-21日&#xff0c; 为期三天, 会议由西安科技大学主办。 欢迎参会&…

4G物联网LED智慧路灯杆显示屏产品介绍

4GLED显示屏是一种具有4G网络连接功能的LED显示屏。它可以通过4G网络连接到互联网&#xff0c;实现远程管理和控制&#xff0c;方便进行内容更新和管理。同时&#xff0c;4GLED显示屏具有高亮度、高清晰度和高对比度的特点&#xff0c;可以提供清晰明亮的图像和视频展示效果。它…

【蓝桥杯EDA设计与开发】立创开源社区分享的关于蓝桥被EDA真题与仿真题的项目分析

立创开源社区内有几个项目分享了往年 EDA 设计题目与仿真题&#xff0c;对此展开了学习。 【本人非科班出身&#xff0c;以下对项目的学习仅在我的眼界范围内发表意见&#xff0c;如有错误&#xff0c;请指正。】 项目一 来源&#xff1a;第十四届蓝桥杯EDA赛模拟题一 - 嘉立…

C++笔记(二)

函数的默认参数 如果我们自己传入数据&#xff0c;就用自己的数据&#xff0c;如果没有&#xff0c;就用默认值 语法&#xff1a; 返回值类型 函数名&#xff08;形参默认值&#xff09;{} int func&#xff08;int a&#xff0c;int b20&#xff0c;int c30&#xff09;{} …

软考复习之多媒体篇

常用的计算公式 数据传输率&#xff08;单位:b/s&#xff09; 未压缩的数据传输率 采样频率&#xff08;Hz&#xff09;* 量化位数&#xff08;位&#xff09;* 声道数 波形声音经过数字化后的信息数据量&#xff08;单位:字节&#xff09; 声音信号数据量 数据传输率 * …

使用Python自动化操作手机,自动执行常见任务,例如滑动手势、呼叫、发送短信等等

使用Python自动化操作手机,自动执行常见任务,例如滑动手势、呼叫、发送短信等等。 此自动化脚本将帮助你使用 Python 中的 Android 调试桥 (ADB) 自动化你的智能手机。下面我将展示如何自动执行常见任务,例如滑动手势、呼叫、发送短信等等。 您可以了解有关 ADB 的更多信息,…

Qt拖拽事件简单实现

1.相关说明 重写resizeEvent(这个按需重写)、dragEnterEvent(拖拽事件函数)、dropEvent(放下事件函数)&#xff0c;可以将本地图片拖拽到label标签中 2.相关界面 3.相关代码 #include "widget.h" #include "ui_widget.h" #include <QDragEnterEvent>…

文本文件的编码详解

今天在程序调试时&#xff0c;程序中用到一个config.json文件&#xff0c;是UTF-8的格式&#xff0c;这是在win11的nodtepad中显示的编码格式&#xff0c;但复制到win7中使用时&#xff0c;出现一个奇特的现象&#xff0c;报文件格式错误&#xff0c;说出现一个特殊字符不识别&…

ArkUI新能力,助力应用开发更便捷

ArkUI是一套构建分布式应用的声明式UI开发框架。它具备简洁自然的UI信息语法、丰富的UI组件、多维的状态管理&#xff0c;以及实时界面预览等相关能力&#xff0c;帮助您提升应用开发效率&#xff0c;并能在多种设备上实现生动而流畅的用户体验。随着HarmonyOS 3.1版本的发布&a…

低代码(Low-Code)技术简化开发难度,快速搭建应用

目录 一、低代码技术定义 二、低代码技术优势 1.提高企业的工作效率 2.降低企业的开发成本 3.提高应用程序和业务流程的质量 三、稳定性和生产率的最佳实践 三、最后 随着数字化时代的到来&#xff0c;低代码&#xff08;Low-Code&#xff09;技术已经成为了企业数字化转…

汽车网络架构与常用总线汇总

汽车CAN总线简述 CAN 是控制器局域网Controller Area Network 的缩写&#xff0c;1986年&#xff0c;由德国Bosch公司为汽车开发的网络技术&#xff0c;主要用于汽车的监测与控制&#xff0c;目的为适应汽车“减少线束的数量”“通过多个网络进行大量数据的高速传输”的需求。…

TypeScript教程(一)在vscode中的配置TypeScript环境

TypeScript教程&#xff08;一&#xff09;在vscode中的配置TypeScript环境 文章目录 TypeScript教程&#xff08;一&#xff09;在vscode中的配置TypeScript环境一、前言二、具体步骤1、Node.js安装2、TypeScript安装3、helloworld 一、前言 未来的开发者们请上座&#xff0c…

kubeadm 安装k8s集群后,master节点notready问题解决方案

使用kubeadm 安装k8s集群后&#xff0c;加载calico cni 网络组件后&#xff0c;master节点notready问题 表现为&#xff1a; 使用命令查看日志&#xff1a;journalctl -f -u kubelet 报错如下&#xff1a; Failed to start ContainerManager failed to initialize top level…

[Linux]HTTP状态响应码和示例

1xx&#xff1a;信息响应类&#xff0c;表示接收到请求并且继续处理 2xx&#xff1a;处理成功响应类&#xff0c;表示动作被成功接收、理解和接受 3xx&#xff1a;重定向响应类&#xff0c;为了完成指定的动作&#xff0c;必须接受进一步处理 4xx&#xff1a;客户端错误&#x…

webpack如何把dist.js中某个模块js打包成一个全局变量,使得在html引入dist.js后可以直接访问

webpack可以通过使用expose-loader来将模块中的一个js文件暴露为全局可以访问的变量。下面是一个示例代码&#xff1a; 1、安装expose-loader npm install expose-loader --save-dev 2、webpack.config.js配置文件 值得注意的是&#xff1a;我在本地使用16.14.2版本的node打包…

QT下载、安装详细教程[Qt5.15及Qt6在线安装,附带下载链接]

QT5.15及QT6的下载和安装 1.下载1.1官网下载1.2国内镜像网站下载 2.安装3.软件启动及测试程序运行3.1Qt Creator&#xff08;Community&#xff09; 1.下载 QT自Qt5.15版本后不在支持离线安装包下载(非商业版本&#xff0c;开源)&#xff0c;故Qt5.15及Qt6需要使用在线安装程序…

Python tkinter (2) —— Button标签

Python的标准Tk GUI工具包的接口 目录 Button 简单示例 设置按钮宽高 设置响应事件 匿名函数调用事件 退出按钮 总结 tkinter系列文章 python tkinter窗口简单实现 Python tkinter (1) —— Label标签 Button Button标签在窗口中显示按钮的控件。 简单示例 实现一个…