您现在的位置是:首页 > 编程 > 

PostgreSQL亿级行数据处理

2025-07-23 01:57:50
PostgreSQL亿级行数据处理 使用Timescale压缩和分块跳过索引,实现PostgreSQL处理数十亿行数据的方法。 译自 Handling Billi of Rows in PostgreSQL,作者 Semab Tariq。在PostgreSQL(或任何关系数据库)中处理数十亿行的表可能具有挑战性,因为数据复杂性高、存储空间占用量大以及更复杂或分析查询的性能问题。通过在Time

PostgreSQL亿级行数据处理

使用Timescale压缩和分块跳过索引,实现PostgreSQL处理数十亿行数据的方法。

译自 Handling Billi of Rows in PostgreSQL,作者 Semab Tariq。

在PostgreSQL(或任何关系数据库)中处理数十亿行的表可能具有挑战性,因为数据复杂性高、存储空间占用量大以及更复杂或分析查询的性能问题。

通过在Timescale中启用列存储(压缩数据)并使用Timescale的块跳过索引,可以解决所有这些挑战。Timescale构建在PostgreSQL之上,旨在简化PostgreSQL的扩展。这篇文章展示了如何使用Timescale的列存储和块跳过索引功能来减小表大小并加快搜索速度。

以下是我们将遵循的方法。首先,我们将数据插入到未压缩表中以获取初始大小和查询速度。然后,我们将这些结果与压缩表进行比较。让我们开始吧。

我们将在Timescale Cloud上使用PostgreSQL——这是一种完全托管的数据库服务,旨在高效处理时间序列数据。它提供PostgreSQL的熟悉功能,同时添加强大的时间序列功能。

功能包括自动缩放、高可用性和各种性能优化,使开发人员更容易存储、管理和查询大量时间序列数据,而无需担心基础设施管理。

以下是这些测试中使用的实例详细信息:

  • 实例类型:时间序列
  • CPU:4核
  • RAM:16 GB
未压缩表基准测试

首先,我们在时间序列数据库中创建一个名为sensor_uncompressed的PostgreSQL堆表,并将十亿行数据导入其中。之后,我们检查其统计信息,包括表大小和SELECT查询性能。

步骤1:创建一个表格

代码语言:javascript代码运行次数:0运行复制
CREATE TABLE sensors_uncompressed (
    sensor_id ITEGER,
    ts TIMESTAMPTZ OT ULL,
    value REAL
);

步骤2:创建索引

代码语言:javascript代码运行次数:0运行复制
CREATE IDEX sensors_ts_idx_uncompressed O sensors_uncompressed (sensor_id, ts DESC);

步骤:摄取数据

数据集被放置在AWS S存储桶中,因此我们使用了timescaledb-parallel-copy工具将数据导入表中。timescaledb-parallel-copy是一个用于并行化PostgreSQL内置COPY功能的命令行程序,用于批量插入数据到TimescaleDB。

curl .csv.gz | gunzip | timescaledb-parallel-copy -batch-size 5000 -connection $DATABASE_URI -table sensors_uncompressed -workers 4 -split '\t'

以下是成功将十亿行数据导入PostgreSQL堆表后的部分统计信息。

  • 数据导入时间:49分钟12秒
  • 包括索引和数据的总表大小:101 GB

步骤4:运行聚合查询

目标是通过对压缩表和未压缩表运行各种缩放聚合查询来比较查询执行时间,观察压缩表相对于未压缩表的性能。

Query 1

代码语言:javascript代码运行次数:0运行复制
SELECT * FROM sensors_uncompressed
WHERE sensor_id = 0
AD ts >= '202-12-21 07:15:00'::timestamp
AD ts <= '202-12-21 07:16:00'::timestamp;
-- Execution Time: 8 ms

Query 2

代码语言:javascript代码运行次数:0运行复制
SELECT sensor_id, DATE_TRUC('day', ts) AS day, MAX(value) AS max_value, MI(value) AS min_value
FROM sensors_uncompressed
WHERE ts >= DATE '202-12-21' AD ts < DATE '202-12-22'
GROUP BY sensor_id, DATE_TRUC('day', ts)
ORDER BY sensor_id, day;
-- Execution Time: 6 min 1 sec

