QuestDB时序数据库快速入门

news2024/12/23 14:51:18

简介

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 {
    //查询
    @Test
    public 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);
    }
    //插入
    @Test
    public 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);
    }
    //更改
    @Test
    public 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 {
    //仅支持数据插入,不支持查询与更新
    @Test
    public 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;
    //初始化连接
    @BeforeAll
    public static void init() throws Exception {
        //默认admin
        properties.setProperty("user", "admin");
        //默认quest
        properties.setProperty("password", "quest");
        //禁用ssl,不支持ssl协议
        properties.setProperty("sslmode", "disable");
        //默认数据库qdb
        connection = DriverManager.getConnection("jdbc:postgresql://192.168.1.3:8812/qdb", properties);
    }
    //插入数据
    @Test
    public 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();
    }
    //查询数据
    @Test
    public 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();
        }
    }
    //更新数据
    @Test
    public 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:00
        Date 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.coloradmin.cn/o/1394353.html

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

相关文章

【音视频】基于NGINX如何播放rtmp视频流

背景 现阶段直播越来越流行&#xff0c;直播技术发展也越来越快。Webrtc、rtmp、rtsp是比较火热的技术&#xff0c;而且应用也比较广泛。本文通过实践来展开介绍关于rtmp如何播放。 概要 本文重点介绍基于NGINX如何播放rtmp视频流 正文 1、构造rtsp视频流 可以参考上一篇…

BigeMap在Unity3d中的应用,助力数字孪生

1. 首先需要用到3个软件&#xff0c;unity&#xff0c;gis office 和 bigemap离线服务器 Unity下载地址:点击前往下载页面(Unity需要 Unity 2021.3.2f1之后的版本) Gis office下载地址:点击前往下载页面 Bigemap离线服务器 下载地址: 点击前往下载页面 Unity用于数字孪生项…

F-Droid:开源Android应用的宝库

F-Droid&#xff1a;开源Android应用的宝库 引言 F-Droid是一个开源应用程序存储库&#xff0c;旨在为安卓用户提供自由、隐私和安全的应用程序。它最初于2010年由Ciaran Gultnieks创建&#xff0c;因为他认为Google Play Store上的应用程序不够透明和安全。F-Droid的目标是为…

FTP文件传输与vsftpd配置

一 存储类型 直连式存储DAS 适用于那些数据量不大&#xff0c;对磁盘访问速度要求较高的中小企业 存储区域网络SAN 用来存储非结构化数据&#xff0c;虽然受限于以太网的速度&#xff0c;但是部署灵活&#xff0c;成本低 网络附加存储NAS 适用于大型应用或数据库系统&…

Backtrader 文档学习-Indicators混合时间框架

Backtrader 文档学习-Indicators混合时间周期 1.不同时间周期 如果数据源在Cerebro引擎中具有不同的时间范围和不同的长度&#xff0c;指示器将会终止。 比如&#xff1a;data0是日线&#xff0c;data1是月线 。 pivotpoint btind.PivotPoint(self.data1) sellsignal self…

FairGuard游戏安全2023年度报告

导 读&#xff1a;2023年&#xff0c;游戏行业摆脱了疫情带来诸多负面影响&#xff0c;国内游戏市场收入与用户规模双双实现突破&#xff0c;迎来了历史新高点。但游戏黑灰产规模也在迅速扩大&#xff0c;不少游戏饱受其侵扰&#xff0c;游戏厂商愈发重视游戏安全问题。 为帮助…

WinForm中使用MaskedTextBox制作IP地址输入框

1.实现的功能:输入IP地址&#xff0c;形如000.000.000.000的格式&#xff0c;并设置keydown事件&#xff0c;当输入点(.)的时候&#xff0c;自动跳至下一栏。 具体方法: (1)…从工具箱中拖入一个MaskedTextBox&#xff0c;命名为MaskedTextBox_IP。 2.在Mask属性中&#xff0c…

【计算机组成与体系结构Ⅱ】多Cache一致性的模拟分析(实验)

实验八&#xff1a;多Cache一致性的模拟分析 一、实验目的 1&#xff1a;加深对多 Cache 一致性的理解。 2&#xff1a;进一步掌握解决多 Cache一致性的目录协议和监听协议的基本思想。 3&#xff1a;掌握在各种情况下&#xff0c; 目录协议和监听协议是如何工作的&#xf…

内网穿透的应用-使用Docker搭建一个Wiki.Js知识库系统并实现分享他人远程创作

文章目录 1. 安装Docker2. 获取Wiki.js镜像3. 本地服务器打开Wiki.js并添加知识库内容4. 实现公网访问Wiki.js5. 固定Wiki.js公网地址 不管是在企业中还是在自己的个人知识整理上&#xff0c;我们都需要通过某种方式来有条理的组织相应的知识架构&#xff0c;那么一个好的知识整…

