立即开始增强您的 PostgreSQL。
作者:Timescale 团队
时间序列数据库解决了一个越来越多的公司意识到的挑战:衡量系统如何变化。
为了有效地分析时间序列数据,您必须高速和大规模地收集数据。如果您每秒摄取数百、数千甚至更多数据点,它们会很快堆积起来。
最终,您需要一个能够处理规模、具有出色可用性和可靠性的数据库。对于实际的时间序列工作负载,通用数据库不太可能满足所有三个标准。您需要一个专门构建的时间序列数据库。
在这篇文章中,我们将比较一些最流行的时间序列数据库,并回顾在选择数据库时要考虑的关键特性和标准。
根据您首选的优化点,数据库可以通过多种方式成为最佳选择。在本节中,我们概述了时间序列数据库最关键的方面:可扩展性、可维护性、可靠性和查询语言。
**可扩展性**。时间序列数据通常增长迅速,数据库必须能够容纳大量数据而不会降低性能。
**可维护性**。维护数据库涉及轻松执行备份、更新和其他例行任务,以确保数据的完整性和可用性。
这很重要,因为它会影响数据库的长期可用性和可靠性,从而减少管理任务所需的时间和精力。
**可靠性**。确保数据的准确性和可用性至关重要,尤其是在高需求或故障期间。由于时间序列数据库通常支持业务决策,因此它们需要一致且可靠的数据访问。
**查询语言**。数据库的查询语言决定了用户如何与数据交互,从而影响易用性和学习曲线。像 SQL 这样的熟悉的查询语言可以减少培训时间,并更容易与现有工具集成,从而提高生产力。
| 数据库模型 | 可扩展性 | 部署 | 查询语言 | 定价模型 |
TimescaleDB | 关系数据库 | 垂直可扩展,具有自动分区、列式压缩、优化查询和自动更新的物化视图 | 自托管或托管云服务 | SQL | TimescaleDB 开源可以自托管并且是免费的
Timescale for AWS 采用按需付费模式 |
InfluxDB | 自定义、非关系型 NoSQL、列式数据库 | 水平可扩展 | 自托管或托管云服务 | SQL、InfluxQL、Flux | InfluxDB 开源版可免费使用,InfluxDB Cloud Serverless 是一项采用按需付费定价模型的托管服务。
InfluxDB Cloud Dedicated 和 InfluxDB Clustered 适用于大容量生产工作负载,成本取决于存储、CPU 和 RAM |
Prometheus | 基于拉取的模型,从目标抓取指标 | 垂直可扩展或通过联合可扩展 | 作为单个二进制文件部署在服务器上或 Kubernetes 等容器平台上 | PromQL | 开源:无相关许可证费用 |
Kdb+ | 具有自定义数据模型的列式数据库 | 水平可扩展,支持多节点和多线程 | 本地部署、云端或混合部署 | Q 语言 | 非商业用途的免费 32 位版本
对于商业部署,定价取决于部署模型和核心数量 |
Graphite | Whisper(基于文件的时间序列)数据库格式 | 水平可扩展,支持复制和集群 | 本地部署或云端部署 | GQL | 开源:无相关许可证费用 |
ClickHouse | 列式数据库 | 水平可扩展 | 本地部署或云端部署
也作为托管服务提供 | 基于 SQL 的声明式查询语言,与 ANSI SQL 标准基本相同 | ClickHouse 是开源的,没有相关的许可证费用
ClickHouse Cloud 采用按需付费的定价模式 |
MongoDB | 具有类似 JSON 文档模型的 No-SQL 数据库 | 水平可扩展 - 支持自动负载均衡、数据分片和复制 | 自托管或托管云服务 | MQL(MongoDB 查询语言) | MongoDB 社区版是开源的,可以免费使用
MongoDB 企业版:定价取决于您选择的功能
MongoDB Atlas 采用按需付费的定价模式 |
InfluxDB 是一款流行的时间序列数据库,以其高性能和可扩展性而闻名。它专为时间序列数据构建,提供无模式数据模型,可以轻松添加新字段,而无需修改模式或迁移数据。根据您使用的版本,它使用称为 InfluxQL 和 Flux 的专用查询语言(您也可以依赖标准 SQL)。
InfluxDB 可以处理指标数据的高吞吐量摄取,并广泛用于监控和警报、物联网数据存储和分析以及实时分析。它还适用于涉及分析和跟踪随时间推移的数据点的应用程序,以及用户需要在摄取后快速查询数据以进行实时分析的场景。
该数据库使用列式存储数据,使用 Apache Arrow 进行内存数据表示,并使用 Parquet 进行文件存储,这允许更快的查询和更好的数据压缩。此外,它允许您定义保留策略,以确定在自动删除数据之前存储数据的时间。
InfluxDB 的独特之处在于其架构,该架构包含四个关键组件(数据摄取、数据压缩、数据查询和垃圾回收),每个组件独立运行。通过将查询和摄取组件分离,可以根据工作负载需求轻松地独立扩展每个组件。InfluxDB 的集群功能还支持水平扩展,使其能够随着数据量和工作负载需求的增长而扩展。
Prometheus 是一款以可靠性和可扩展性著称的监控和警报工具包。它针对收集和查询与系统和应用程序指标相关的时间序列数据进行了优化。Prometheus 使用基于拉取的模型,服务器定期从目标收集指标,并将数据存储在自定义时间序列数据库中,该数据库针对低资源使用率和快速查询进行了优化。这种模型使其非常适合监控动态环境,因为它支持自动发现并允许监控新实例。
Prometheus 具有可扩展的模块化架构,包含服务发现机制和导出器等组件。但是,由于它不是分布式系统,因此无法水平扩展,并且缺少内置集群,尽管它支持联合,这允许多台服务器共享数据。
其查询语言 PromQL 允许用户对指标数据执行强大的查询和聚合。Prometheus 广泛用于云原生环境,并以其简单性、灵活性和稳健性而闻名。它与 Kubernetes 等容器编排平台无缝集成,使其成为监控微服务架构(包括 Docker 和 Kubernetes)的热门选择。其他用例包括应用程序性能监控和异常检测。
但是,请注意,Prometheus 不是通用的时间序列数据库,因此它可能不适合长期数据存储。
kdb+ 是一种高性能列式数据库,针对处理金融时间序列数据(如股票市场交易)进行了优化,尽管它也适用于其他类型的时间序列数据,包括物联网和传感器数据。kdb+ 由 Kx Systems 开发,以其速度、可扩展性和高级分析功能而闻名,使其适用于需要对海量数据进行高速查询和分析的应用程序。它使用一种称为 q 的专有语言来查询和操作数据,该语言专为处理金融领域的时间序列数据而量身定制。
该数据库在需要低延迟访问大量时间序列数据的场景中表现出色,例如算法交易、风险管理和逐笔数据分析。其架构允许磁盘和内存存储,并且可以跨多台机器水平扩展。其高效的数据存储和处理使其成为寻求从实时市场数据中获取洞察力的金融机构和贸易公司的首选。
TimescaleDB 是一款强大的开源时间序列数据库,构建于 PostgreSQL 之上。它提供可扩展性、可靠性和高级时间序列功能。它使用块和超表自动对时间序列数据进行分区,从而提高查询性能并简化数据管理。
Timescale 的压缩通过减少时间序列数据的占用空间来显着节省存储空间,通常可实现高达 90% 的压缩率。这不仅降低了存储成本,还提高了查询性能,因为需要从磁盘读取的数据更少,从而加快了分析速度并提高了资源利用率。
借助 TimescaleDB,您可以利用 SQL 的熟悉程度和强大功能来查询和操作时间序列数据,从而实现复杂的分析和洞察。它还为不同的场景提供了专门的 SQL 运算符和函数,例如 first、last 和 time_bucket,它们简化了聚合和查询。
Timescale 的连续聚合通过定期预先计算和物化时间序列查询的结果来优化查询性能。这允许即时访问最新的摘要和洞察,减少数据库的计算开销,并确保更快、更高效的数据分析。
它与 PostgreSQL 的集成还确保了数据持久性、ACID 合规性,以及与各种工具和应用程序的兼容性。无论是用于监控、物联网、财务分析还是其他时间序列用例,TimescaleDB 都提供了一个强大的解决方案,用于大规模存储和分析时间序列数据。
所有这些功能使 Timescale 成为存储和分析指标的理想选择,例如应用程序日志、传感器数据、服务器性能指标和财务数据(如交易量、汇率和股票价格)。
Graphite 是一款开源监控工具和时间序列数据库,用于收集、存储和可视化指标数据。它使用 Whisper 格式,该格式通过根据保留策略聚合和过期数据来有效地管理和存储数据,并且使用简单但功能强大的查询语言,可以轻松摄取和分析时间序列数据。它还支持一系列用于查询、聚合和转换时间序列数据的函数,允许用户创建自定义仪表板和图形。
该数据库的架构由许多组件组成,包括 Carbon(从各种来源接收指标、将它们缓存在内存中并将它们存储在 Whisper 中)、Whisper(管理数据聚合和保留)和 Graphite-web(一个 Web 应用程序,提供用于可视化和查询时间序列数据的用户界面)。
Graphite 通常用于监控从小型部署到大型系统的环境中的基础设施和应用程序指标。它提供可自定义的仪表板和图形,用于可视化指标趋势和异常,帮助用户深入了解系统的性能和运行状况。虽然与其他一些解决方案相比,Graphite 在可扩展性方面存在局限性,但其简单性和多功能性使其成为许多监控用例的热门选择。
ClickHouse 是一款开源列式数据库管理系统,针对分析工作负载进行了优化。它擅长对大量数据执行快速分析查询,并以面向列的格式存储数据,这可以实现高效的压缩和分析查询处理(因为在执行查询时仅读取必要的列)。
该数据库对 SQL 的原生支持使您能够利用现有的 SQL 技能和工具来查询和分析数据。它还具有高度可扩展性,可以处理分布式集群中的 PB 级数据。此外,它使用 MergeTree 作为其主要表引擎,该引擎支持索引、复制和分区。它还支持物化视图,这可以提高查询性能。
ClickHouse 通常用于临时分析、实时报告和数据仓库应用程序,在这些应用程序中,快速的查询性能至关重要。尽管它没有针对处理时间序列数据进行优化,但它仍然可以有效地存储和分析时间序列数据。虽然它可以在摄取后快速查询时间序列数据,但在数据应小批量摄取的高写入场景中,它可能会遇到困难。
MongoDB 是一款 NoSQL 数据库,以其易用性、可扩展性和灵活性而闻名。它以类似 JSON 的文档存储数据,使其适用于各种用例,包括实时分析、内容管理和移动应用程序。尽管 MongoDB 是一个通用的 NoSQL 数据库,但它可以有效地处理和存储时间序列数据。其灵活的数据模型可以轻松适应不断发展的时间序列数据结构。
MongoDB 的水平可扩展性使其能够轻松处理大量数据和高吞吐量工作负载。其查询语言 MongoDB 查询语言 (MQL) 支持用于查询和操作文档数据的丰富操作集。
时间序列数据库可以提供通用功能以及时间序列功能(例如 TimescaleDB),或者专注于以支持更通用的工作负载为代价来提供时间序列功能(例如 InfluxDB)。时间序列数据并非孤立存在(您经常希望将其与其他类型的数据相关联),因此无论采用哪种方法,您都需要一种关联通用数据和时间序列数据的方法。
在下一节中,您将看到时间序列数据库中可以预期的时间序列功能示例。为了向您展示这两个极端,您将看到这些功能在两个完全不同的时间序列数据库中的外观:InfluxDB(使用其自己的“Flux”查询语言)和 TimescaleDB(它扩展了 PostgreSQL 并提供完整的 SQL 支持)。
基于时间的聚合是时间序列数据库的必备功能。没有它,您将无法创建对分析时间序列数据至关重要的小时、日、周和月时间聚合(以及介于两者之间的许多排列)。让我们看看在 InfluxDB 和 TimescaleDB 中基于时间的聚合查询是什么样的
InfluxDB
from(bucket: "crypto")
|> range(start: 0)
|> aggregateWindow(every: 1d, fn: count, createEmpty: false)
|> sort(columns: ["_time", ], desc:true)
|> limit(n:10)
使用 InfluxDB 的查询语言 Flux,您必须为查询定义一个时间范围。您可以使用 0
作为 start
参数来解决此问题 - 以防您不想定义特定的时间范围。然后,您可以使用 aggregateWindow()
函数创建任意时间“存储桶”。
TimescaleDB
SELECT
time_bucket('1 day', time) AS bucket,
count(*) row_count
FROM crypto_ticks
GROUP BY bucket
ORDER BY bucket DESC
LIMIT 10
bucket |row_count|
-------------------+---------+
2022-09-09 02:00:00| 52277|
2022-09-08 02:00:00| 128657|
2022-09-07 02:00:00| 134849|
2022-09-06 02:00:00| 132837|
2022-09-05 02:00:00| 126254|
在 TimescaleDB 中,您可以使用 time_bucket() 函数创建任意时间存储桶。此外,所有其他可用的 PostgreSQL 函数(如 count(*)
)的工作方式与在常规 PostgreSQL 中相同。
在处理时间序列数据时,您必须创建许多基于时间的聚合,因此请确保您选择的数据库提供了一个简单直观的界面来创建时间存储桶。
时间序列数据通常以非常高的分辨率摄取(例如,每秒数千个数据点)。为了更容易地分析时间序列,用户经常对他们的数据进行降采样(例如,他们将每秒数千个数据点转换为一个)。这种技术不仅节省了存储成本,因为需要存储较低分辨率的数据,而且还更容易创建可视化并识别数据趋势。
降采样通常重复且连续地进行,这意味着,例如,如果您每秒插入多个新行,数据库会自动将传入数据汇总到更大的存储桶中。数据库会自动实时处理原始数据,而不是您自己聚合。让我们以OHLC 示例为例,看看 InfluxDB 和 TimescaleDB 如何处理降采样。
InfluxDB
close=from(bucket: "crypto")
|> range(start: -30d)
|> group(columns:["symbol"])
|> filter(fn: (r) => r["_measurement"] == "ohlc")
|> window(every: 1h)
|> reduce(fn: (r, accumulator) => ({
indexLow:
if (r._field=="low") then
accumulator.indexLow+1
else
accumulator.indexLow,
indexOpen:
if (r._field=="open") then
accumulator.indexOpen+1
else
accumulator.indexOpen,
open:
if (r._field=="open") then
if (accumulator.indexOpen==0) then
r._value
else
accumulator.open
else
accumulator.open
,
high:
if (r._field=="high") then
if(r._value>accumulator.high ) then
r._value
else
accumulator.high
else
accumulator.high
,
low:
if (r._field=="low") then
if(r._value<accumulator.low or accumulator.indexLow==0.0) then
r._value
else
accumulator.low
else
accumulator.low,
close:
if (r._field=="close") then
r._value
else
accumulator.close,
volume:
if (r._field=="volume") then
r._value+accumulator.volume
else
accumulator.volume
}),
identity: {indexLow:0,indexOpen:0,open: 0.0,high: 0.0,low: 0.0,close: 0.0,volume: 0.0})
|> drop(columns: ["indexOpen","indexLow"])
|> group(columns:["pair"])
|> yield(name: "candle")
InfluxDB 的 Flux 为编写简单查询提供了一种便捷的方式,但如果您想创建一些更复杂的查询,例如从原始金融报价数据创建 OHLC 聚合,最终的查询可能会变得相当长,如您所见。
TimescaleDB
CREATE MATERIALIZED VIEW hourly_buckets
WITH (timescaledb.continuous)
AS
SELECT
time_bucket('1 hour', time) AS bucket,
symbol,
first(price, time) AS open,
max(price) AS high,
min(price) AS low,
last(price, time) AS close
FROM crypto_ticks
GROUP BY bucket, symbol;
SELECT * FROM hourly_buckets;
bucket |symbol |open |high |low |close |
-------------------+--------+-------+-------+-------+-------+
2022-02-08 22:00:00|ADA/USD | 1.166| 1.17| 1.157| 1.168|
2022-02-08 22:00:00|ATOM/USD| 30.44| 30.63| 30.3| 30.51|
2022-02-08 22:00:00|AVAX/USD| 87.85| 88.0| 86.72| 87.06|
2022-02-08 22:00:00|BNB/USD | 413.5| 416.5| 410.3| 410.3|
2022-02-08 22:00:00|BTC/USD |44192.4|44354.0|43938.6|44185.2|
如果您熟悉 PostgreSQL 语法,您会发现 TimescaleDB 方法与PostgreSQL 物化视图非常相似。但是,底层机制有所不同,它通过随着时间的推移自动存储预聚合桶、在原始数据更改时维护聚合,甚至返回实时数据,为时间序列数据提供了更好的开发者体验。
您可能希望构建可视化仪表板来显示时间序列趋势,甚至接近实时数据。要创建趋势图,您可以使用前面提到的降采样方法。但对于实时数据,您可能希望看到更精细和最近的数据,例如过去五分钟内的所有数据点。让我们看看如何在 InfluxDB 和 TimescaleDB 中发出这个简单请求。
InfluxDB
from(bucket: "crypto")
|> range(start: -5m)
|> filter(fn: (r) => r.symbol == "BTC/USD")
|> sort(columns: ["_time", ], desc:true)
|> limit(n:5)
|> keep(columns: ["_time", "_value"])
在 Flux 中,您可以使用start: -5m
指定一个相对于现在的的时间范围,它将返回过去五分钟内“BTC/USD”符号的所有数据。
TimescaleDB
SELECT
time,
price
FROM crypto_ticks
WHERE
"time" > NOW() - INTERVAL '5 minutes' AND
symbol = 'BTC/USD'
ORDER BY time DESC
LIMIT 5;
time |price |
-------------------+-------+
2022-09-12 15:24:07|22346.7|
2022-09-12 15:24:03|22346.3|
2022-09-12 15:23:50|22346.7|
2022-09-12 15:23:45|22355.9|
2022-09-12 15:23:40|22358.1|
在 TimescaleDB 示例中,您可以看到一个熟悉的 SQL 示例(如果您已经了解 SQL),它在WHERE
子句中使用符号过滤器和相对时间过滤器,并使用NOW()
PostgreSQL 函数。但是,在底层,此查询的执行方式与常规 PostgreSQL 不同:当您将时间序列数据插入数据库时,TimescaleDB 会根据时间列自动对您的表进行分区。
然后,当您进行包含时间过滤器的查询时(如此示例中所示),TimescaleDB 可以排除整个块进行扫描,这使得查询最近的数据速度极快,即使您的数据库中存储了数十亿行数据。
如果您还想分析更长的时间范围,例如过去一年的所有数据,该怎么办?也许您想查看过去一年中某个股票或加密货币符号的最高价格。
InfluxDB
from(bucket: "crypto")
|> range(start: -1y)
|> group(columns: ["code"])
|> max()
|> group()
|> sort(columns: ["_value"], desc: true)
此示例表明 Flux 按照您描述的相同顺序执行您的查询。
TimescaleDB
SELECT
symbol,
MAX(price) AS max_price
FROM crypto_ticks
WHERE
"time" >= NOW() - INTERVAL '1 year'
GROUP BY symbol
ORDER BY max_price DESC;
symbol |max_price |
---------+----------+
BTC/USD | 48210.1|
WBTC/USD | 48169.56|
ETH/USD | 3579.38|
BNB/USD | 460.0|
SOL/USD | 143.55|
像这样以较大的时间窗口作为过滤器的分析查询不是典型的时间序列查询,但您可能希望不时地运行这些查询。TimescaleDB 提供了两个可以显着加快这些查询速度的功能:原生压缩(节省空间并将数据转换为列式)和连续聚合(自动维护可以与原始读数分开保留的物化聚合数据)。总之,这些功能可以对应用程序的性能产生巨大影响。
有时我们只谈论时间序列数据,而没有提及现实世界项目在其数据基础架构中的所有其他数据。但现实情况是,时间序列数据始终与非时间序列(业务)数据相关联。如果您打算一起分析您的时间序列数据和业务数据,那么您选择的数据库需要能够JOIN
它们并快速、轻松地处理它们。在以下示例中,您可以看到如何在 InfluxDB 和 TimescaleDB 中连接两个表。
InfluxDB
crypto_assets = from(bucket: "crypto-assets")
|> range(start: -1mo)
|> filter(fn: (r) => r._measurement == "assets" and r._field == "symbol")
crypto_ticks = from(bucket: "crypto-ticks")
|> range(start: -1m)
|> filter(fn: (r) => r._measurement == "ticks" and r._field == "price")
join(
tables: {assets:crypto_assets, ticks:crypto_ticks},
on: [symbol, ],
)
在这方面,InfluxDB 和 TimescaleDB 之间的最大区别在于 InfluxDB 只能存储带时间戳的数据,而 TimescaleDB 可以将带时间戳和不带时间戳的数据存储在一起。因此,在 InfluxDB 中,您只能将时间序列数据与其他时间序列数据连接,而不能与关系数据连接。
TimescaleDB
SELECT crypto_assets.name, bucket, close, high, low, open
FROM one_day_candle
INNER JOIN crypto_assets ON crypto_assets.symbol = one_day_candle.symbol
WHERE
bucket > NOW() - INTERVAL '1 month' AND
one_day_candle.symbol = 'BTC/USD'
ORDER BY bucket;
name |bucket |close |high |low |open |
-----------+-------------------+-------+-------+-------+-------+
Bitcoin USD|2022-08-13 02:00:00|24460.6|24889.5|24312.3|24402.2|
Bitcoin USD|2022-08-14 02:00:00|24312.4|25034.2|24160.4|24455.2|
Bitcoin USD|2022-08-15 02:00:00|24092.8|25210.9|23798.7|24316.2|
Bitcoin USD|2022-08-16 02:00:00|23867.7|24247.5|23692.0|24103.2|
Bitcoin USD|2022-08-17 02:00:00|23340.1|24430.1|23184.4|23857.3|
在 TimescaleDB 中,您可以使用 PostgreSQL 的JOIN
连接同一数据库中的任何两个表,从而使您能够将非时间序列数据存储在时间序列数据旁边。如果没有此功能,您可能很难将来自多个来源的数据整合在一起。
有趣的事实: 创建 TimescaleDB 的原因之一是创始人难以找到能够对时间序列数据进行简单 JOIN 操作的数据库。
在观察数千家公司如何处理时间序列数据时,我们发现它会随着时间的推移而变得不那么有价值。这意味着用户通常希望在一段时间后存档甚至删除旧数据,以节省存储成本。
InfluxDB
在 InfluxDB 中,您可以在 UI 上为每个桶更改数据保留设置。在旧版本中,您也可以通过这种方式添加数据保留策略
CREATE RETENTION POLICY "one_year" ON "some_dataset" DURATION 1y REPLICATION 1 DEFAULT
TimescaleDB
–Compression:
ALTER TABLE example SET (
timescaledb.compress,
timescaledb.compress_segmentby = symbol
);
SELECT add_compression_policy(‘crypto_ticks’, INTERVAL '2 weeks');
–Data retention:
SELECT add_retention_policy(‘crypto_ticks’,, INTERVAL '1 year');
使用 TimescaleDB,您可以同时设置压缩策略(以节省存储需求但保留数据以供查询)和数据保留策略(在定义的时间段后删除数据)。如果没有数据库中有关数据压缩和数据保留的基本工具,您将需要手动实施和维护这些自动化。
时间序列数据提供的价值是毋庸置疑的。这就像观看整部电影,而不仅仅是最后一帧。但是,您确实需要一个合适的数据库来为您播放这部电影。我希望本文能为您提供选择最适合您的团队和项目的时间序列数据库所需的工具。
查看我们的文档以获取时间序列实践教程
如果您对 Timescale 感兴趣,您可以免费试用或查看我们的GitHub、社区 Slack或论坛并加入与数千名 Timescale 用户的讨论。