Query

代码语言:javascript代码运行次数:0运行复制
SELECT sensor_id, ts, value
FROM sensors_uncompressed
WHERE ts >= '202-12-21 07:15:00'
AD ts < '202-12-21 07:20:00'
ORDER BY value DESC
LIMIT 5;
-- Execution Time: 6 min 24 sec
压缩超表基准测试

现在是时候收集使用Timescale的列存储方法的压缩超表(按时间自动分区PostgreSQL表)的统计信息了。

步骤1:创建一个表

代码语言:javascript代码运行次数:0运行复制
CREATE TABLE sensors_compressed (
    sensor_id ITEGER,
    ts TIMESTAMPTZ OT ULL,
    value REAL
);

步骤2:创建索引

代码语言:javascript代码运行次数:0运行复制
CREATE IDEX sensors_ts_idx_compressed O sensors_compressed (sensor_id, ts DESC);

步骤:转换为超表

代码语言:javascript代码运行次数:0运行复制
SELECT create_hypertable('sensors_compressed', by_range('ts', ITERVAL '1 hour'));

步骤4:启用列存储/压缩

代码语言:javascript代码运行次数:0运行复制
ALTER TABLE sensors_compressed SET (timescaledbpress, timescaledbpress_segmentby = 'sensor_id');

步骤5:添加压缩策略

代码语言:javascript代码运行次数:0运行复制
SELECT add_compression_policy('sensors_compressed', ITERVAL '24 hour');

步骤6:摄取数据

代码语言:javascript代码运行次数:0运行复制
curl .csv.gz | gunzip | timescaledb-parallel-copy -batch-size 5000 -connection $COECTIO_STRIG -table sensors_compressed -workers 4 -split '\t'

以下是成功将十亿行数据导入启用压缩的超表后的统计信息。

  • 数据导入时间:1小时分21秒
  • 包括索引和数据的总表大小:5.5 GB

步骤7:运行聚合查询

Query 1

代码语言:javascript代码运行次数:0运行复制
SELECT * FROM sensors_compressed
WHERE sensor_id = 0
AD ts >= '202-12-21 07:15:00'::timestamp
AD ts <= '202-12-21 07:16:00'::timestamp;
-- Execution Time: 20 ms

Query 2

代码语言:javascript代码运行次数:0运行复制
SELECT sensor_id, DATE_TRUC('day', ts) AS day, MAX(value) AS max_value, MI(value) AS min_value
FROM sensors_compressed
WHERE ts >= DATE '202-12-21' AD ts < DATE '202-12-22'
GROUP BY sensor_id, DATE_TRUC('day', ts)
ORDER BY sensor_id, day;
-- Execution Time: 5 min

Query

代码语言:javascript代码运行次数:0运行复制
SELECT sensor_id, ts, value 
FROM sensors_compressed 
WHERE ts >= '202-12-21 07:15:00' 
AD ts < '202-12-21 07:20:00' 
ORDER BY value DESC 
LIMIT 5;
Execution Time: 4.4 sec

关键要点

  • 存储效率: 启用压缩后,表大小减少了约 95%
  • 聚合查询 1 在压缩表上快了 47.7%
  • 聚合查询 2 在压缩表上快了 2%
  • 聚合查询 在压缩表上快了 98.8%

这些结果证明了使用 TimescaleDB 的压缩功能的显著优势,无论是在存储节省方面还是在查询性能改进方面。使用分块跳过索引增强 PostgreSQL 性能

在时间尺度中的块跳过

进一步加快 PostgreSQL 性能并减少存储空间占用的是 Timescale 的分块跳过索引(从 TimescaleDB 2.16.0 开始可用)。此功能使开发人员能够使用元数据在规划或执行期间动态修剪和排除分区(称为块),因为并非所有查询都非常适合分区。如果无法按分区列进行筛选,则会导致查询缓慢,因为 PostgreSQL 无法在没有非分区列的元数据的情况下排除任何分区。

分块跳过索引通过允许我们在搜索大型数据集时绕过不相关的块来优化查询性能。

