阅读视图

InfluxDB 诡异的时间窗口对齐(是我太傻逼)

✇obaby
作者obaby

之前的项目,将部分数据迁移到了InfluxDB v2 数据库。但是,在查询数据的时候发生了一件很诡异的事情,就是使用不同的时间间隔,返回的数据却完全不一样。

感谢 ymz316 帮我找到了 bug,还是数据处理逻辑的问题。我把 ai 给唬住了,他没分析代码,我也没分析代码。另外一个问题就是上报数据的时间间隔太长了,在时间为 1m 的时候表现出了诡异的行为,根本原因在于 1m 中采样的时候,后面四分钟都没数据(上报频率正好也是 5 分钟)于是采样到了 06 分钟的数据。就成了 01 06 11 的样子,这 tmd 把数据上报频率也给忽略了。

查询代码如下:

def query_data_with_5min_sampling(device_id, start_time, end_time, interval='05m'):
    """
    查询指定设备在时间范围内的数据,支持不同的采样间隔
    :param device_id: 设备ID
    :param start_time: 开始时间
    :param end_time: 结束时间
    :param interval: 采样间隔,支持 '10s', '30s', '01m', '05m', '10m', '30m', '01h',默认为 '05m'
    :return: 采样后的数据列表
    """
    if interval is None or interval == '':
        interval = '05m'
    if 'm' not in interval and 's' not in interval:
        interval = f"0{interval}m" if int(interval) < 10 else f"{interval}m"
    # 验证时间范围,如果大于一天,强制使用5分钟采样
    if end_time - start_time > timedelta(days=1):
        interval = '05m'
    # 如果小于等于一天且没有指定间隔,使用1分钟采样
    elif interval == '5m' and end_time - start_time <= timedelta(days=1):
        interval = '01m'

    query = f"""
    from(bucket: "ts")
        |> range(start: {datetime_to_tz_time_string(datetime_to_utc_time(start_time))}, 
                stop: {datetime_to_tz_time_string(datetime_to_utc_time(end_time))})
        |> filter(fn: (r) => r._measurement == "TSSourceData")
        |> filter(fn: (r) => r.device_id_string == "{device_id}")
        |> filter(fn: (r) => r._field =~ /^(temperature|humidity|health_level)$/)
        |> aggregateWindow(
            every: {interval},
            fn: mean,
            createEmpty: false
        )
        |> pivot(rowKey:["_time"], columnKey: ["_field"], valueColumn: "_value")
    """
    
    tables = client.query_api().query(org="power", query=query)
    
    lines = []
    for table in tables:
        for record in table.records:
            lines.append(record.values)
    return lines

数据支持: ’10s’, ’30s’, ’01m’, ’05m’, ’10m’, ’30m’, ’01h’,默认为 ’05m’

然而,当使用 5 分钟为间隔查询的时候,返回的第一条数据时间竟然是 01 分,不是整点,查询代码:

nt = current_time = get_rounded_time_before_time(8)
    # # print(nt)
    lines = query_data_with_5min_sampling('mddt6825050023_1', nt, datetime.now(), interval='5m')
    # print(lines)
    for data_point in lines:
        # print(data_point)   
        utc_time = data_point.get('_time')
        tz = pytz.timezone('Asia/Shanghai')
        local_time = utc_time.astimezone(tz)
        print(local_time.strftime('%Y-%m-%d %H:%M:%S'))

执行结果:

2025-05-27 01:01:00
2025-05-27 01:06:00
2025-05-27 01:11:00
2025-05-27 01:16:00
2025-05-27 01:21:00
2025-05-27 01:26:00
2025-05-27 01:31:00
2025-05-27 01:36:00
2025-05-27 01:41:00

然而,当时间改成 15 分钟或者其他时间,就完全是按照整点以及时间间隔来的:

lines = query_data_with_5min_sampling('mddt6825050023_1', nt, datetime.now(), interval='15m')

执行结果:

