简体   繁体   English

对于 10 亿行,Postgres 查询非常慢

[英]Postgres query is very slow for 1 billion rows

I have around 1 billion rows and 6 columns (id, col1, col2..col5) in my Postgres database table.我的 Postgres 数据库表中有大约 10 亿行和 6 列(id、col1、col2..col5)。 I have id as my PRIMARY_KEY.我有 id 作为我的 PRIMARY_KEY。 I am using this as a read-only database, and every time I only need to retrieve/read 10000 rows based on a list of 10000 ids.我将其用作只读数据库,每次我只需要根据 10000 个 ID 列表检索/读取 10000 行。 The query takes 40-60 seconds to retrieve 10000 rows, which is very slow.查询需要 40-60 秒才能检索 10000 行,这非常慢。 Below is my query:以下是我的查询:

SELECT * 
FROM table_1b 
WHERE id IN (18792, 8718, 15010, 16671,.....*list of 10000 ids*....., 290224379, 192494270)

EXPLAIN ANALYZE for the query: EXPLAIN ANALYZE 查询:

Index Scan using table_1b_pkey on table_1b 
(cost=0.57..46197.28 rows=10000 width=239) 
(actual time=13.305..55927.060 rows=10000 loops=1)
Index Cond: (id = ANY ('{18792, 8718, 15010, 16671,............,290224379,192494270}'::bigint[]))
Planning Time: 13.380 ms
Execution Time: 55935.614 ms

I would like to significantly speed up the retrieval of the 10000 rows.我想显着加快 10000 行的检索速度。 I am fairly new to Postgres.我对 Postgres 相当陌生。 I have read other posts and tried a few things including modifying work_mem from 4 MB to 256 MB, but it did not help.我已阅读其他帖子并尝试了一些方法,包括将 work_mem 从 4 MB 修改为 256 MB,但没有帮助。 The Postgres server is running on a machine with 384 GB RAM. Postgres 服务器在具有 384 GB RAM 的机器上运行。

Appreciate any help in advance.提前感谢任何帮助。

Edit 1: EXPLAIN ANALYZE for 10 records编辑 1:解释 10 条记录的分析

Index Scan using table_1b_pkey on table_1b
(cost=0.57..49.95 rows=10 width=239) 
(actual time=17.204..123.489 rows=10 loops=1)
Planning Time: 0.378 ms
Execution Time: 123.540 ms

Edit 2: track_io_timing = ON;编辑 2:track_io_timing = ON; EXPLAIN (ANALYZE, BUFFERS)解释(分析,缓冲区)

Index Scan using table_1b_pkey on table_1b 
(cost=0.57..17243.33 rows=10000 width=239) 
(actual time=17.304..55371.511 rows=10000 loops=1)
Buffers: shared hit=29891 read=20108
I/O Timings: read=55032.422
Planning Time: 13.113 ms
Execution Time: 55380.644 ms

Edit 3: Updated Server Configuration编辑 3:更新的服务器配置

max_connections = 20
shared_buffers = 25GB
effective_cache_size = 75GB
maintenance_work_mem = 2GB
checkpoint_completion_target = 0.7
wal_buffers = 16MB
default_statistics_target = 100
random_page_cost = 1.1
effective_io_concurrency = 200
work_mem = 640MB
min_wal_size = 1GB
max_wal_size = 4GB

So essentially all the time is spent reading the data from disk.所以基本上所有的时间都花在从磁盘读取数据上。 Each row requires two reads, one for the index leaf page, and one for the table page.每行需要两次读取,一次用于索引叶页,一次用于表页。 (The higher level blocks in the index are presumably mostly in cache). (索引中更高级别的块可能主要在缓存中)。 Some (unknown) fraction of them might be satisfied by the OS/filesystem cache.操作系统/文件系统缓存可能会满足其中的一些(未知)部分。

384GB is a lot of memory. 384GB是很多memory。 Maybe it is enough to hold the entire table and index, I don't know.也许容纳整个表和索引就足够了,我不知道。 (How big is the table and index?) But just having enough RAM to hold the data set is not enough. (表和索引有多大?)但是仅仅有足够的 RAM 来保存数据集是不够的。 You would have to get the data into RAM in the first place (pg_prewarm could help with that) and you would have to convince the OS to keep it there rather than evict it in favor of something else (do you also have other large data sets which are frequently used?).您必须首先将数据放入 RAM(pg_prewarm 可以帮助您),并且您必须说服操作系统将其保留在那里而不是驱逐它以支持其他东西(您是否还有其他大型数据集哪些是经常使用的?)。

Of course the "easy" solution is to get faster storage hardware.当然,“简单”的解决方案是获得更快的存储硬件。

If you created a covering index which included all the columns of the table (you could use the INCLUDE syntax, so that you could still have just one index and use it both for this query and for uniqueness enforcement) that would enable index-only scans.如果您创建了一个包含表的所有列的覆盖索引(您可以使用 INCLUDE 语法,这样您仍然可以只有一个索引并将其用于此查询和唯一性强制),这将启用仅索引扫描. This would cut the amount of IO in half, as then you would only need to read the index leaf page and not the table page for each row.这会将 IO 的数量减少一半,因为您只需要读取每行的索引叶页而不是表页。

You could get parallel query if you put the ids into another table and then write the query as a join.如果将 id 放入另一个表中,然后将查询写入联接,则可以获得并行查询。

SELECT table_1b.* 
FROM table_1b JOIN id_list using (id)

Since the id_list table will be small (10,000 rows large for an IN list, but not for a table) you probably need to override its settings to get parallel query to actually be used.由于 id_list 表会很小(对于 IN 列表,10,000 行大,但对于表来说不是),您可能需要覆盖其设置才能实际使用并行查询。 ALTER TABLE id_list set (parallel_workers=8) . ALTER TABLE id_list set (parallel_workers=8) How effective this is would depend on your storage system.这有多有效取决于您的存储系统。 A single spinning HDD would likely see little improvement, while a SSD or a RAID of HDD would see more.单个旋转 HDD 可能会看到很少的改进,而 SSD 或 HDD RAID 会看到更多。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM