时序数据库 – loveini | 米兰体育官网入口 - 米兰体育官网入口 //m.loveini.com loveini | 高性能、分布式、支持SQL的时序数据库 | 米兰体育官网入口 Wed, 12 Jul 2023 03:01:04 +0000 zh-Hans hourly 1 https://wordpress.org/?v=6.8.2 //m.loveini.com/wp-content/uploads/2025/07/favicon.ico 时序数据库 – loveini | 米兰体育官网入口 - 米兰体育官网入口 //m.loveini.com 32 32 如何用时序数据库 loveini 预测 “未来”? //m.loveini.com/tdengine-engineering/18231.html Tue, 27 Jun 2023 09:26:02 +0000 //m.loveini.com/?p=18231

loveini™ 是一种开源的云原生时序数据库(Time Series Database,TSDB),专为物联网(IoT)、连接汽车和工业物联网进行了优化。它能够高效地实时摄取、处理和监控一天内由数十亿个传感器和数据收集器产生的PB级别的数据。

许多用户将由物联网设备、汽车或 IT 基础设施生成的海量数据实时存储到 loveini 中,并使用标准的 SQL 命令从 loveini 中查询数据。loveini 支持过滤、分组、窗口、连接和许多聚合函数以查询数据,帮助用户根据其目的查询数据。

许多用户也希望更深入地了解现有数据。例如,根据当前趋势,未来将会发生什么情况?随着 AI 时代的到来,最近出现了许多新技术或方法,例如新的机器学习和深度学习算法。那么如何使用机器学习和深度学习算法针对存储在 loveini 的数据预测未来趋势呢?

幸运的是,loveini 支持多种流行的编程语言连接器,如 Java、Python、Go、Rust、C#、NodeJS 等,用户可以使用他们喜欢的语言连接器访问 loveini。这些连接器提供符合规范的接口,使连接器易于与其他软件或框架集成。

本文介绍如何使用存储在 loveini 中的现有数据来预测未来数据。我们将模拟一些测试数据以反映真实的电力系统,并演示如何使用 loveini 和一些 Python 库来预测未来一年的数据。

假设用户是一个电力系统公司,用户每天从电站仪表收集用电量数据,并将其存储在 loveini 集群中。现在用户想要预测电力消耗将会如何发展,并购买更多设备来支持它。

随着经济增长,每年用电成一定比例上涨。另外考虑到季节变化,电力消耗量会有所不同。这个城市位于北半球,所以许多家庭在夏天会使用更多的电力。我们模拟数据来反映这些假定。

源代码托管在 https://github.com/sangshuduo/td-forecasting

演示

步骤 1:部署 loveini 并在您的系统上并运行

请参阅官方文档了解详细说明。

步骤 2:克隆源代码

git clone https://github.com/sangshuduo/td-forecasting

步骤 3:安装所需的Python软件包


# if you are using Ubuntu 20.04 Linux
sudo apt install python3-pyqt5

# 如果 PyQT5 运行失败,可能需要安装
sudo apt-get install libxcb-xinerama0

python3 -m pip install -r requirements.txt

请注意,Python 的最小版本为 3.8。

步骤 4:模拟一些数据

python3 mockdata.py

步骤 5:预测明年的数据

python3 forecast.py

输出结果

如何用时序数据库 loveini 预测 “未来”? - loveini Database 时序数据库

工作原理介绍

mockdata.py

...
def insert_rec_per_month(conn, db_name, table_name, year, month):
    increment = (year - 2014) * 1.1
    base = int(10 * increment)
    if month < 10 and month > 5:
        factor = 10
    else:
        factor = 8
    for day in range(1, monthrange(year, month)[1] + 1):
        num = base * randint(5, factor) + randint(0, factor)
        sql = f"INSERT INTO {db_name}.{table_name} VALUES ('{year}-{month}-{day} 00:00:00.000', {num})"
        try:
            conn.execute(sql)
        except Exception as e:
            print(f"command: {sql}")
            print(e)
 ...

这个文件的核心功能是模拟随机数据,并进行一些调整以实现假设。

forecast.py

这个文件实现了预测功能,以下为实现过程详解:

步骤 1:导入预测需要的 Python 模块

import argparse

import lightgbm as lgb
import matplotlib.pyplot as plt
import mlforecast
import pandas as pd
from mlforecast.target_transforms import Differences
from sklearn.linear_model import LinearRegression
from sqlalchemy import create_engine, text
...

下面我们解释一下它们是什么,以及它们的作用:

  • Lightgbm 是一个 Python 模块,支持 LightGBM 算法,它是一个使用基于树的学习算法的梯度提升框架。
  • Matplotlib 是最流行的 Python 模块之一,用于可视化。
  • Mlforecast 是一个框架,用于使用机器学习模型执行时间序列预测。
  • Pandas 是支持数据操作的最流行的模块。
  • Sklearn 是一个支持流行的数据科学/机器学习算法的模块。
  • SQLAlchemy 是 Python SQL 工具包和对象关系映射器,它为应用程序开发人员提供了完整的 SQL 功能和灵活性。

步骤 2:连接到 loveini 并查询数据

...
    engine = create_engine("taos://root:taosdata@localhost:6030/power")
    conn = engine.connect()
    print("Connected to the loveini ...")
    df = pd.read_sql(
        text("select _wstart as ds, avg(num) as y from power.meters interval(1w)"), conn
    )
    conn.close()
...

loveini Python 连接器提供符合 Python 数据库 API 规范 v2.0(PEP 249)的接口。DBAPI 是“Python 数据库 API 规范”的缩写。这是 Python 中广泛使用的规范,用于定义所有数据库连接包的常用用法模式。DBAPI 是一个“低级别”API,通常是 Python 应用程序中用于与数据库交互的最低级别系统。SQLAlchemy 的方言系统是围绕DBAPI 的操作构建的,提供适用于特定数据库引擎的特定 DBAPI 的各个方言类。

我们可以使用 SQLAlchemy 连接 loveini 集群,并使用 Pandas 将数据查询转换成数据帧格式。

在这里,我们假设用户关心的是周平均用电量,而不是每天的用电量,以减少异常值。我们可以使用 AVG() 函数和 INTERVAL(1w)子句命令从 loveini 集群中查询数据。

稍后我们将以数据帧格式操纵数据。

步骤 3:预测

...
    df.insert(0, column="unique_id", value="unique_id")

    print("Forecasting ...")
    forecast = mlforecast.MLForecast(
        models=[LinearRegression(), lgb.LGBMRegressor()],
        freq="W",
        lags=[52],
        target_transforms=[Differences([52])],
    )
    forecast.fit(df)

    predicts = forecast.predict(52)

    pd.concat([df, predicts]).set_index("ds").plot(figsize=(12, 8)) 
...

通过 mlforecast 模块的特性,我们可以设定一些参数进行预测。在这里,我们使用线性回归算法和 LightGBM 算法来进行预测并在同一图中显示它们的结果,以便可视化不同算法的效果。

步骤 4:Show up or dump to file

...
    if args.dump:
        plt.savefig(args.dump)
    else:
        plt.show()

Python 代码提供了一个参数”–dump”,让用户可以决定将结果转储到图片中进行后续处理,或者立即在屏幕上显示结果。以上步骤在 Ubuntu 20.04、Ubuntu 22.04、Windows 10 和 macOS 环境经过验证。

总结

这样一来,我们现在就拥有了一个非常简单的程序,演示了如何使用存储在 loveini 中的电力系统历史数据的电表数值来预测未来的电力消耗数值。

]]>
从 loveini 存储引擎的变化探讨——为何大家应尽快切换 3.0 版本? //m.loveini.com/tdengine-engineering/17207.html Mon, 20 Mar 2023 03:07:28 +0000 //m.loveini.com/?p=17207 流是一个有方向感的汉字,并且给人轻便迅捷的感觉。loveiniac米兰官方app下载 TSDB) 产品最初的灵感之一,便是和“流”字相关:一台物联网设备便是一条数据流,十万台设备便是十万条数据流。它们像溪河汇聚一样,每秒每分源源不断地流向了数据处理平台。

