Postgresql 提高大表中的选择性能/并行性

Mat*_* SM 5 postgresql performance postgresql-9.5 query-performance performance-tuning

我有一个巨大的表(> 30 亿行,总计 3 TB),使用 Postgres 9.5 定义如下:

CREATE TABLE user_events
(
    user_id VARCHAR NOT NULL,
    datetime BIGINT NOT NULL,
    field1 VARCHAR,
    field2 VARCHAR,
    field3 VARCHAR,
    field4 VARCHAR,
    field5 VARCHAR,
    CONSTRAINT user_events_user_id_datetime_pk PRIMARY KEY (user_id, datetime)
);

alter table user_events set (autovacuum_vacuum_cost_delay=10);
Run Code Online (Sandbox Code Playgroud)

我需要大约每秒 2000 次查询这个表,但我无法进行足够的并行化(我达到了大约一半)。

使用以下准备好的语句,我得到了合理的(我想)响应时间:

PREPARE getact AS select * from user_events where user_id = $1 order by datetime limit 1000;

explain analyze EXECUTE getact('xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx');
Run Code Online (Sandbox Code Playgroud)
CREATE TABLE user_events
(
    user_id VARCHAR NOT NULL,
    datetime BIGINT NOT NULL,
    field1 VARCHAR,
    field2 VARCHAR,
    field3 VARCHAR,
    field4 VARCHAR,
    field5 VARCHAR,
    CONSTRAINT user_events_user_id_datetime_pk PRIMARY KEY (user_id, datetime)
);

alter table user_events set (autovacuum_vacuum_cost_delay=10);
Run Code Online (Sandbox Code Playgroud)

一些补充说明:

  • 内容没有以有序的方式加载,因此日期时间不会越来越多地物理排序,但从现在开始它会。
  • 该表主要是仅追加
  • 从长远来看,我们预计该表最多增长 50%。

在这一点上,我正在寻找任何配置建议。我知道我有像只读副本或分片这样的扩展选项,但现在想尽可能避免这种情况。

CLUSTER在这种情况下会执行工作吗?似乎大部分成本都在索引扫描中,所以我猜它不会,但我不知道(并且测试它真的很昂贵)。

更多信息:

  • 有多个线程以相同的速率写入(主要是追加)需要读取
  • 虽然用准备好的语句描述的时间是真实的,但我看到结果从 < 1 ms 到 > 100ms

EXPLAIN (ANALYZE, BUFFERS) (最慢)结果

EXPLAIN (ANALYZE, BUFFERS) SELECT * FROM user_events WHERE user_id = 'XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX';
Run Code Online (Sandbox Code Playgroud)
PREPARE getact AS select * from user_events where user_id = $1 order by datetime limit 1000;

explain analyze EXECUTE getact('xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx');
Run Code Online (Sandbox Code Playgroud)
EXPLAIN (ANALYZE, BUFFERS) SELECT * FROM user_events WHERE user_id = 'yyyyyyyy-yyyy-yyyy-yyyy-yyyyyyyyyyyy';
Run Code Online (Sandbox Code Playgroud)
                                                                        QUERY PLAN                                                                        
----------------------------------------------------------------------------------------------------------------------------------------------------------
 Limit  (cost=0.70..1495.10 rows=367 width=878) (actual time=0.028..0.044 rows=4 loops=1)
   ->  Index Scan using user_events_user_id_datetime_pk on user_events  (cost=0.70..1495.10 rows=367 width=878) (actual time=0.026..0.034 rows=4 loops=1)
         Index Cond: ((user_id)::text = 'xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx'::text)
 Execution time: 0.073 ms
Run Code Online (Sandbox Code Playgroud)

Mat*_* SM 1

虽然我没有找到这个问题的答案(由于时间限制),但我认为最有用的提示是:

使用EXPLAIN (ANALYZE, BUFFERS) ...许多查询(具有不同的值)来尝试了解正在发生的情况。CLUSTER就我而言,这作为一个可能的优化选项放回原处。

如果我有时间尝试优化表格,我会做什么:

  1. 将索引值更改为尽可能小的值。就我而言,这意味着将user_idfrom更改VARCHARUUID
  2. 执行CLUSTER以减少获取的页面数。