Skip to main content

TimescaleDB

TimescaleDB 是一个开源的时间序列数据库,它建立在 PostgresSQL 数据库之上。它可以高效地存储和查询时间序列数据,支持SQL查询语言。TimescaleDB 的一些关键特性包括:

  • 自动分片技术 - 它会根据时间自动将数据分到不同的分片中,减少每个分片的数据量,提高查询效率。
  • 压缩 - 它对老旧的数据进行压缩,减少存储空间。
  • 索引 - 它会对时间列建立索引,加速时间范围查询。
  • 并行处理 - 它支持跨分片进行并行查询,充分利用多核CPU。
  • SQL支持 - 用户可以用标准SQL进行查询,无需学习新的查询语言。
  • 与 PostgresSQL 完全兼容 - 可以利用 PostgresSQL 的所有特性和生态圈。
  • TimescaleDB 广泛用于 IoT、监控、运维等需要处理大规模时间序列数据的场景。它的设计目标是使时间序列数据的存储和查询变得简单快速。

现实应用场景

Fiapp 消费者版本的 k 线数据一直存储在 postgres 上,但是因为不是专业序列优化的原因,一直以来读取数据都存在延迟性能问题,所以我们需要寻找一种数据库或者扩展方案来解决这个问题。
在经过一段时间的调研后,我们发现了 timescaledb 这个项目,它是一个开源的时间序列数据库,它建立在 postgresql 数据库之上,且完美兼容 postgresql,实际应用过程中,不需要对原表和数据进行任何迁移和修改,只需要启用 timescaledb 扩展,然后一行命令创建超级表 hyper table,就可以实现对原表的无缝升级,同时该命令也可以对 hyper table 进行自动分区,从而实现对数据的分片存储,提高查询性能。


安装

Fiapp 服务器基于 debian,所以我们需要查阅 timescaledb 的官方文档,找到对应的安装方式,官方文档地址:https://docs.timescale.com/self-hosted/latest/install/installation-linux/

虽然官方文档建议全新安装,卸载之前的 postgresql,但是我们的服务器上已经安装了 postgresql,所以这是不现实的。不过 timescaledb 只是一个扩展插件,所以我们只需要在服务器上安装好对应 postgresql 版本的 timescaledb,即可直接启用使用。

现在先假定我们已经安装好了 postgresql,接下来我们需要安装 timescaledb 的 apt 源,然后安装 timescaledb 的包,具体步骤如下:

# 安装 timescaledb 的 apt 源
echo "deb https://packagecloud.io/timescale/timescaledb/debian/ $(lsb_release -c -s) main" | sudo tee /etc/apt/sources.list.d/timescaledb.list

# 安装 timescaledb 的 GPG 密钥
wget --quiet -O - https://packagecloud.io/timescale/timescaledb/gpgkey | sudo gpg --dearmor -o /etc/apt/trusted.gpg.d/timescaledb.gpg

# 更新 apt 源
apt update

# 安装 timescaledb
apt install timescaledb-2-postgresql-[version]

需要注意的是,最后一步 apt install timescaledb-2-postgresql-[version] 中的 [version] 需要替换成对应的 postgresql 版本,例如我们服务器上使用的是最新版本 15,那么就需要执行 apt install timescaledb-2-postgresql-15

配置 timescaledb

# 运行以下命令以启动 timescaledb 的配置向导
sudo timescaledb-tune

# 首先,系统会要求您确认 PostgresSQL 配置文件的路径等:
Output
Using postgresql.conf at this path:
/etc/postgresql/15/main/postgresql.conf

Is this correct? [(y)es/(n)o]:

一路键入 y 即可安装,当然你也可以根据输出内容进行配置的微调。

启用 timescaledb

# 连接到 postgresql
sudo -u postgres psql

# 现在创建一个新数据库并连接到它。timeseries :
CREATE DATABASE timeseries;
\c timeseries