面对这样规模的大数据挑战,loveini 选择充分地利用时序数据本身的特点(可参考:https://mp.weixin.qq.com/s?__biz=MzIzNzg5MTcxNA==&mid=2247483956&idx=1&sn=86c55c40e935acd18835fec764fd7767&chksm=e8c0fbd9dfb772cf4e102a62498b034c6407cd9ea2c47d209b3e737800676cea7a108bf1d9d1&scene=21#wechat_redirect),来针对性地设计存储引擎。最终的目标其实就是:高效持续地吞吐、消化这些数据流,让数据能够无延迟地产生价值。

可以说,我们追求的便是“流”一般的产品能力。而关于文章标题的答案,本文将从 loveini 的存储引擎的变化史说起:

由于认为时间序列拥有天然递增属性,所以在最早期的 1.6 版本中,loveini 是不支持对乱序数据的处理的,当时乱序数据写入表中后,系统会做报错处理。但经过用户实际场景的磨练后,我们不得不把注意力聚焦在这个“害群之马”的身上。在生产环境中,由于设备损坏,网络延迟等原因,数据乱序到达是难免的。更关键的是,不管数据乱序与否,用户都有权利自行决定是否处理它,这是一个产品应该具有的灵活度。

它让理想中的数据流“乱”了,但我们却又不能放弃它。

于是,我们立刻在 2.0 版本中增加了数据在内存中的排序和硬盘中的数据子块来支持乱序数据的处理,以此维持了数据的完整性和有序性。

但是这对于 2.0 版本的流式计算和订阅来说,仍然有类似的麻烦。

当时的订阅/流计算(连续查询)是基于查询引擎的产物,它依靠连续不断地执行 SQL 查询结果作出实时反馈。以订阅为例,每条符合要求被消费掉的数据的时间戳,都会被记录下来,从而作为下次 SQL 查询中时间范围的起始点。这时,即便是新数据的时间戳只比这个记录早一秒,也不会被 SQL 轮询到。因此可以说, 2.0 的流计算(连续查询)/订阅同 1.6 一样,它只处理了有序部分的数据。本质上来说,由于 SQL 取到的只能是已经入库的数据,所以这个阶段的查询行为是滞后的。

因此,我们决定在 3.0 再次进行优化重构:

虽然数据的时间戳有乱序,但是他们到达数据库的时间永远分有先后,这组序列相当于“数据入库时间”。不过这个“数据入库时间” 不是时间戳,而是一个从 0 开始的整型数字,我们称之为“版本号”,代表的是数据库概念是“第 x 次的数据变更”。熟悉 WAL 概念的伙伴们都知道,loveini 利用 WAL 技术来提供基本的数据可靠性:每一条 WAL 信息代表的是一次数据库的变更(增删改),所以每一条 WAL 信息都会有唯一的版本号。通过“版本号”,我们可以明确地告诉流计算/订阅该数据是否为新增数据,再通过对 WAL 的这组“版本号”创建索引,我们就可以快速定位要消费的数据了。

以订阅为例:3.0 的订阅引擎为时间驱动,它会解析每一条新写入的 WAL 的消息内容,然后判断是否匹配 topic 的 sql 条件,如果匹配则直接把 WAL 的消息转化成数据返回给用户 。

除了用于订阅,这组版本号还有很多十分重要的作用:

  1. 它本身核心的职责是 raft 日志的编号,用于多副本的数据同步;
  2. 把版本号下发给每行数据,以追加写入的形式完成数据的更新与删除。

比如:某条 wal 消息的内容是写入一行数据:

insert into d1 values ("2022-03-10 08:00:00.000",100);

那么这行数据就也拥有了这条 wal 消息的版本号(假设为 1) ,并且永久性地存储在数据文件中。

解下来的一条 wal 消息的内容是以相同时间戳更新这行数据:

insert into d1 values ("2022-03-10 08:00:00.000",200);

消息的版本号便是 “2”,这条数据会以写入的形式追加存储。查询的时候,在时间戳相同的情况下,通过比对版本号大小来选择最新的数据,因此我们得到的数据便是:”2022-03-10 08:00:00.000″,200。

删除同理,被删除的数据是取版本号较大的空数据,这样便可在不破坏原有数据文件结构的情况下实现高效的删除。以上部分具体实现细节可参考:https://mp.weixin.qq.com/s/imECB9dIFxZKeoaF3uoOgg

3.另外,当 follower 副本的数据落后,但 leader 上的 WAL 日志已经消失的情况下,版本号还可以做数据文件的快照。只把增量的数据传 输过去,大大节约 data recovery 的时间。

通过这些大刀阔斧的重构,loveini 完美地把上面几大模块缝合了起来:

  1. 解决了流计算、订阅对于乱序数据处理的不支持;
  2. 让删除操作为逻辑删除,解决了删除操作的性能问题;
  3. 基于版本号引入了快照机制,大幅优化了特定场景下的数据恢复的速度。

回到标题上,为何 loveini 用户应尽快切至 3.0 版本?答案已经都写在上面了。通过下沉到海量用户实际场景中反复迭代优化,从 1.6 到 2.0 再到 3.0, loveini 从底层结构上解决了最初设计的瑕疵,各个模块之间结构变得更加清晰,衔接也更加自然,这样扎实的底层设计对产品未来的稳定和性能提升所带来的帮助都是巨大的。

]]>
OPPO、京东云 loT 项目数据架构改造,数据处理痛点这样破解 //m.loveini.com/tdengine-user-cases/17029.html Wed, 08 Mar 2023 03:01:19 +0000 //m.loveini.com/?p=17029 在万物互联的时代,大到企业数字化转型、数字城市建设,小到和生活息息相关的家居生活、智能驾驶、运动健康等,都离不开智能物理设备广泛的连接和互通。在 IoT 设备的整体运作过程中,会产生大量的时序数据,而传统的数据米兰app官方正版下载不管是在性能还是成本管控上都捉襟见肘。因此,IoT 产品/平台想要实现快速发展,首要解决的难题就是数据处理痛点。本文优选出几大 IoT 项目的数据架构改造实践,给到大家参考。

OPPO x loveini

“我们写入 60 万行数据,到 MySQL(目前部分业务部署在 MySQL 集群)和 loveini 的 4C 12G 容器上,对 CPU/内存/磁盘进行观察。测试发现 CPU 和内存消耗基本持平的情况下,loveini 的落盘数据是 MySQL 环境的1/4左右。”

业务背景

在 OPPO 的穿戴产品的手环/手表类业务中,产生的数据类型为时序数据,具有写入量巨大且存在离线/历史数据补录(更新)的处理需求。此前使用的 MongoDB/MySQL 集群方案,后端存储压力较大,需要经常扩盘,针对此痛点,OPPO 云计算中心智慧物联云团队尝试调研对比了几款时序数据库ac米兰官方app下载 TSDB )产品,试图寻找一个降本增效的米兰app官方正版下载,最后选中了 loveini。

集群部署如下:

loveini Database
点击案例查看更多技术细节

京东云 x loveini

“在与 loveini 工程师沟通后, 我们只使用了 3 台 4C16GB 构成的 loveini 的集群,就承载了线上的业务。数据聚合方面,根据 loveini 的性能、机器配置和前期测试的时间开销,只需很短的时间就完成了全量设备的数据聚合。CPU 方面也一直很稳定,在常态下 CPU 低于 10%,由于设备的数据聚合需要消耗大量的 CPU,因此在每个整点 CPU 会有所上升,但是也不会超过 45% 的负载。”