2025-05-27 01:15:00
2025-05-27 01:30:00
2025-05-27 01:45:00
2025-05-27 02:00:00
2025-05-27 02:15:00
2025-05-27 02:30:00
2025-05-27 02:45:00
2025-05-27 03:00:00
2025-05-27 03:15:00
2025-05-27 03:30:00
2025-05-27 03:45:00
2025-05-27 04:00:00
2025-05-27 04:15:00
2025-05-27 04:30:00
2025-05-27 04:45:00
2025-05-27 05:00:00
2025-05-27 05:15:00
2025-05-27 05:30:00
2025-05-27 05:45:00
2025-05-27 06:00:00
2025-05-27 06:15:00

我勒个豆,这么神奇吗?对于这种错误其实猜测可能是返回数据的对齐粒度问题,但是在尝试了使用 offset 等各种参数之后,对于 5 分钟的数据还是返回了 01。直接崩溃,让 cursor 来回改,最后代码改的面目全非了依然没达到效果。只能回滚代码。

这时候鬼使神差想到,这个参数既然是个字符串,那么传个 05m 呢?

lines = query_data_with_5min_sampling('mddt6825050023_1', nt, datetime.now(), interval='05m')

执行结果:

2025-05-27 01:05:00
2025-05-27 01:10:00
2025-05-27 01:15:00
2025-05-27 01:20:00
2025-05-27 01:25:00
2025-05-27 01:30:00
2025-05-27 01:35:00
2025-05-27 01:40:00
2025-05-27 01:45:00
2025-05-27 01:50:00
2025-05-27 01:55:00
2025-05-27 02:00:00
2025-05-27 02:05:00
2025-05-27 02:10:00
2025-05-27 02:15:00
2025-05-27 02:20:00

竟然神奇的治愈了,这尼玛不得不说竟然这么神奇。所以最开始的代码其实是修复之后的代码,对于没有 0 开头的分钟进行填充。

问了下 cursor,给出了下面的答复:

这特性,真是服了,问题是 cursor,为什么不是你发现了告诉我?而是我发现了告诉你呢?

果然是高级 quirk!

The post InfluxDB 诡异的时间窗口对齐(是我太傻逼) appeared first on obaby@mars.

  •  

django TimescaleDB

✇obaby
作者obaby

平时项目使用的都是 mysql 数据库,少数时候会用到 mariadb,而至于时序数据库那基本没用过。而现在,对于一些监控数值需要更高的写入效率,查询效率,所以想着迁移到时序数据库上。

搜了一下,推荐的基本都是 timescadb:

TimescaleDB 是一个基于 PostgreSQL 的开源时序数据库扩展。它将 PostgreSQL 的强大功能与时序数据的优化存储和查询相结合,特别适合处理时间序列数据(如传感器数据、监控数据、金融数据等)。TimescaleDB 提供了高性能的时序数据存储、压缩、自动分区(hypertables)和高效的查询功能。

以下是关于 TimescaleDB 的详细介绍,以及如何在 Django 项目中集成和使用它。


TimescaleDB 的核心特性

  1. 基于 PostgreSQL
    • TimescaleDB 是 PostgreSQL 的扩展,因此你可以使用 PostgreSQL 的所有功能(如 ACID 事务、SQL 语法、JSONB 支持等)。
    • 兼容现有的 PostgreSQL 工具和生态系统。
  2. Hypertables
    • TimescaleDB 引入了 hypertables,这是一种自动分区的表结构,专门为时序数据优化。
    • 数据按时间维度自动分区,支持高效的数据插入和查询。
  3. 时间序列优化
    • 支持高效的时间范围查询、降采样(downsampling)、数据压缩和连续聚合(continuous aggregates)。
    • 提供专门的时序函数和窗口函数。
  4. 可扩展性
    • 支持分布式架构(TimescaleDB 2.0+),可以水平扩展以处理大规模数据。
  5. 开源
    • TimescaleDB 是开源的,社区版免费使用,企业版提供额外的高级功能。

而这个东西也提供了一个 django 的粗件:https://pypi.com.cn/project/django-timescaledb/