vue3的创建及认识

1、创建项目 使用creat-vue搭建vue3项目 2、认识creat-vue create-vue是Vue官方新的脚手架工具&#xff0c;底层切换到了 vite &#xff08;下一代前端工具链&#xff09;&#xff0c;为开发提供极速响应 3、创建create-vue项目 npm init vuelatest 4、认识vue3 首先熟悉一下v…

【Axure高保真原型】文字翻页效果

今天和大家分享选择文字翻页效果的原型模板&#xff0c;我们通过这个模板实现类似翻书的效果。鼠标点击右箭头&#xff0c;可以翻开下一页&#xff0c;点击左箭头翻开上一页&#xff1b;当然我们也可以通过鼠标拖动的操作进行翻页&#xff0c;鼠标想左拖动时&#xff0c;翻开下…

3d音响按键怎么建立模型---模大狮模型网

要建立3D音响按键的模型&#xff0c;您可以按照以下步骤进行&#xff1a; 选择建模软件&#xff1a;首先&#xff0c;选择一个三维建模软件&#xff0c;如Blender、3ds Max或Maya。这些软件都提供了丰富的建模工具和功能&#xff0c;适合用于创建复杂的三维模型。 参考图像&am…

基于 Hologres+Flink 的曹操出行实时数仓建设

本文整理自曹操出行实时计算负责人林震基于 HologresFlink 的曹操出行实时数仓建设的分享&#xff0c;内容主要分为以下六部分&#xff1a; 曹操出行业务背景介绍曹操出行业务痛点分析HologresFlink 构建企业级实时数仓曹操出行实时数仓实践曹操出行业务成果分析未来展望 一、曹…

C语言中的字符串操作函数自定义实现:标准版与限定长度版

目录 1. 标准字符串操作函数自定义实现 (a) 自定义strcpy函数 (b) 自定义strcat函数 (c) 自定义strcmp函数 2. 限定长度字符串操作函数自定义实现 (a) 自定义strncpy函数 (b) 自定义strncat函数 (c) 自定义strncmp函数 对字符串的操作是不可或缺的一部分。标准库提供了…

论文阅读 Vision Transformer - VIT

文章目录 1 摘要1.1 核心 2 模型架构2.1 概览2.2 对应CV的特定修改和相关理解 3 代码4 总结 1 摘要 1.1 核心 通过将图像切成patch线形层编码成token特征编码的方法&#xff0c;用transformer的encoder来做图像分类 2 模型架构 2.1 概览 2.2 对应CV的特定修改和相关理解 解…

Dobbo --- HelloWorld项目搭建

Dobbo-HelloWorld 1. demo -- spring方式集成1.1 实现步骤 2. demo -- springboot方式集成2.1 实现provider2.2 实现consumer2.3 项目测试 1. demo – spring方式集成 dubbo官方文档 提供一个可被调用的接口 提供方&#xff1a;实现接口的方法逻辑&#xff0c;启动应用程序&a…

【JavaEE】文件操作: File 类的用法和 InputStream, OutputStream 的用法

目录 1. File 概述 1.1 File的属性 1.2 File的构造方法 1.3 File的方法 2.读文件 2.1 InputStream 概述 2.2 FileInputStream 概述 2.3 正确打开和关闭文件的方式 2.4 不同方式读取文件代码示例 2.4 另一种方法:利用 Scanner 进行字符读取 3.写文件 3.1 OutputStre…

【架构】docker实现3主3从架构配置【案例1/4】

一&#xff0c;集群规划及准备工作 架构实现&#xff1a;Redis3主3从 二&#xff0c;搭建命令 第一步&#xff0c;创建6台服务&#xff1a; docker run -d --name redis-node-1 --net host --privilegedtrue -v /data/redis/share/redis-node-1:/data redis:6.0.8 --clust…

redis原理(四)redis命令

目录 一、字符串命令&#xff1a; 二、列表命令&#xff1a; 三、集合命令&#xff1a; 四、散列命令&#xff1a; 五、有序集合命令&#xff1a; 六、redis发布与订阅命令&#xff1a; 七、事务命令 八、其他命令 1、排序&#xff1a;SORT 2、键的过期时间&#xff…

SSH隧道技术

SSH隧道 简介 SSH隧道是一种通过SSH协议在两个网络节点之间建立安全通信的技术。它可以用于多种用途&#xff0c;包括加密和保护敏感数据传输、绕过防火墙限制、远程访问内部服务等。 应用&#xff1a; 端口转发&#xff1a;SSH隧道可以将本地端口转发到远程主机上&#xf…