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.mzph.cn/news/642442.shtml

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

相关文章

别再局限于Android和iOS了尝试鸿蒙APP系统开发吧!

最近&#xff0c;多家互联网公司也发布了鸿蒙OS的App开发工程师的岗位&#xff0c;开启了抢人大战&#xff0c;有的企业开出了近百万的年薪招聘鸿蒙OS工程师&#xff0c;而华为甚至为鸿蒙OS资深架构师开出了100万元—160万元的年薪。 「纯血」鸿蒙开启&#xff0c;欲与 Andori…

WEBDYNPRO FPM 框架

框架搭建 1、FPM_OVP_COMPONENT 1 METHOD change_toolbar_btn .2 * enabled "ABAP_TRUE可用 ABAP_FALSE不可用3 * visibility "01不可见 02可见4 DATA: ls_btn TYPE if_fpm_ovp>ty_s_toolbar_button.5 CHECK wd_this->mo_cnr IS BOUND.6 7 TRY .8 …

2011-2022年北大数字普惠金融指数“第五期”(包括省市县)

2011-2022年北大数字普惠金融指数“第五期”&#xff08;包括省市县&#xff09; 1、时间&#xff1a;2011-2022年 其中县级的时间为2014-2022年 2、来源&#xff1a;北大数字普惠金融指数 3、范围&#xff1a;全国31省&#xff0c;337个地级市以及2800个县 4、指标&#x…

API调试?试试Apipost

你是否经常遇到接口开发过程中的各种问题&#xff1f;或许你曾为接口测试与调试的繁琐流程而烦恼。不要担心&#xff01;今天我将向大家介绍一款功能强大、易于上手的接口测试工具——Apipost&#xff0c;并带你深入了解如何玩转它&#xff0c;轻松实现接口测试与调试。 什么是…

linux更新内核

内核介绍 官网链接:https://kernel.org 内核下载库: https://mirrors.edge.kernel.org/pub/linux/kernel/ 更新软件源 rootcary:~# apt-get update rootcary:~# sudo apt-get install libncurses5-dev build-essential kernel-package flex bison libelf-dev libssl-dev 下…

机器学习实验报告——Bayes算法

目录 一、算法介绍 1.1算法背景 1.2算法假设 1.3 贝叶斯与朴素贝叶斯 1.4算法原理 二、算法推导 2.1朴素贝叶斯介绍 2.2朴素贝叶斯算法推导 2.2.1先验后验概率 2.2.2条件概率公式 2.3 独立性假设 2.4 朴素贝叶斯推导 三、算法实现 3.1数据集描述 3.2代码实现 四…

SpringBoot:Bean生命周期自定义初始化和销毁

&#x1f3e1;浩泽学编程&#xff1a;个人主页 &#x1f525; 推荐专栏&#xff1a;《深入浅出SpringBoot》《java项目分享》 《RabbitMQ》《Spring》《SpringMVC》 &#x1f6f8;学无止境&#xff0c;不骄不躁&#xff0c;知行合一 文章目录 前言一、Bean注解指…

如何做好一个信息系统项目经理,一个项目经理的个人体会和经验总结(三)

前言 今天我们继续聊聊在 项目开发阶段&#xff0c;项目经理需要做好的事情 &#x1f603; 二、项目开发阶段&#xff08;续&#xff09; 4. 控制好项目开发质量 要控制好项目开发质量&#xff0c;主要是依赖测试&#xff0c;好的产品都是靠不断地测试&#xff0c;不断地试…

《WebKit 技术内幕》学习之四(3): 资源加载和网络栈

3. 网络栈 3.1 WebKit的网络设施 WebKit的资源加载其实是交由各个移植来实现的&#xff0c;所以WebCore其实并没有什么特别的基础设施&#xff0c;每个移植的网络实现是非常不一样的。 从WebKit的代码结构中可以看出&#xff0c;网络部分代码的确比较少的&#xff0c;它们都在…

西方企业在与中国的竞争中,无可避免地“效仿中国”