网上搜一下相关的文章都简单的 1b,但是如果简单的按照文章中的内容操作,很可能直接就卡在数据库连接上了。

建议在开始配置之前不要安装任何的 postgresql 数据库以及客户端,直接参考官方文档:https://docs.timescale.com/self-hosted/latest/install/installation-macos/#install-and-configure-timescaledb-on-postgresql

在开发电脑以及服务器上都要安装相关的postgresql timescaledb插件。否则就会提示找不到相关的组件。

postgres=# CREATE EXTENSION IF NOT EXISTS timescaledb;
ERROR:  could not open extension control file "/usr/share/postgresql/12/extension/timescaledb.control": No such file or directory

同样在开发电脑上也要安装相关的组件,如果安装了其他版本的postgresql删除掉旧版本,或者直接全新安装。

执行下面的命令创建用户以及数据库:

sudo -i -u postgres
psql
CREATE USER data_db WITH PASSWORD '1qaz@WSX';
CREATE DATABASE data_db OWNER data_db;

修改配置文件,让 data_db 用于允许远程登录:

vim /etc/postgresql/12/main/pg_hba.conf
host    data_db             data_db             0.0.0.0/0   md5
root@2gcc5hbhemlhjejc:~# sudo service postgresql restart

创建扩展,并且查看加载情况:

postgres=# CREATE EXTENSION IF NOT EXISTS timescaledb;
CREATE EXTENSION
postgres=# \dx
                                                List of installed extensions
    Name     | Version |   Schema   |                                      Description                                      
-------------+---------+------------+---------------------------------------------------------------------------------------
 plpgsql     | 1.0     | pg_catalog | PL/pgSQL procedural language
 timescaledb | 2.17.2  | public     | Enables scalable inserts and complex queries for time-series data (Community Edition)
(2 rows)

如果看到timescaledb 就表示成功了:

安装相关的组件之后,如果要在 django 中使用,还需要安装psycopg2,不建议安装psycopg2-binary因为安装这个东西,在 mac 下同样会爆上面的错误。psycopg2  需要编译安装,安装过程可能会提示找不到 ssl 库,通过brew info openssl 定位 ssl 库位置。

brew info openssl
==> openssl@3: stable 3.4.0 (bottled)
Cryptography and SSL/TLS Toolkit
https://openssl-library.org
Installed
/opt/homebrew/Cellar/openssl@3/3.4.0 (7,236 files, 33.4MB) *
  Poured from bottle using the formulae.brew.sh API on 2024-11-22 at 09:36:14
From: https://github.com/Homebrew/homebrew-core/blob/HEAD/Formula/o/openssl@3.rb
License: Apache-2.0
==> Dependencies
Required: ca-certificates ✔
==> Caveats
A CA file has been bootstrapped using certificates from the system
keychain. To add additional certificates, place .pem files in
  /opt/homebrew/etc/openssl@3/certs

and run
  /opt/homebrew/opt/openssl@3/bin/c_rehash
==> Analytics
install: 318,996 (30 days), 1,205,908 (90 days), 4,903,860 (365 days)
install-on-request: 39,060 (30 days), 168,471 (90 days), 637,515 (365 days)
build-error: 6,326 (30 days)

修改.bash_prfile文件添加上述位置:

export LDFLAGS="-L/opt/homebrew/Cellar/openssl@3/3.4.0/lib"
export CPPFLAGS="-I/opt/homebrew/Cellar/openssl@3/3.4.0/include"

再次执行source ~/.bash_profile & pip install psycopg2即可。

剩下的就可以按照https://pypi.com.cn/project/django-timescaledb/里面的步骤实施了。

相关示例工程:https://gitcode.com/gh_mirrors/dj/django-timescaledb

注意连接引擎为timescale.db.backends.postgresql:

'ENGINE': 'timescale.db.backends.postgresql',
        'NAME': 'data_db',
        'USER': 'data_db',
        'PASSWORD': '1qaz@WSX',
        'HOST': '113.125.1.1',
        'PORT': '5432',
    },

 

 

  •