# 现在,我们将启用 timescaledb 扩展:
CREATE EXTENSION IF NOT EXISTS timescaledb CASCADE;

您将看到以下类似的输出:

Output
WARNING:
WELCOME TO
_____ _ _ ____________
|_ _(_) | | | _ \ ___ \
| | _ _ __ ___ ___ ___ ___ __ _| | ___| | | | |_/ /
| | | | _ ` _ \ / _ \/ __|/ __/ _` | |/ _ \ | | | ___ \
| | | | | | | | | __/\__ \ (_| (_| | | __/ |/ /| |_/ /
|_| |_|_| |_| |_|\___||___/\___\__,_|_|\___|___/ \____/
Running version 1.7.1
For more information on TimescaleDB, please visit the following links:

1. Getting started: https://docs.timescale.com/getting-started
2. API reference documentation: https://docs.timescale.com/api
3. How TimescaleDB is designed: https://docs.timescale.com/introduction/architecture

Note: TimescaleDB collects anonymous reports to better understand and assist our users.
For more information and how to disable, please see our docs https://docs.timescaledb.com/using-timescaledb/telemetry.

CREATE EXTENSION

这时候,你的 timescaledb 就已经安装好了,接下来我们就可以开始使用了。

使用 timescaledb

在 timescaledb 中,我们需要对原有表创建一个超级表,这个超级表是附加在原表之上的,你无需对任何表、任何数据库改动任何结构,它只是一个表概念,虽然它实际存在,但是你无需关心它。
超级表的作用就是 timescaledb 会自动对超级表进行分区,从而实现数据的分片存储,提高查询性能。

# 创建原始数据表
CREATE TABLE conditions (
time TIMESTAMP WITH TIME ZONE NOT NULL,
device_id TEXT,
temperature NUMERIC,
humidity NUMERIC
);

此命令将创建一个包含 conditions 四列的表。第一列将存储时间戳,其中包括时区,不能为空。接下来,您将使用时间列将表转换为按时间分区的超表:

SELECT create_hypertable('conditions', 'time');

此命令调用该 create_hypertable() 函数,该函数从 PostgresSQL 表创建 TimescaleDB 超表,替换后者。 这时候超表就创建完成了。

你无需关注任何超表的内容,因为他们都是自动同步扩展的,你只需要关注原表的内容即可。

现实案例

Fiapp 存在一个已有的 k 线表,包含如下结构:

    Column    |           Type           | Collation | Nullable |      Default      
--------------+--------------------------+-----------+----------+-------------------
id | uuid | | not null | gen_random_uuid()
created_at | timestamp with time zone | | | now()
open_at | timestamp with time zone | | not null |
open_bid | numeric | | not null |
highest_bid | numeric | | not null |
lowest_bid | numeric | | not null |
close_bid | numeric | | not null |
volume_bid | numeric | | |
timeframe_id | uuid | | not null |
coin_id | uuid | | not null |

这时候如果直接运行命令,

SELECT create_hypertable('kline', 'open_at');

会提示原表已经有数据了,那么我们只需要添加一个参数即可无缝迁移:

SELECT create_hypertable('kline', 'open_at', migrate_data := true);

不过这时候又会提示,open_at 不是唯一主键,因为我们的表设计,是无法只针对 open_at 设置一个唯一主键的,我们目前的主键已经有 id 了,所以我们只需要确保 open_at 在唯一主键中即可,所以我们需要在原有的 id 主键上添加 open_at,这样就可以了。

再次运行命令,即可成功迁移和分区,此时我们的所有 api 依然原样不动,读取数据的方式也没有任何修改,数据库就自动提速,所以这个方案成本极低,兼容性极强。

大家可以在 https://fiapp.pro/chart/ADAUSDT 感受 k 线加载速度,同时也点击左侧栏的各种币来感受速度提升。

@2023 HeroUI & Singularity Robotics PTE. LTD. All rights reserved.