长期以来&#xff0c;在西方观察家的视野里&#xff0c;中国科技领域的成功突破主要归结于三大支柱&#xff1a;一是中国建立了完备的基础设施网络&#xff1b;二是大量创新型企业如雨后春笋般涌现&#xff0c;以惊人的速度追赶乃至超越美国硅谷的企业&#xff1b;三是这些创新…

wps word 文档里的空白空间太大了

wps word 文档里的空白空间太大了&#xff0c;如下图1 点击【页面】--->【页边距】&#xff0c;把左边、右边的页边距调为0厘米。如下图2 点击【视图】--->【显示比例】从75%改为页宽&#xff0c;页宽的意思是使页面的宽度与窗口的宽度一致。如下图3 图1

浪花 - 用户加入队伍

一、接口设计 1. 请求参数&#xff1a;TeamJoinRequest package com.example.usercenter.model.request;import lombok.Data; import java.io.Serializable;/*** 加入队伍请求参数封装类*/ Data public class TeamJoinRequest implements Serializable {private static final…

用Axure RP 9制作弹出框

制作流程 1.准备文本框 下拉列表 按钮 动态面板 如图 2.先把下拉列表放好 再放动态面板覆盖 3.点动态面板 进入界面 如图 4.给按钮添加交互 3个按钮一样的 如图 5.提交按钮添加交互 如图

linux安装python3.11

yum -y gcc install zlib-devel bzip2-devel openssl-devel ncurses-devel sqlite-devel readline-devel tk-devel gdbm-devel db4-devel libpcap-devel xz-devel libffi-devel下载地址 https://www.python.org/ftp/python/3.11.7/Python-3.11.7.tar.xz 上传python文件&#x…

Kafka(二)【文件存储机制 生产者】

目录 一、Kafka 文件存储机制 二、Kafka 生产者 1、生产者消息发送流程 1.1、发送原理 2、异步发送 API 2.1、普通异步发送 案例演示 2.2、带回调函数的异步发送 2.3、同步发送 API 3、生产者分区 3.1、分区的好处 3.2、生产者发送消息的分区策略 &#xff08;1&am…

基于springboot+vue的学科竞赛管理系统(前后端分离)

博主主页&#xff1a;猫头鹰源码 博主简介&#xff1a;Java领域优质创作者、CSDN博客专家、公司架构师、全网粉丝5万、专注Java技术领域和毕业设计项目实战 主要内容&#xff1a;毕业设计(Javaweb项目|小程序等)、简历模板、学习资料、面试题库、技术咨询 文末联系获取 研究背景…

12.常用统计分析方法——聚类分析

目录 基础知识 实操 层次聚类 划分聚类 方法一&#xff1a;K均值聚类&#xff08;最常见&#xff09; 方法二&#xff1a;基于中心点的划分&#xff08;PAM&#xff09; 避免不存在的类 基础知识 概念&#xff1a; 聚类分析是一种数据归约技术&#xff0c;旨在揭露一个…

Python基础之数据库操作

一、安装第三方库PyMySQL 1、在PyCharm中通过 【File】-【setting】-【Python Interpreter】搜索 PyMySQL进行安装 2、通过PyCharm中的 Terminal 命令行 输入: pip install PyMySQL 注&#xff1a;通过pip安装&#xff0c;可能会提示需要更新pip&#xff0c;这时可执行&#…

discuz论坛附件上传限制大小2MB

我遇到了这个问题&#xff0c;去修改了配置PHP.ini文件没有解决. 我把他变成2000M依旧没有用&#xff0c;然后我选择了用户组&#xff0c;附件部分。如图所示&#xff1a; 然后这个时候我还是没有好&#xff0c;我同事的却不限制大小了&#xff0c;我去清理缓存&#xff…

中文电码在历史关键时刻的作用

1. 中文电码&#xff1a;一段被遗忘的历史 中文电码是一种将汉字转换为电信号编码的方式&#xff0c;它的历史可以追溯到19世纪末。在当时&#xff0c;电报技术传入中国&#xff0c;为了实现汉字的电子传输&#xff0c;我国学者研究了一种将汉字转换为电码的方法。这种方法通过…