在 TimescaleDB 中,数据被组织成基于时间的块,每个块代表超表的整体的一个子集。当查询指定时间范围或其他可以筛选数据的条件时,分块跳过索引使用元数据来识别和访问仅相关的块,而不是顺序扫描每个块。

这种有针对性的访问最大限度地减少了磁盘 I/O 和计算开销,使查询更快、更高效,尤其是在拥有数十亿行的超表中。

让我们创建一个名为 product_orders 的表,其中包含订单详细信息的列,例如 ID、时间戳、数量、总计、地址和状态。

代码语言:javascript代码运行次数:0运行复制
CREATE TABLE product_orders (
    order_id serial,
    order_date timestamptz,
    customer_id int,
    product_id int,
    quantity int,
    order_total float,
    shipping_address text,
    payment_status text,
    order_status text
);

转换为超表

product_orders 表转换为 TimescaleDB 超表,按 order_date 分区,间隔为四天。

代码语言:javascript代码运行次数:0运行复制
SELECT create_hypertable('product_orders', 'order_date', c_time_interval=>'4 day'::interval);

摄取数据

为了导入数据,我们将使用一个查询来生成 5000 万行虚拟订单数据,模拟从 202 年 1 月 1 日开始每分钟一个订单。该查询为客户和产品 ID、数量、总计和状态字段分配随机值,以创建真实的订单记录。

代码语言:javascript代码运行次数:0运行复制
WITH time_series AS (
    SELECT generate_series(
        '202-01-01 00:00:00'::timestamptz,
        '202-01-01 00:00:00'::timestamptz + interval '50000000 minutes',
        '1 minute'::interval
    ) AS order_date
)
ISERT ITO product_orders (
    order_date, customer_id, product_id, quantity, order_total,
    shipping_address, payment_status, order_status
)
SELECT
    order_date,
    (random() * 1000)::int + 1 AS customer_id,
    (random() * 100)::int + 1 AS product_id,
    (random() * 10 + 1)::int AS quantity,
    (random() * 500 + 10)::float AS order_total,
    '12 Example St, Example City' AS shipping_address,
    CASE WHE random() > 0.1 THE 'Completed' ELSE 'Pending' ED AS payment_status,
    CASE WHE random() > 0.2 THE 'Shipped' ELSE 'Pending' ED AS order_status
FROM time_series;

数据导入完成后,让我们执行一个简单的 SELECT 语句来测量查询执行所需的时间。

代码语言:javascript代码运行次数:0运行复制
tsbd=> select * from product_orders where order_id = 50000000;
order_id | order_date           | customer_id | product_id | quantity | order_total      | shipping_address           | payment_status | order_status
----------+-----------------------+-------------+------------+----------+-------------------+------------------------------+----------------+--------------
50000000 | 2117-01-24 12::00+00 | 515         | 14         | 9        | 61.005405718740 | 12 Example St, Example City | Completed       | Shipped
(1 row)
Time: 42049.154 ms (00:42.049)

目前,order_id 列上没有索引,这就是为什么查询花费近 42 秒才能执行的原因。

添加索引

让我们看看是否可以通过在 order_id 列上创建 B 树索引 来减少这 42 秒。

代码语言:javascript代码运行次数:0运行复制
CREATE IDEX order_id O product_orders (order_id);

创建索引后,让我们重新运行 SELECT 查询并检查执行时间是否从 42 秒减少。

代码语言:javascript代码运行次数:0运行复制
tsdb=> select * from product_orders where order_id = 50000000;
order_id | order_date | customer_id | product_id | quantity | order_total | shipping_address | payment_status | order_status
----------+------------------------+-------------+------------+----------+-------------------+------------------------------+----------------+--------------
50000000 | 2117-01-24 12::00+00 | 515 | 14 | 9 | 61.005405718740 | 12 Example St, Example City | Completed | Shipped
(1 row)
Time: 9684.18 ms (00:09.684)

太好了!创建索引后,执行时间缩短到了不到9秒,这是一个显著的改进。现在,让我们进一步优化,探索块跳过如何能进一步提升性能。

启用分块跳过索引

要利用块跳过索引,我们首先需要在表上启用块跳过,然后对其进行压缩。这样可以让 TimescaleDB 为每个块生成必要的元数据。