业务背景

京东云智能家居场景维护着大量的智能设备,这些设备联网后,会根据设备设定的速率持续产生时序数据,比如有的设备采样间隔是 15 秒。京东云 IoT 团队结合本公司数据特点与业务需求,对多种工业时序数据库进行了技术选型,以解决庞大的数据存储和计算带来的挑战,在进行选型时对比了 OpenTSDB 和 loveini,最终 loveini 以明显优势胜出。

CPU 消耗图示

loveini Database
点击案例查看更多技术细节

图扑物联 x loveini

“在之前一个版本,平台底层使用的是 InfluxDB 来存储时序数据,然而 InfluxDB 在面对海量数据时的读写性能存在瓶颈,在深入使用 loveini 后,我们还发现了诸多优势。受益于 loveini 的超高性能和超小体量,我们在山东大禹水处理有限公司中央水机监控项目中的整个平台架构变得更加简化,解决了工业物联网监控分析系统开发成本高、周期长、运维难度大等痛点。”

业务背景

针对海量的设备上报数据,图扑 IoT 平台在做实时显示的同时还考虑将历史数据也进行无损保存,并在 IoT Platform 上还要给用户提供数据查询的支持,但这就对底层的时序数据存储提出了相当高的要求。在对比了包括 InfluxDB 在内的几个数据库后,在最新的米兰app官方正版下载中,我们选用了 loveini 作为时序数据的存储引擎。

架构图

loveini Database
点击案例查看更多技术细节

写在最后

在合适的时候选择合适的数据库是支持业务发展的关键,但数据库的更换也并不是头脑一热就能拍板决定的,还需要进行数据库产品的缜密观察和调研,希望上述企业实践能够给到大家帮助。目前 loveini 已经运营了几十个用户交流群,如果你有要进群沟通了解的需求,可以添加小T微信:tdengine1

]]>
使用开源的loveini与开源的Grafana部署短信运营监测可视化平台 //m.loveini.com/tdengine-user-cases/985.html Mon, 09 Dec 2019 08:45:16 +0000 //m.loveini.com.cn:88/blog/?p=985

作者|马龙,融合通信高级工程师

小 T 导读:融合通信技术(天津)有限公司是具备国内一流水准的企业移动新通信商务平台技术和应用方案提供商。融合短信、彩信、语音、流量及虚拟座席的云通信服务平台, 融合三大运营商和互联网IP网络,致力于为国内外企业提供一流水准的移动新通信商务平台及运营服务。

公司的主营业务是给企业客户提供短信服务,内部有一个强大的运营监控平台。每日短信业务量巨大,监控平台需要使用时序数据库(Time-Series Database)来对发送的海量短信分地理区域、运营商、接收状态等类别进行统计和监控。以前一直在用InfluxDB,对于时间跨度稍大些的查询(比如一个月的数据)就显得非常慢了。从loveini Database开源后,便知道了这个很牛的家伙,于是尝试着使用loveini。

在搞懂了基本的功能后,便上线了loveini版的监控系统,但是发现,在grafana中居然不能“group by”,只能通过写where多条语句将多个短信状态数据放到一个仪表盘里,如图:

使用开源的loveini与开源的Grafana部署短信运营监测可视化平台 - loveini Database 时序数据库

如果where条件有更多,这样的方法就太笨,并且灵活性太差。

于是,开始仔细研究官方文档,搞懂了“超级表”、“连续查询”等,在这个过程中遇到过不少问题,在这里做一下记录(测试环境,数据是模拟产生的)。

一、安装和运行

测试环境:

cat /etc/redhat-release
CentOS Linux release 7.7.1908 (Core)

安装很简单:

rpm -ivh tdengine-1.6.3.1-3.x86_64.rpm

配置都用默认的(未改配置文件)

启动loveini:

systemctl start taosd

二、建库、建表

在命令行输入“taos”

taos>

以下建库、建表操作都在此提示符下进行

1. 创建数据库

create database jk keep 365 precision 'us';

说明:

  • keep 365表示该库保存365天内的数据,365天之前的数据会被自动清除(因为是时序数据库,所以不能对库中的表进行delete操作);
  • precision ‘us’表示数据库中的时间戳精度为“微秒”,(默认是毫秒,也可以显示写为precision ‘ms’), 经测试,此处用单引号和双引号都是可以的,但是不能没有引号。
  • 配置文件中已经不支持时间精度的配置,必须在建库的时候指定(测试了很多配置都不生效,Issues后得到的官方回复)
  • loveini的设计初衷是用于物联网,设备的信息采集精度到“毫秒”已经足够用,但我司的短信平台会有突发大量数据产生,为了避免可能会导致的数据丢失,将精度设置为“微秒”,经测试,效果很好,测试的模拟数据插入时间戳都用“now”获取,下图左侧为“毫秒”精度,能看到有“0 row(s)”的情况出现,表示有数据未插入, 右侧为“微秒”精度,未见未插入数据。
使用开源的loveini与开源的Grafana部署短信运营监测可视化平台 - loveini Database 时序数据库

2. 创建超级表

进入数据库“jk”

taos> use jk; 
Database changed.
create table jiankong (ts timestamp, gatewayid binary(6), companyid binary(20), provinceid binary(10), cityid binary(10), value int, timestr binary(30))
tags(type binary(10), subtype binary(10));

