我注意到一些奇怪/奇怪的东西:
开发/生产中完全相同的查询未使用相同的查询路径。特别是,开发版本使用的是在生产中省略的索引(支持seqscan)。
唯一真正的区别是数据集的生产量要大得多 - 索引大小为1034 MB,而生产中为29 MB。如果他们(或表)太大,PostgreSQL会不会使用索引吗?
编辑:EXPLAIN ANALYZE
两个查询:
发展:
Limit (cost=41638.15..41638.20 rows=20 width=154) (actual time=159.576..159.581 rows=20 loops=1)
-> Sort (cost=41638.15..41675.10 rows=14779 width=154) (actual time=159.575..159.577 rows=20 loops=1)
Sort Key: (sum(scenario_ad_group_performances.clicks))
Sort Method: top-N heapsort Memory: 35kB
-> GroupAggregate (cost=0.00..41244.89 rows=14779 width=154) (actual time=0.040..151.535 rows=14197 loops=1)
-> Nested Loop Left Join (cost=0.00..31843.75 rows=93800 width=154) (actual time=0.022..82.509 rows=50059 loops=1)
-> Merge Left Join (cost=0.00..4203.46 rows=14779 width=118) (actual time=0.017..27.103 rows=14197 loops=1)
Merge Cond: (scenario_ad_groups.id = scenario_ad_group_vendor_instances.ad_group_id)
-> Index Scan using scenario_ad_groups_pkey on scenario_ad_groups (cost=0.00..2227.06 rows=14779 width=114) (actual time=0.009..12.085 rows=14197 loops=1)
Filter: (scenario_id = 22)
-> Index Scan using index_scenario_ad_group_vendor_instances_on_ad_group_id on scenario_ad_group_vendor_instances (cost=0.00..1737.02 rows=27447 width=8) (actual time=0.007..7.021 rows=16528 loops=1)
Filter: (ad_vendor_id = ANY ('{1,2,3}'::integer[]))
-> Index Scan using index_ad_group_performances_on_vendor_instance_id_and_date on scenario_ad_group_performances (cost=0.00..1.73 rows=11 width=44) (actual time=0.002..0.003 rows=3 loops=14197)
Index Cond: ((vendor_instance_id = scenario_ad_group_vendor_instances.id) AND (date >= '2012-02-01'::date) AND (date <= '2012-02-28'::date))
Total runtime: 159.710 ms
生产:
Limit (cost=822401.35..822401.40 rows=20 width=179) (actual time=21279.547..21279.591 rows=20 loops=1)
-> Sort (cost=822401.35..822488.42 rows=34828 width=179) (actual time=21279.543..21279.560 rows=20 loops=1)
Sort Key: (sum(scenario_ad_group_performances.clicks))
Sort Method: top-N heapsort Memory: 33kB
-> GroupAggregate (cost=775502.60..821474.59 rows=34828 width=179) (actual time=19126.783..21226.772 rows=34495 loops=1)
-> Sort (cost=775502.60..776739.48 rows=494751 width=179) (actual time=19125.902..19884.164 rows=675253 loops=1)
Sort Key: scenario_ad_groups.id
Sort Method: external merge Disk: 94200kB
-> Hash Right Join (cost=25743.86..596796.70 rows=494751 width=179) (actual time=1155.491..16720.460 rows=675253 loops=1)
Hash Cond: (scenario_ad_group_performances.vendor_instance_id = scenario_ad_group_vendor_instances.id)
-> Seq Scan on scenario_ad_group_performances (cost=0.00..476354.29 rows=4158678 width=44) (actual time=0.043..8949.640 rows=4307019 loops=1)
Filter: ((date >= '2012-02-01'::date) AND (date <= '2012-02-28'::date))
-> Hash (cost=24047.72..24047.72 rows=51371 width=143) (actual time=1123.896..1123.896 rows=34495 loops=1)
Buckets: 1024 Batches: 16 Memory Usage: 392kB
-> Hash Right Join (cost=6625.90..24047.72 rows=51371 width=143) (actual time=92.257..1070.786 rows=34495 loops=1)
Hash Cond: (scenario_ad_group_vendor_instances.ad_group_id = scenario_ad_groups.id)
-> Seq Scan on scenario_ad_group_vendor_instances (cost=0.00..11336.31 rows=317174 width=8) (actual time=0.020..451.496 rows=431770 loops=1)
Filter: (ad_vendor_id = ANY ('{1,2,3}'::integer[]))
-> Hash (cost=5475.55..5475.55 rows=34828 width=139) (actual time=88.311..88.311 rows=34495 loops=1)
Buckets: 1024 Batches: 8 Memory Usage: 726kB
-> Bitmap Heap Scan on scenario_ad_groups (cost=798.20..5475.55 rows=34828 width=139) (actual time=4.451..44.065 rows=34495 loops=1)
Recheck Cond: (scenario_id = 276)
-> Bitmap Index Scan on index_scenario_ad_groups_on_scenario_id (cost=0.00..789.49 rows=34828 width=0) (actual time=4.232..4.232 rows=37006 loops=1)
Index Cond: (scenario_id = 276)
Total runtime: 21306.697 ms
答案 0 :(得分:15)
免责声明
我很少使用PostgreSQL。我正在根据我对SQL Server索引使用和执行计划的了解来回答。如果我出错了,我会向PostgreSQL众神寻求怜悯。
查询优化工具是动态的
您说您的查询计划已从开发环境更改为生产环境。这是可以预料的。查询优化器旨在根据当前数据条件生成最佳执行计划。在不同的条件下,优化器可能会认为使用表扫描和索引扫描更有效。
何时使用表扫描与索引扫描更有效?
SELECT A, B
FROM someTable
WHERE A = 'SOME VALUE'
假设您在列A
上有非聚集索引。在这种情况下,您要对列A
进行过滤,这可能会利用索引。如果索引足够有选择性,那么这将是有效的 - 基本上,有多少不同的值构成索引?数据库保留有关此选择性信息的统计信息,并在计算执行计划的成本时使用这些统计信息。
如果表中有一百万行,但A
只有10个可能值,那么您的查询可能会返回大约100K行。由于索引是非群集的,并且您要返回未包含在索引B
中的列,因此需要对返回的每一行执行查找。这些查找是随机访问查找,比表扫描使用的顺序读取要贵得多。在某一点上,数据库只执行表扫描而不是索引扫描变得更有效。
这只是一个场景,还有很多其他场景。如果不了解您的数据是什么样的,索引的样子以及您尝试访问数据的方式,就很难知道。
回答原始问题:
如果他们(或表)太大,PostgreSQL会不会使用索引吗?不可以。在您访问数据的方式中,PostgreSQL使用索引与使用表扫描的效率较低。
PostgreSQL FAQ触及了这个主题(请参阅:为什么我的查询速度慢?为什么他们不使用我的索引?):https://wiki.postgresql.org/wiki/FAQ#Why_are_my_queries_slow.3F_Why_don.27t_they_use_my_indexes.3F
答案 1 :(得分:3)
Postgres的查询优化器提出了多种方案(例如索引与seq-scan),并使用有关表的统计信息以及配置中设置的磁盘/内存/索引/表访问的相对成本来评估它们。
您是否使用EXPLAIN
命令查看为什么省略索引使用?您是否使用EXPLAIN ANALYZE
来确定决定是否有误?我们能看到产出吗?
编辑:
就像在不同系统上分析两个不同的单一查询一样困难,我想我会看到一些事情。
生产环境的实际/成本率约为每个成本单位20-100毫秒。我甚至不是DBA,但这似乎是一致的。主查询的开发环境有261个。这看起来是对的吗?您是否期望生产环境的原始速度(内存/磁盘/ CPU)比开发环境快2-10倍?
由于生产环境具有更多复杂的查询计划,因此看起来它正在完成其工作。毫无疑问,开发环境的计划和更多已被考虑,并且被认为成本太高。根据我的经验,20-100差异并不是那么多(但同样,不是DBA),并且表明没有任何方法可行。不过,您可能希望在数据库上运行VACUUM
以防万一。
我没有足够的经验和耐心来解码完整的查询,但是可能存在非规范化/ NOSQL化的优化点吗?
最大的瓶颈似乎是90 MB的磁盘合并。如果生产环境有足够的内存,您可能需要增加相关设置(工作内存?)以在内存中进行。这似乎是work_mem
参数here,但您需要仔细阅读其余内容。
我还建议look使用index usage statistics。存在许多具有部分和功能指数的选项。
答案 2 :(得分:3)
尝试
SET enable_seqscan TO 'off'
之前EXPLAIN ANALYZE
答案 3 :(得分:2)
在我看来,您的开发数据比生产数据“简单”得多。举个例子:
发展:
-> Index Scan using index_scenario_ad_group_vendor_instances_on_ad_group_id on scenario_ad_group_vendor_instances
(cost=0.00..1737.02 rows=27447 width=8)
(actual time=0.007..7.021 rows=16528 loops=1)
Filter: (ad_vendor_id = ANY ('{1,2,3}'::integer[]))
生产:
-> Seq Scan on scenario_ad_group_vendor_instances
(cost=0.00..11336.31 rows=317174 width=8)
(actual time=0.020..451.496 rows=431770 loops=1)
Filter: (ad_vendor_id = ANY ('{1,2,3}'::integer[]))
这意味着,在dev 27447中,已经预先估计了匹配行,并且确实找到了16528行。这不是同一个球场,还可以。
在生产中,已预先估算了317174个匹配行,并找到了431770行。还行。
但是将dev与prod进行比较意味着数字是10倍不同。正如其他答案所表明的那样,进行10次随机搜索(由于索引访问)可能确实比普通表扫描更糟糕。
因此,有趣的问题是:这个表在dev和prod中包含多少行? dev和prod之间的number_returned_rows / number_total_rows
是否具有可比性?
编辑不要忘记:我已选择一个索引访问权作为示例。快速浏览显示其他索引访问具有相同的症状。