如果Postgres坚持执行顺序扫描,该如何强制它使用索引?
如果Postgres坚持执行顺序扫描,该如何强制它使用索引?
Answers:
假设您要查询许多数据库中常见的“索引提示”功能,PostgreSQL没有提供这种功能。这是PostgreSQL团队做出的明智决定。在这里可以找到关于为什么以及可以做什么的很好的概述。原因基本上是,这是一个性能黑客,随着数据的变化,它会在以后引起更多的问题,而PostgreSQL的优化器可以根据统计信息对计划进行重新评估。换句话说,今天可能是一个好的查询计划,可能永远不会成为一个好的查询计划,并且索引提示会一直强迫一个特定的查询计划。
您可以使用enable_seqscan
和enable_indexscan
参数,这是一个非常钝的锤子,可用于测试。看到:
这些不适合正在进行的生产使用。如果您在查询计划选择方面遇到问题,则应查看有关跟踪查询性能问题的文档。不要只是设置enable_
参数然后走开。
除非您有很好的理由使用索引,否则Postgres可能会做出正确的选择。为什么?
另请参阅此旧新闻组帖子。
可能是使用的唯一有效理由
set enable_seqscan=false
是在编写查询并希望快速查看表中有大量数据时查询计划的实际情况。或者,当然,如果您只是由于数据集太小而需要快速确认查询未使用索引的话。
set enable_seqscan=false
,运行查询,然后快速运行set enable_seqscan=true
以使postgresql返回其正确的行为(并且显然不在生产中执行此操作,而仅在开发中!)
SET SESSION enable_seqscan=false
只影响自己
有时PostgreSQL无法针对特定条件对索引进行最佳选择。例如,假设有一个包含几百万行的事务处理表,其中任意一天有数百行,并且该表具有四个索引:transaction_id,client_id,date和description。您要运行以下查询:
SELECT client_id, SUM(amount)
FROM transactions
WHERE date >= 'yesterday'::timestamp AND date < 'today'::timestamp AND
description = 'Refund'
GROUP BY client_id
PostgreSQL可能选择使用索引transactions_description_idx而不是transactions_date_idx,这可能导致查询花费几分钟而不是不到一秒钟的时间。在这种情况下,您可以通过以下条件强制使用日期索引:
SELECT client_id, SUM(amount)
FROM transactions
WHERE date >= 'yesterday'::timestamp AND date < 'today'::timestamp AND
description||'' = 'Refund'
GROUP BY client_id
your_wanted_index
,而是可以使Postgresql引擎仅执行序列/主键扫描。结论-没有100%可靠的方法强制对PostgreSql服务器使用某些索引。
where
条件但只有两个表或已联接并且Postgres无法获取索引该怎么办。
当索引扫描的估计成本太高并且不能正确反映现实时,通常会发生此问题。您可能需要降低random_page_cost
配置参数来解决此问题。从Postgres文档中:
降低此值将使系统偏向于索引扫描;提高它会使索引扫描看起来相对更昂贵。
您可以检查一个较小的值是否实际上会使Postgres使用索引(但仅将其用于测试):
EXPLAIN <query>; # Uses sequential scan
SET random_page_cost = 1;
EXPLAIN <query>; # May use index scan now
您可以SET random_page_cost = DEFAULT;
再次使用恢复默认值。
索引扫描需要非顺序磁盘页读取。Postgres用于random_page_cost
估计与顺序获取有关的此类非顺序获取的成本。默认值为4.0
,因此假设与顺序读取相比,平均成本因子为4(考虑了缓存效果)。
但是,问题在于此默认值不适用于以下重要的实际场景:
1)固态驱动器
正如文档所承认的:
相对于顺序驱动(例如固态驱动器)而言,具有较低随机读取成本的存储可以用较低的值更好地建模
random_page_cost
。
根据最后一点这张幻灯片从谈一谈PostgresConf 2018年,random_page_cost
应设置之间的事情1.0
和2.0
固态驱动器。
2)缓存的数据
如果所需的索引数据已经缓存在RAM中,则索引扫描将始终比顺序扫描快得多。该文档说:
相应地,如果您的数据可能完全在高速缓存中,则减少数据丢失
random_page_cost
是适当的。
问题是,您当然无法轻松知道相关数据是否已被缓存。但是,如果经常查询特定索引,并且系统具有足够的RAM,则可能会缓存数据,因此random_page_cost
应将其设置为较低的值。您必须尝试不同的值,然后看看哪种方法对您有用。
您可能还希望将pg_prewarm扩展用于显式数据缓存。
这个问题本身是非常无效的。强制执行(例如,通过启用enable_seqscan = off)是个坏主意。检查它是否会更快会很有用,但是生产代码永远不要使用这种技巧。
取而代之的是-解释一下您的查询,阅读它,然后找出为什么PostgreSQL选择错误的计划(在您看来)。
网络上有一些工具可以帮助阅读解释分析输出-其中之一是我编写的explain.depesz.com。
另一种选择是在freenode irc网络上加入#postgresql频道,并与那里的人聊天以帮助您-因为优化查询不是“问一个问题,得到一个快乐的答案”的问题。它更像是一次对话,需要检查很多事情,需要学习很多事情。
有一个技巧可以使postgres倾向于OFFSET 0
在子查询中添加seqscan 的方法
当您只需要n个first / last元素时,这对于优化链接大型/大型表的请求非常方便。
假设您要查找的前/后20个元素涉及多个具有100k(或更多)条目的表,当要查找的数据位于前100个或1000个中时,没有点建立/链接所有数据上的所有查询条目。例如,在这种情况下,进行顺序扫描的速度要快10倍以上。