代码语言:javascript代码运行次数:0运行复制
ALTER TABLE product_orders SET (timescaledbpress);
SELECT compress_c(show_cs('product_orders'));
SELECT enable_c_skipping('product_orders', 'order_id');

启用分块跳过和列存储(压缩数据)后,让我们重新运行相同的SELECT查询以观察性能改进。

代码语言:javascript代码运行次数:0运行复制
select * from product_orders where order_id = 50000000;
order_id | order_date | customer_id | product_id | quantity | order_total | shipping_address | payment_status | order_status
----------+------------------------+-------------+------------+----------+-------------------+------------------------------+----------------+--------------
50000000 | 2117-01-24 12::00+00 | 515 | 14 | 9 | 61.005405718740 | 12 Example St, Example City | Completed | Shipped
(1 row)
Time: 04.1 ms

哇!查询现在只需 04 毫秒即可执行,与没有索引的初始执行时间相比,性能提高了 99.28%,与 PostgreSQL 索引相比,性能提高了 96.86%。这是一个显著的差异!

查询优化方法

执行时间

性能提升(与无索引相比)

无索引

42,049 毫秒(≈42 秒)

基线

使用 B 树索引

9,684 毫秒(≈9.7 秒)

快 77%

使用分块跳过索引 + 列存储(压缩)

04 毫秒(0. 秒)

快 99.28%

总之,使用 TimescaleDB 的关键特性——例如超表、列存储和分块跳过索引——可以极大地提高 PostgreSQL 的性能:

  • 超表帮助您更轻松地管理大量数据,同时保持一切井然有序。
  • 列存储减少存储空间并通过减少需要读取的数据量来加快查询速度。
  • 分块跳过索引还可以通过忽略不必要的数据来加快查询性能。

这些特性共同简化了时间序列数据、事件和实时分析的工作。通过选择 TimescaleDB,您正在投资一个更高效、更强大的数据系统,该系统可以处理大型工作负载并轻松扩展 PostgreSQL。

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。 原始发表:2025-01-112,如有侵权请联系 cloudcommunity@tencent 删除压缩postgresql数据处理数据索引

#感谢您对电脑配置推荐网 - 最新i3 i5 i7组装电脑配置单推荐报价格的认可,转载请说明来源于"电脑配置推荐网 - 最新i3 i5 i7组装电脑配置单推荐报价格

本文地址:http://www.dnpztj.cn/biancheng/1184469.html

相关标签:无
上传时间: 2025-07-22 09:13:41
留言与评论(共有 13 条评论)
本站网友 武汉卡梅尔小镇
28分钟前 发表
15
本站网友 武汉租房网
25分钟前 发表
代码语言:javascript代码运行次数:0运行复制ALTER TABLE product_orders SET (timescaledbpress); SELECT compress_c(show_cs('product_orders')); SELECT enable_c_skipping('product_orders'
本站网友 性爱音乐
17分钟前 发表
ITERVAL '24 hour');步骤6:摄取数据代码语言:javascript代码运行次数:0运行复制curl .csv.gz | gunzip | timescaledb-parallel-copy -batch-size 5000 -connection $COECTIO_STRIG -table sensors_compressed -workers 4 -split '\t'以下是成功将十亿行数据导入启用压缩的超表后的统计信息
本站网友 修改mac地址软件
13分钟前 发表
Example City | Completed | Shipped (1 row) Time
本站网友 祛白酊
13分钟前 发表
5 minQuery 代码语言:javascript代码运行次数:0运行复制SELECT sensor_id
本站网友 动力火车摇篮曲
24分钟前 发表
每个块代表超表的整体的一个子集
本站网友 买酒网站
19分钟前 发表
这就是为什么查询花费近 42 秒才能执行的原因
本站网友 移动短信平台
13分钟前 发表
00
本站网友 三大运营商财报
7分钟前 发表
总计
本站网友 麒麟丸
12分钟前 发表
然后对其进行压缩
本站网友 美甲大概多少钱
7分钟前 发表
聚合查询 2 在压缩表上快了 2%
本站网友 更新带动器
15分钟前 发表
16