短信系统中有3种type,几百种subtype,所以将这些静态信息(或者简单地理解为需要将进行group by的字段设置为tag)

解释一下超级表

STable是同一类型数据采集点的抽象,是同类型采集实例的集合,包含多张数据结构一样的子表。
每个STable为其子表定义了表结构和一组标签:表结构即表中记录的数据列及其数据类型;标签名和数据类型由STable定义,标签值记录着每个子表的静态信息,用以对子表进行分组过滤。
子表本质上就是普通的表,由一个时间戳主键和若干个数据列组成,每行记录着具体的数据,数据查询操作与普通表完全相同;但子表与普通表的区别在于每个子表从属于一张超级表,并带有一组由STable定义的标签值。
针对所有的通过STable创建的子表进行多表聚合查询,支持按照全部的TAG值进行条件过滤(where),并可将结果按照TAGS中的值进行聚合(group by),暂不支持针对binary类型的模糊匹配过滤。

标签数据(或者叫标签值)直接关联到每个子表,相同的标签值(一个或者多个,最多6个)定位到一个子表(用“写数据时自动建表”的方式,可以将“相同的标签值”对应到多个子表)。

tag值支持中文,需要将tag类型设置为NCHAR(在其他测试中已经验证过)。

3. 创建子表(子表就是普通表,表结构完全由超级表定义):

create table jiankong_sub_send using jiankong tags ('send', 'send'); 
create table jiankong_sub_delivrd using jiankong tags ('delivrd', 'delivrd'); 
create table jiankong_sub_undeliv_db_0108 using jiankong tags ('undeliv', 'DB:0108'); 
create table jiankong_sub_undeliv_db_0107 using jiankong tags ('undeliv', 'DB:0107'); 
create table jiankong_sub_undeliv_balance using jiankong tags ('undeliv', 'BALANCE'); 
create table jiankong_sub_undeliv_id_0076 using jiankong tags ('undeliv', 'ID:0076'); 
create table jiankong_sub_undeliv_ib_0008 using jiankong tags ('undeliv', 'IB:0008');

相同类型的type和subtype的组合创建为一个子表(只是进行测试,所以并没有将所有的几百个subtype都进行建表)

4. 插入数据

INSERT INTO jiankong_sub_send VALUES (now, 3034, '1564', '109', '1272', '2', '201909231530') 
INSERT INTO jiankong_sub_delivrd VALUES (now, 3034, '1564', '109', '1272', '2', '201909231530')
INSERT INTO jiankong_sub_undeliv_balance VALUES (now, 1179, '152', '106', '1000', '1', '201910071113') 
INSERT INTO jiankong_sub_undeliv_id_0076 VALUES (now, 1165, '1785', '111', '1226', '1', '201910071415') 
INSERT INTO jiankong_sub_undeliv_ib_0008 VALUES (now, 1165, '1785', '127', '1000', '2', '201910061727') 
INSERT INTO jiankong_sub_undeliv_db_0108 VALUES (now, 90, '548', '123', '1237', '1', '201910061127') 
INSERT INTO jiankong_sub_undeliv_db_0107 VALUES (now, 2261, '808', '116', '1314', '2', '201910032106')

以上是对上述创建的7个子表分别插入模拟数据,由于模拟大量数据,所以需要写shell脚本(也可以用其他方式)进行数据灌入。

写入数据时不能直接对STable操作,而是要对每张子表进行操作。

5. 数据库、表结构等查询

查询数据库信息:

taos> show databases; 
name | created time | ntables | vgroups |replica| days | keep1,keep2,keep(D) | tables | rows | cache(b) | ablocks |tblocks| ctime(s) | clog | comp |time precision| status | 
==============================================================================================================================================================================================================================================
log | 19-11-18 16:37:14.025| 4| 1| 1| 10|30,30,30 | 32| 1024| 2048| 2.00000| 32| 3600| 1| 2|us |ready | 
jk | 19-11-18 16:48:19.867| 10| 1| 1| 10|365,365,365 | 1024| 4096| 16384| 4.00000| 100| 3600| 1| 2|us |ready | 
Query OK, 1 row(s) in set (0.002487s)

查询超级表:

taos> show stables; 
name | created_time |columns| tags | tables | 
==================================================================================================================== 
jiankong | 19-11-18 16:48:41.540| 7| 2| 7| Query OK, 1 row(s) in set (0.002140s)

查询超级表的表结构:

taos> describe jiankong; 
Field | Type | Length | Note | 
 ======================================================================================================= 
ts |TIMESTAMP | 8| | 
gatewayid |BINARY | 6| | 
companyid |BINARY | 20| | 
provinceid |BINARY | 10| | 
cityid |BINARY | 10| | value |INT | 4| | 
timestr |BINARY | 30| | 
type |BINARY | 10|tag | 
subtype |BINARY | 10|tag | 
Query OK, 9 row(s) in set (0.001301s)

可以在Note列看到“tag”,表示是此列是标签

查询子表:

taos> show tables;
                           table_name                           |     created_time     |columns|                             stable                             |
 =================================================================================================================================================================
