【发布时间】:2022-08-20 00:35:20
【问题描述】:
我们有一个 180m 行的表,大小为 20 GB。 表 DDL 为:
create table app.table
(
a_id integer not null,
b_id integer not null,
c_id integer not null,
d_id integer not null,
e_id integer not null,
f_id integer not null,
a_date timestamp not null,
date_added timestamp,
last_date_modified timestamp default now()
);
价值分布:
- a_id 的范围为 0-160,000,000
- b_id只有一个值(这个表是一个分区表的单个分区的副本,这个ID正好是分区键)
- c_id 的范围为 0-4
- d_id 有一个值(当前)
- e_id 有一个值(当前)
主键是复合键:
alter table app.table add constraint table_pk primary key (a_id, b_id, c_id, d_ie, e_ie);
我们在 Aurora PostgreSQL v12.8 中运行 r6g.xlarge 集群。这是一个没有其他流量的实例。我们已经在桌子上运行了 ANALYZE 和 VACUUM ANALYZE:
INFO: \"table\": scanned 30000 of 1711284 pages, containing 3210000 live
rows and 0 dead rows; 30000 rows in sample, 183107388 estimated total rows
问题
当shared_buffers 很冷(或我们可以得到的最冷)时,此查询需要 9 秒才能运行:
select a_id, b_id, c_id, d_id, a_date
from app.table ts
where a_id in ( <5000 values> )
and b_id = 34
and c_id in (2,3)
and d_id = 0
EXPLAIN 输出:
Index Scan using table_pk on table ts (cost=0.57..419134.91 rows=237802 width=24) (actual time=8.335..9803.424 rows=5726 loops=1)
\" Index Cond: ((a_id = ANY (\'{66986803,90478329,...,121697593}\'::integer[])) AND (b_id = 34))\"
\" Filter: (c_id = ANY (\'{2,3}\'::integer[])))\"
Rows Removed by Filter: 3
Buffers: shared hit=12610 read=10593
I/O Timings: read=9706.055
Planning:
Buffers: shared hit=112 read=29
I/O Timings: read=29.227
Planning Time: 33.437 ms
Execution Time: 9806.271 ms
我们认为这是不合理的缓慢。当查询再次运行时,因此来自缓存,所需时间为 25 毫秒。如果可能,我们宁愿不预热。
无论如何,我们宁愿对这种查询有更好的性能,如果可能的话,大约在 1-2 秒左右。关于我们如何提高性能的任何想法?
编辑 - 添加覆盖索引的效果:
尝试添加覆盖索引以包含 \"a_date\":
create unique index covering_idx on app.table (a_id, b_id, c_id, d_id, e_id) include (a_date)
EXPLAIN 重新运行查询后的结果(使用冷的shared_buffers 缓存):
Index Only Scan using covering_idx on table ts (cost=0.57..28438.58 rows=169286 width=24) (actual time=8.020..7028.442 rows=5658 loops=1)
Index Cond: ((a_id = ANY (\'{134952505,150112033,…,42959574}\'::integer[])) AND (b_id = 34))
Filter: ((e_id = ANY (\'{0,0}\'::integer[])) AND (c_id = ANY (\'{2,3}\'::integer[])))
Rows Removed by Filter: 2
Heap Fetches: 0
Buffers: shared hit=12353 read=7733
I/O Timings: read=6955.935
Planning:
Buffers: shared hit=80 read=8
I/O Timings: read=8.458
Planning Time: 11.930 ms
Execution Time: 7031.054 ms
使用位图堆扫描与索引扫描时的效果:
我们发现,当使用位图堆扫描而不是索引扫描执行查询时,我们可以加快速度。我们通过使用pg_hint_plan 强制执行计划发现了这一点:
添加/*+ BitmapScan(table) */时:
Bitmap Heap Scan on table ts (cost=22912.96..60160.79 rows=9842 width=24) (actual time=3972.237..4063.417 rows=5657 loops=1)
Recheck Cond: ((a_id = ANY (\'{24933126,19612702,27100661,73628268,...,150482461}\'::integer[])) AND (b_id = 34))
Filter: ((d_id = ANY (\'{0,0}\'::integer[])) AND (c_id = ANY (\'{2,3}\'::integer[])))
Rows Removed by Filter: 4
Heap Blocks: exact=5644
Buffers: shared hit=14526 read=11136
I/O Timings: read=22507.527
-> Bitmap Index Scan on table_pk (cost=0.00..22898.00 rows=9842 width=0) (actual time=3969.920..3969.920 rows=5661 loops=1)
Index Cond: ((a_id = ANY (\'{24933126,19612702,27100661,,150482461}\'::integer[])) AND (b_id = 34))
Buffers: shared hit=14505 read=5513
I/O Timings: read=3923.878
Planning:
Buffers: shared hit=6718
Planning Time: 21.493 ms
{Execution Time: 4066.582 ms
目前,我们正在考虑使用pg_hint_plan 将这个计划强制投入生产——但我们更想知道为什么规划者选择了一个不太理想的计划!我们已经运行 VACUUM ANALYZE 和 1000 的 default_statistics_target。
-
它似乎只是用于获取记录的 IO,因为它正在使用索引。您是否考虑过对这张表进行分区?
-
我刚刚意识到这是来自另一个表的分区的副本:P 然而,一个 20GB 的表似乎是进一步分区的候选者。
-
我们可以进一步对其进行分区,但这仅意味着我们最终会跨分区进行查询。据我了解,分区应该旨在让您尽可能少地访问分区,这将违反。
-
这完全取决于分区键范围......在不了解完整用例的情况下很难说。
-
我懂了。我会尝试创建一个covering index,也许这里的问题是堆页面的随机访问。
标签: postgresql amazon-aurora postgresql-12