对数十亿行表的慢查询 // 使用的索引

man*_*man 11 postgresql performance postgresql-9.3 query-performance

由于我是一名年轻的开发人员并且不太擅长使用数据库(PostgreSQL 9.3),因此我在项目中遇到了一些问题,我确实需要帮助。

我的项目是关于从设备(最多 1000 个或更多设备)收集数据,其中每个设备每秒发送一个数据块,每小时大约生成 300 万行。

目前我有一张大表,用于存储每个设备的传入数据:

CREATE TABLE data_block(
    id bigserial
    timestamp timestamp
    mac bigint
)
Run Code Online (Sandbox Code Playgroud)

由于数据块可以(或不可以)包含多种类型的数据,因此还有其他表引用该data_block表。

CREATE TABLE dataA(
    data_block_id bigserial
    data

    CONSTRAINT fkey FOREIGN KEY (data_block_id) REFERENCES data_block(id);
);
CREATE TABLE dataB(...);
CREATE TABLE dataC(...);
CREATE INDEX index_dataA_block_id ON dataA (data_block_id DESC);
...
Run Code Online (Sandbox Code Playgroud)

有可能在一个 data_block 中有 3x dataA、1x dataB,但没有 dataC。

数据将保留数周,因此该表中将有大约 50 亿行。目前,我在表中有大约 6 亿行,我的查询需要很长时间。所以我决定在timestampand上做一个索引mac,因为我的 select 语句总是随着时间的推移而查询,而且通常也随着时间+mac。

CREATE INDEX index_ts_mac ON data_block (timestamp DESC, mac);
Run Code Online (Sandbox Code Playgroud)

...但我的查询仍然需要很长时间。比如我查询了一天一台mac的数据:

SELECT * FROM data_block 
WHERE timestamp>'2014-09-15' 
AND timestamp<'2014-09-17' 
AND mac=123456789
Run Code Online (Sandbox Code Playgroud)
CREATE TABLE data_block(
    id bigserial
    timestamp timestamp
    mac bigint
)
Run Code Online (Sandbox Code Playgroud)

在查询运行之前我做了一个完全真空。有没有一种优雅的方法来解决大表的此类问题以进行<10秒的查询?

我读过分区,但这不适用于我的 dataA、dataB、dataC 对 data_block_id 的引用,对吗?如果它能以某种方式工作,我应该随着时间的推移还是通过 mac 进行分区?

我将索引更改为另一个方向。首先是 MAC,然后是时间戳,它获得了很多性能。

CREATE INDEX index_mac_ts ON data_block (mac, timestamp DESC);
Run Code Online (Sandbox Code Playgroud)

但是,查询仍然需要> 30 秒。特别是当我对LEFT JOIN数据表进行处理时。这是一个EXPLAIN ANALYZE带有新索引的查询:

EXPLAIN ANALYZE SELECT * FROM data_block WHERE mac = 123456789 AND timestamp < '2014-10-05 00:00:00' AND timestamp > '2014-10-04 00:00:00'
Run Code Online (Sandbox Code Playgroud)
CREATE TABLE dataA(
    data_block_id bigserial
    data

    CONSTRAINT fkey FOREIGN KEY (data_block_id) REFERENCES data_block(id);
);
CREATE TABLE dataB(...);
CREATE TABLE dataC(...);
CREATE INDEX index_dataA_block_id ON dataA (data_block_id DESC);
...
Run Code Online (Sandbox Code Playgroud)

不幸的是,我的硬件受到严格限制。我使用的是 Intel i3-2100 @3.10Ghz,4GB RAM。我目前的设置如下:

default_statistics_target = 100
maintenance_work_mem = 512MB
constraint_exclusion = on
checkpoint_completion_target = 0.9
effective_cache_size = 4GB
work_mem = 512MB
wal_buffers = 16MB
checkpoint_segments = 32
shared_buffers = 2GB
max_connections = 20
random_page_cost = 2
Run Code Online (Sandbox Code Playgroud)

小智 -2

我开发的应用程序具有数十亿个电表读数,并且在不到 10 秒的时间内执行了大多数查询。

我们的环境不同。服务器级计算机上的 Microsoft SQL Server(4 核、24 GB 内存)。有机会升级到服务器吗?

一个大问题是一次提取一个读数会对数据库的性能产生很大的影响。写入数据需要锁,查询会等待。可以批量插入吗?

根据您的架构,您将拥有 4 个非常大的表。所有连接都使用两个表上的索引非常重要。表扫描将花费很长时间。将它们合并到 1 个具有可为空字段的表是否可行?