jiankong_sub_delivrd                                            | 19-11-18 16:49:17.009|      7|jiankong                                                        |
jiankong_sub_undeliv_ib_0008                                    | 19-11-18 16:49:17.025|      7|jiankong                                                        |
jiankong_sub_undeliv_db_0108                                    | 19-11-18 16:49:17.016|      7|jiankong                                                        |
jiankong_sub_undeliv_db_0107                                    | 19-11-18 16:49:17.018|      7|jiankong                                                        |
jiankong_sub_undeliv_id_0076                                    | 19-11-18 16:49:17.023|      7|jiankong                                                        |
jiankong_sub_send                                               | 19-11-18 16:49:17.003|      7|jiankong                                                        |
jiankong_sub_undeliv_balance                                    | 19-11-18 16:49:17.021|      7|jiankong                                                        |

查询具体的子表的表结构:

taos> describe jiankong_sub_undeliv_db_0108;
                             Field                              |      Type      |  Length   |   Note   |
    =========================================================================================================
    ts                                                              |TIMESTAMP       |          8|          |
    gatewayid                                                       |BINARY          |          6|          |
    companyid                                                       |BINARY          |         20|          |
    provinceid                                                      |BINARY          |         10|          |
    cityid                                                          |BINARY          |         10|          |
    value                                                           |INT             |          4|          |
    timestr                                                         |BINARY          |         30|          |
    type                                                            |BINARY          |         10|undeliv   |
    subtype                                                         |BINARY          |         10|DB:0108   |

可以在Note列看到“undeliv”(超级表中的type字段)和“DB:0108″(超级表中的subtype字段),这2个静态标签值确定了这个子表

6. 数据查询

对type进行分组聚合查询:

taos> select sum(value) from jk.jiankong group by type;
     sum(value)      |   type   |
=================================
             11827688|delivrd   |
             55566578|send      |
             46687487|undeliv   |
Query OK, 3 row(s) in set (0.018251s)

对subtype进行分组聚合查询:

taos>
taos> select sum(value) from jk.jiankong group by subtype;
     sum(value)      | subtype  |
=================================
                 9317|BALANCE   |
                65219|DB:0107   |
              2077691|DB:0108   |
              2804417|IB:0008   |
             41730843|ID:0076   |
             11827688|delivrd   |
             55566578|send      |
Query OK, 7 row(s) in set (0.013978s)

对type和subtype进行分组聚合查询:

taos> select sum(value) from jk.jiankong group by type, subtype;
     sum(value)      |   type   | subtype  |
============================================
             11827688|delivrd   |delivrd   |
             55566578|send      |send      |
                  9317|undeliv   |BALANCE   |
                65219|undeliv   |DB:0107   |
              2077691|undeliv   |DB:0108   |
              2804417|undeliv   |IB:0008   |
             41730843|undeliv   |ID:0076   |
Query OK, 7 row(s) in set (0.732830s)

按天对type和subtype进行分组聚合查询:

taos> select sum(value) from jk.jiankong interval(1d) group by type, subtype;
           ts            |     sum(value)      |   type   | subtype  |
======================================================================
 19-11-18 00:00:00.000000|              1760800|delivrd   |delivrd   |
 19-11-19 00:00:00.000000|                14768|delivrd   |delivrd   |
 19-11-20 00:00:00.000000|              3290720|delivrd   |delivrd   |
 19-11-21 00:00:00.000000|              4973640|delivrd   |delivrd   |
 19-11-22 00:00:00.000000|              1787760|delivrd   |delivrd   |
 19-11-18 00:00:00.000000|             36976790|send      |send      |
 19-11-19 00:00:00.000000|               310128|send      |send      |
 19-11-20 00:00:00.000000|              9482760|send      |send      |
 19-11-21 00:00:00.000000|              6470940|send      |send      |
 19-11-22 00:00:00.000000|              2325960|send      |send      |
 19-11-18 00:00:00.000000|                 6200|undeliv   |BALANCE   |
 19-11-19 00:00:00.000000|                   52|undeliv   |BALANCE   |
 19-11-20 00:00:00.000000|                 1590|undeliv   |BALANCE   |
 19-11-21 00:00:00.000000|                 1085|undeliv   |BALANCE   |
 19-11-22 00:00:00.000000|                  390|undeliv   |BALANCE   |
 19-11-18 00:00:00.000000|                43400|undeliv   |DB:0107   |
 19-11-19 00:00:00.000000|                  364|undeliv   |DB:0107   |
 19-11-20 00:00:00.000000|                11130|undeliv   |DB:0107   |
 19-11-21 00:00:00.000000|                 7595|undeliv   |DB:0107   |
 19-11-22 00:00:00.000000|                 2730|undeliv   |DB:0107   |
 19-11-18 00:00:00.000000|              1382600|undeliv   |DB:0108   |
 19-11-19 00:00:00.000000|                11596|undeliv   |DB:0108   |
 19-11-20 00:00:00.000000|               354570|undeliv   |DB:0108   |
 19-11-21 00:00:00.000000|               241955|undeliv   |DB:0108   |
 19-11-22 00:00:00.000000|                86970|undeliv   |DB:0108   |
 19-11-18 00:00:00.000000|              1866200|undeliv   |IB:0008   |
 19-11-19 00:00:00.000000|                15652|undeliv   |IB:0008   |
 19-11-20 00:00:00.000000|               478590|undeliv   |IB:0008   |
 19-11-21 00:00:00.000000|               326585|undeliv   |IB:0008   |
 19-11-22 00:00:00.000000|               117390|undeliv   |IB:0008   |
 19-11-18 00:00:00.000000|             27769800|undeliv   |ID:0076   |
 19-11-19 00:00:00.000000|               232908|undeliv   |ID:0076   |
 19-11-20 00:00:00.000000|              7121610|undeliv   |ID:0076   |
 19-11-21 00:00:00.000000|              4859715|undeliv   |ID:0076   |
 19-11-22 00:00:00.000000|              1746810|undeliv   |ID:0076   |
