postgresql - Postgres:如何有效地对以下随机事件 id 进行分桶(小时、config_id、sensor_id)
问题描述
我有一个有 4 列的大表“测量”:
measurement-service=> \d measurement
Table "public.measurement"
Column | Type | Collation | Nullable | Default
-----------------------+-----------------------------+-----------+----------+---------
hour | timestamp without time zone | | not null |
config_id | bigint | | not null |
sensor_id | bigint | | not null |
event_id | uuid | | not null |
Partition key: RANGE (hour)
Indexes:
"hour_config_id_sensor_id_event_id_key" UNIQUE CONSTRAINT, btree (hour, config_id, sensor_id, event_id)
Number of partitions: 137 (Use \d+ to list them.)
分区名称示例:“measurement_y2019m12d04”
然后我通过 COPY 将许多事件作为 CSV 插入到一个临时表中,然后我从那里使用 ON CONFLICT DO NOTHING 将表直接复制到分区中。
例子:
CREATE TEMPORARY TABLE 'tmp_measurement_y2019m12d04T02_12345' (
hour timestamp without timezone,
config_id bigint,
sensor_id bigint,
event_id uuid
) ON COMMIT DROP;
[...]
COPY tmp_measurement_y2019m12d04T02_12345 FROM STDIN DELIMITER ',' CSV HEADER;
INSERT INTO measurement_y2019m12d04 (SELECT * FROM tmp_measurement_y2019m12d04T02_12345) ON CONFLICT DO NOTHING;
我想我只通过发送包含同一小时数据的 CSV 来帮助 postgres。也在那个小时内,我删除了 CSV 中的所有重复项。因此 CSV 仅包含唯一行。
但是我在不同的时间发送了很多批次。没有顺序。它可以是今天、昨天、最后一周的时间。等等。
到目前为止,我的方法效果很好,但我认为我现在已经达到了极限。插入速度变得很慢。当 CPU 空闲时,我有 25% 的 i/o 等待。子系统是具有数 TB 的 RAID,使用非 SSD 的磁盘。
maintenance_work_mem = 32GB
max_wal_size = 1GB
fsync = off
max_worker_processes = 256
wal_buffers = -1
shared_buffers = 64GB
temp_buffers = 4GB
effective_io_concurrency = 1000
effective_cache_size = 128GB
每天每个分区大约 20gb 大,包含不超过 500m 行。通过维护每个分区的唯一索引,我再次复制了数据。
另一方面,查找速度很快。
我认为限制是在(小时,config_id,sensor_id)中使用相当随机的UUID维护btree。我不断地改变它,把它写出来,必须重新阅读。
我想知道,如果有另一种方法。基本上我想要(小时,config_id,sensor_id,event_id)的唯一性,然后每个(小时,config_id,sensor_id)快速查找。
我正在考虑删除唯一索引,并且只有一个超过(小时,config_id,sensor_id)的索引。然后在读者方面提供唯一性。但它可能会减慢阅读速度,因为当我通过(小时,config_id,sensor_id)查找时,event_id 不能再通过索引传递。它必须访问实际行以获取 event_id。
或者我通过哈希索引提供唯一性。
欢迎任何其他想法!
谢谢你。
解决方案
当您执行插入时,您应该指定一个与要插入的表的索引匹配的 ORDER BY:
INSERT INTO measurement_y2019m12d04
SELECT * FROM tmp_measurement_y2019m12d04T02_12345
order by hour, config_id, sensor_id, event_id
只有当这未能提供足够的改进时,我才会考虑您列出的任何其他选项。
哈希索引不提供唯一性。您可以使用排除约束对其进行模拟,但我认为它们的效率较低。排除约束支持 DO NOTHING,但不支持 DO UPDATE。因此,只要您的用例不会演变为需要 DO UPDATE,那么您在这方面会做得很好,但我仍然怀疑它是否真的能解决问题。如果您的瓶颈是更新索引的 IO,那么哈希只会使情况变得更糟,因为它旨在将您的数据分散到整个地方,而不是将其集中在一个小的可缓存区域中。
您还提到了并行处理。对于插入临时表,这可能没问题。但我不会并行执行 INSERT...SELECT 。如果 IO 是您的瓶颈,那可能只会让情况变得更糟。当然,如果在我的 ORDER BY 建议之后 IO 不再是瓶颈,那么忽略这部分。
推荐阅读
- c# - MS GraphAPI Drives 创建新文件夹
- list - 如何使用递归删除 Racket 列表中的第一个和最后一个元素
- sql-server - 条件存在于 top(1) 行
- c++ - 调用具有可转换类型的重载函数时模板化构造函数歧义
- angular - Angular 8 中单页应用程序上的跨组件通信
- java - org.apache.spark.ml.linalg.DenseVector 不是矢量模式的有效外部类型
- javascript - 使用表单发送数组时反转
- javascript - 如何在断点处重新排序 div/更改标记
- python - 如何编写代码来读取输出文件以找出它在抓取网站中的距离,然后从它停止的地方开始
- flutter - 当用户在颤动中转到下一个文本文件时,我如何获得文本字段的值