我从互联网资源中读到,当补偿增加时,查询速度会变慢.但就我而言,我觉得太慢了.我用postgres 9.3

以下是查询(id是主键):

select * from test_table offset 3900000 limit 100;

它会在大约10 seconds分钟内返回数据.我觉得太慢了.我在表中有大约4 million条记录.数据库的总大小为23GB.

机器配置:

RAM: 12 GB
CPU: 2.30 GHz
Core: 10

我更改的postgresql.conf文件中的几个值如下所示.其他都是默认的.

shared_buffers = 2048MB
temp_buffers = 512MB
work_mem = 1024MB
maintenance_work_mem = 256MB
dynamic_shared_memory_type = posix
default_statistics_target = 10000
autovacuum = on
enable_seqscan = off   ## its not making any effect as I can see from Analyze doing seq-scan

除此之外,我还try 了改变random_page_cost = 2.0cpu_index_tuple_cost = 0.0005的值,结果是一样的.

Explain (analyze, buffers)查询结果如下:

"Limit  (cost=10000443876.02..10000443887.40 rows=100 width=1034) (actual time=12793.975..12794.292 rows=100 loops=1)"
"  Buffers: shared hit=26820 read=378984"
"  ->  Seq Scan on test_table  (cost=10000000000.00..10000467477.70 rows=4107370 width=1034) (actual time=0.008..9036.776 rows=3900100 loops=1)"
"        Buffers: shared hit=26820 read=378984"
"Planning time: 0.136 ms"
"Execution time: 12794.461 ms"

世界各地的人是如何在研究生中讨论这个问题的?任何替代方案都会对我有所帮助.

UPDATE::添加order by id(也try 了其他索引列),下面是解释:

"Limit  (cost=506165.06..506178.04 rows=100 width=1034) (actual time=15691.132..15691.494 rows=100 loops=1)"
"  Buffers: shared hit=110813 read=415344"
"  ->  Index Scan using test_table_pkey on test_table  (cost=0.43..533078.74 rows=4107370 width=1034) (actual time=38.264..11535.005 rows=3900100 loops=1)"
"        Buffers: shared hit=110813 read=415344"
"Planning time: 0.219 ms"
"Execution time: 15691.660 ms"

推荐答案

它的速度很慢,因为它需要找到前offset行,然后扫描下offset行.当你处理巨大的偏移量时,任何优化都不会改变这一点.

这是因为您的查询可以让DB引擎通过使用offset 3900000来访问很多行,即390万行.加快速度的选项并不多.

超高速RAM、SSD等将有所帮助.但这样做只会获得一个恒定的因素,也就是说,这只不过是在路上踢罐子,直到你达到一个足够大的偏移量.

确保桌子能放在内存中,并有足够多的空闲空间,同样会有更大的恒定系数——except the first time.但如果有足够大的表或索引,这可能是不可能的.

确保只进行索引扫描在一定程度上是可行的.(见velis的答案,它有很多优点.)这里的问题是,出于所有实际目的,可以将索引视为存储磁盘位置和索引字段的表.(它比这更优化,但它是一个合理的第一近似值.)行数足够多时,偏移量足够大时仍会遇到问题.

试图存储和维护行的精确位置肯定也是一种昂贵的方法.(这是由例如benjist提出的.)虽然在技术上是可行的,但它也受到与使用树 struct 的MPTT类似的限制:在插入、更新或删除 node 时,需要同时更新大量数据,这样会大大增加读取次数,但最终会导致写入时间过长.

希望更清楚的是,在处理如此大的偏移量时,没有任何真正的灵丹妙药.通常,寻找替代方法更好.

如果您是基于ID(或日期字段,或任何其他可索引的字段集)进行分页,一个潜在的技巧(例如,blogspot使用)是使查询从索引中的任意点开始.

换句话说,不是:

example.com?page_number=[huge]

做一些类似的事情:

example.com?page_following=[huge]

这样,你就可以跟踪你在索引中的位置,查询就会变得非常快,因为它可以直接到达正确的起点,而不必费力地浏览无数行:

select * from foo where ID > [huge] order by ID limit 100

当然,你会失go 跳转到3000页的能力.但请诚实地想一想:你上一次跳转到一个网站上的一个巨大的页码,而不是直接go 查看它的每月档案或使用它的搜索框,是什么时候?

如果您正在分页,但希望以任何方式保持页面偏移,另一种方法是禁止使用较大的页码.这并不愚蠢:这是谷歌对搜索结果所做的.当运行搜索查询时,谷歌会给你一个估计的结果数(你可以用explain得到一个合理的数字),然后允许你浏览前几千个结果——仅此而已.除此之外,他们这样做是出于性能原因——正是你遇到的原因.

Postgresql相关问答推荐

函数返回查询执行plpgsql时不存在列

在Postgres游标中从一行变量中减go 另一行变量

数据库所有者无法授予 Select 权限

Upper() 不会大写重音字符

Power BI + Postgres:只读用户

无法从外部连接到在 AWS EC2 实例上运行的 Postgres

在 postgresql 数据库 timestampz 中保留用户偏移量

计算每行的多列中的非 NULL 元素

使用 RPostgreSQL 进行 UTF-8 / Unicode 文本编码

在psql中,为什么有些命令没有效果?

带有 postgres 的 DOCKER 容器,警告:could not open statistics file "pg_stat_tmp/global.stat": Operation not permitted

在 postgres 中Decode解码相似的函数

在这个 Dockerfile 中创建的 Postgres 用户名/密码在哪里?

PostgreSQL CASE 在函数中的使用

如何在 plpgsql 中使用记录类型变量?

POSTGRESQL 中的 CHARINDEX (SQL SERVER) 相似函数是什么?

Array Push的 Postgres 数组追加和数组长度

当从 Heroku pg:pull 数据库时提示:role "root" does not exist.

使用 PostgreSQL 的 Linq To Sql

实时监控 PostgreSQL 查询的应用程序?