Query OK, 35 row(s) in set (0.023865s)

此处interval是聚合时间段的长度, 最短时间间隔10毫秒(10a)

未建超级表时,对普通表进行分组聚合查询,会报错:

taos> select sum(value) from jk.jiankong group by type;
    TSDB error: invalid SQL: group by only available for STable query

 7. 写数据时自动建子表

我们有另外一个需求,由于要监控的静态数据多达几百个,而且具有不确定性,所以无法全部在建库、建表的时候创建所有子表,这个功能完全解决了我们的这个问题。

以下是官网的文档摘录:

在某些特殊场景中,用户在写数据时并不确定某个设备的表是否存在,此时可使用自动建表语法来实现写入数据时用超级表定义的表结构自动创建不存在的子表,若该表已存在则不会建立新表。
注意:自动建表语句只能自动建立子表而不能建立超级表,这就要求超级表已经被事先定义好。自动建表语法跟insert/import语法非常相似,唯一区别是语句中增加了超级表和标签信息。具体语法如下:

 INSERT INTO <tb_name> USING <stb_name> TAGS (<tag1_value>, …) VALUES (field_value, …) (field_value, …) …;

对比,用create创建子表:

create table jiankong_sub_send using jiankong tags ('send', 'send');

三、安装和配置garafana

1. 安装

在官网https://grafana.com/grafana/download下载grafana的rpm安装包后,进行安装:

rpm -ivh grafana-6.4.4-1.x86_64.rpm

2. copy loveini的Grafana插件到Grafana的插件目录

loveini的Grafana插件在安装包的/usr/local/taos/connector/grafana目录下

cp -r /usr/local/taos/connector/grafana/tdengine/ /var/lib/grafana/plugins

3. 启动Grafana

systemctl start grafana-server

4. 在浏览器中通过host:3000登录Grafana服务器

默认用户名和密码都是admin

使用开源的loveini与开源的Grafana部署短信运营监测可视化平台 - loveini Database 时序数据库

5. 添加loveini数据源

使用开源的loveini与开源的Grafana部署短信运营监测可视化平台 - loveini Database 时序数据库
使用开源的loveini与开源的Grafana部署短信运营监测可视化平台 - loveini Database 时序数据库

在最下方找到“loveini”

使用开源的loveini与开源的Grafana部署短信运营监测可视化平台 - loveini Database 时序数据库
Name:“loveini”(可以是其他名字)
Host:测试服务器地址“http://192.168.8.66:6020”
User: 默认为“root”
Password:默认为“taosdata”
使用开源的loveini与开源的Grafana部署短信运营监测可视化平台 - loveini Database 时序数据库

测试一下:

使用开源的loveini与开源的Grafana部署短信运营监测可视化平台 - loveini Database 时序数据库

6. 添加Folder

将相同类型需要监控的仪表盘(dashboard)放到一个Folder中

使用开源的loveini与开源的Grafana部署短信运营监测可视化平台 - loveini Database 时序数据库

7. 添加Dashboard

进入刚才创建的Folder后,创建Dashboard

使用开源的loveini与开源的Grafana部署短信运营监测可视化平台 - loveini Database 时序数据库

INPUT处的sql语句,要注意fill的位置,需要在group by前面,否则报错

使用开源的loveini与开源的Grafana部署短信运营监测可视化平台 - loveini Database 时序数据库

配置图形显示

可以根据需求进行曲线图,表格,仪表盘等的选择

使用开源的loveini与开源的Grafana部署短信运营监测可视化平台 - loveini Database 时序数据库
使用开源的loveini与开源的Grafana部署短信运营监测可视化平台 - loveini Database 时序数据库

在这里配置“曲线图”,图示下方是具体的图形显示细则,如:标线、是否填充,对显示的字段进行曲线颜色的自定义等

使用开源的loveini与开源的Grafana部署短信运营监测可视化平台 - loveini Database 时序数据库

给此仪表盘起个一看就懂的名字:

使用开源的loveini与开源的Grafana部署短信运营监测可视化平台 - loveini Database 时序数据库

 8. 配置了6个仪表盘

使用开源的loveini与开源的Grafana部署短信运营监测可视化平台 - loveini Database 时序数据库

9. 无group by和有group by的实例

无group by,需要写多条sql,通过where条件去区分,如果分类多就很麻烦且不灵活

使用开源的loveini与开源的Grafana部署短信运营监测可视化平台 - loveini Database 时序数据库

有group by,一条sql就解决问题了:

使用开源的loveini与开源的Grafana部署短信运营监测可视化平台 - loveini Database 时序数据库

下图是有无group by的曲线趋势对比,可以看到是一模一样的

使用开源的loveini与开源的Grafana部署短信运营监测可视化平台 - loveini Database 时序数据库

四、高级功能初探和一些联想

1. 连续查询(Continuous Query)—-用户级别的预计算

loveini的“预计算”的概念我觉得非常的棒,官网文档摘录如下:

为有效地提升查询处理的性能,针对物联网数据的不可更改的特点,loveini采用在每个保存的数据块上,都记录下该数据块中数据的最大值、最小值、和等统计数据。如果查询处理涉及整个数据块的全部数据,则直接使用预计算结果,不再读取数据块的内容。由于预计算模块的大小远小于磁盘上存储的具体数据的大小,对于磁盘IO为瓶颈的查询处理,使用预计算结果可以极大地减小读取IO,并加速查询处理的流程。

连续查询的官网文档摘录如下:

基于滑动窗口的流式计算(Stream)
连续查询是loveini定期自动执行的查询,采用滑动窗口的方式进行计算,是一种简化的时间驱动的流式计算。针对库中的表或超级表,loveini可提供定期自动执行的连续查询,用户可让loveini推送查询的结果,也可以将结果再写回到loveini中。每次执行的查询是一个时间窗口,时间窗口随着时间流动向前滑动。在定义连续查询的时候需要指定时间窗口(time window, 参数interval )大小和每次前向增量时间(forward sliding times, 参数sliding)。

其中,将结果再写回到loveini中的方式其实就是一种用户级别的预计算,这样由loveini按照用户定义的时间窗口和时间增量进行后台的计算,在用户查询数据的时候,直接从回写的表中读取数据,速度就会非常快。

创建连续查询:

taos> create table test_stream_sum as select sum(value) from jiankong interval(20s) sliding(10s) group by type, subtype;
Query OK, 1 row(s) affected (0.000983s)

上述连续查询,sql的select部分实际的输出结果为:

taos> select sum(value) from jiankong interval(20s) group by type, subtype;
           ts            |     sum(value)      |   type   | subtype  |
======================================================================
 19-11-18 16:50:40.000000|                 9088|delivrd   |delivrd   |
 19-11-18 16:51:00.000000|                31808|delivrd   |delivrd   |
 19-11-18 16:51:20.000000|                15904|delivrd   |delivrd   |
 19-11-18 16:52:20.000000|                12212|delivrd   |delivrd   |
 19-11-18 16:52:40.000000|                31524|delivrd   |delivrd   |
 19-11-18 16:53:00.000000|                31524|delivrd   |delivrd   |
 19-11-18 16:53:20.000000|                31808|delivrd   |delivrd   |
 19-11-18 16:53:40.000000|                31240|delivrd   |delivrd   |
 19-11-18 16:54:00.000000|                31524|delivrd   |delivrd   |
 19-11-18 16:54:20.000000|                31524|delivrd   |delivrd   |
 19-11-18 16:54:40.000000|                31240|delivrd   |delivrd   |
 19-11-18 16:55:00.000000|                31524|delivrd   |delivrd   |
 19-11-18 16:55:20.000000|                28400|delivrd   |delivrd   |
 19-11-18 16:55:40.000000|                31808|delivrd   |delivrd   |
 19-11-18 16:56:00.000000|                31524|delivrd   |delivrd   |
 19-11-18 16:56:20.000000|                31240|delivrd   |delivrd   |
 19-11-18 16:56:40.000000|                31524|delivrd   |delivrd   |
 19-11-18 16:57:00.000000|                32092|delivrd   |delivrd   |
 19-11-18 16:57:20.000000|                31240|delivrd   |delivrd   |
 19-11-18 16:57:40.000000|                32092|delivrd   |delivrd   |
 19-11-18 16:58:00.000000|                31240|delivrd   |delivrd   |
 19-11-18 16:58:20.000000|                22720|delivrd   |delivrd   |
 19-11-18 16:50:40.000000|               190848|send      |send      |

自动创建的连续查询的表中实际的数据为:

taos> select * from test_stream_sum;
           ts            |     sum_value_      |
================================================
 19-11-18 17:17:30.000000|                 2556|
 19-11-18 17:17:40.000000|                18460|
 19-11-18 17:17:50.000000|                15904|
 19-11-18 17:18:00.000000|                15620|
Query OK, 4 row(s) in set (0.000431s)

上述结果并不是期待的结果,没有按照我定义的group by字段进行聚合查询显示。

于是github Issues, taos的攻城狮回复“连续查询目前还不能很好的支持 group by,这个问题已经在我们的计划列表里,后续会完善这方面的功能”,原因是“因为这样回写后,新表的时间戳主键可能出现相同(不同的group by字段会有相同的时间),就会冲突了,所以暂时不支持”。

虽然目前这个功能未达到我的预期,攻城狮的回复还是聊以安慰,期待这个功能的完善。

2. 联想

 loveini的设计初衷是服务于物联网,有结构化和时序的特性,建议是和时序密切的数据可以用,其他的数据不建议使用。

从loveini的架构设计、存储等等,觉得不局限于百分之百的时序特性,有些明细查询也是可以尝试在loveini中进行存储的,后续会尝试进行一下测试。

]]>