2012-02-28 49 views
8

我注意到一些奇怪的/奇怪的:PostgreSQL查詢不使用生產索引

在開發/生產完全相同的查詢不使用相同的查詢路徑。特別是,開發版本使用的是在生產中省略的索引(有利於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

該指數在您的生產環境中可能沒有足夠的選擇性 – 2012-02-28 03:14:30

+0

「不夠選擇性」是什麼意思? – vonconrad 2012-02-28 03:30:40

+1

如果你有一個帶有索引的布爾列,其中99%的數據是'false',那麼掃描索引然後掃描表格的99%,而不是掃描整個表格就沒有多大的意義了。對於「錯誤」值,索引不夠有選擇性。 – aib 2012-02-28 04:55:42

回答

13

免責聲明

我使用PostgreSQL的很少。我回答基於我對SQL Server索引使用和執行計劃的瞭解。如果我出錯了,我會問PostgreSQL的神。

查詢優化是動態

你說你的查詢計劃已經從開發改爲生產環境。這是可以預料的。查詢優化器旨在根據當前數據條件生成最佳執行計劃。在不同的條件下,優化器可以決定使用表掃描與索引掃描相比效率更高。

什麼時候使用表掃描與索引掃描更高效?

SELECT A, B 
FROM someTable 
WHERE A = 'SOME VALUE' 

比方說,你有A列一個非聚集索引。在這種情況下,您正在過濾列A,這可能會利用索引。如果索引足夠有選擇性,基本上有多少不同的值構成索引,這將是有效的?數據庫保存這個選擇性信息的統計信息,並在計算執行計劃的成本時使用這些統計信息

如果表中有一百萬行,但只有10個可能的值爲A,那麼您的查詢可能會返回大約100K行。由於索引是非羣集的,並且您返回的索引中不包含列,B,因此需要爲每個返回的行執行查找。這些查找是隨機訪問查找,這些查找比表掃描使用的順序讀取要昂貴得多。在某個時候,數據庫僅僅執行表掃描而不是索引掃描會更高效。

這只是一種情況,還有很多其他情況。如果不知道更多關於您的數據是什麼樣的信息,您的索引是什麼樣的以及您如何嘗試訪問數據,很難知道。

要回答原來的問題

將PostgreSQL的使用索引放棄,如果他們(或表)過大?沒有。更有可能的是,在您訪問數據的方式中,PostgreSQL使用索引vs使用表掃描效率較低。

PostgreSQL的常見問題在觸及這個主題(參見:爲什麼我的查詢速度慢他們爲什麼不利用索引?):https://wiki.postgresql.org/wiki/FAQ#Why_are_my_queries_slow.3F_Why_don.27t_they_use_my_indexes.3F

+0

維基頁面非常棒。 – Benoit 2014-08-14 22:24:32

+0

最後一個鏈接被破壞 - 工作鏈接:https://wiki.postgresql.org/wiki/FAQ#Why_are_my_queries_slow.3F_Why_don.27t_they_use_my_indexes.3F – dmikam 2015-03-12 08:54:55

+0

非常大膽地開始回答這種免責聲明! – jwg2s 2016-01-27 23:33:56

3

Postgres的查詢優化器自帶了多種方案(例如, index vs seq-scan)並使用關於您的表的統計信息以及配置中設置的磁盤/內存/索引/表訪問的相關成本對它們進行評估。

您是否使用EXPLAIN命令查看爲什麼省略索引使用?您是否使用EXPLAIN ANALYZE來確定該決定是否有誤?我們能看到輸出嗎?

編輯:

硬如分析兩種不同的奇異查詢在不同的系統,我想我看到一對夫婦的事情。

生產環境的實際/成本率大約爲每個成本單元20-100毫秒。我甚至不是DBA,但這似乎是一致的。主查詢的開發環境有261個。這看起來正確嗎?你會期望生產環境的原始速度(內存/磁盤/ CPU)比dev快2-10倍嗎?

由於生產環境有一個更多複雜的查詢計劃,它看起來像它的工作。毫無疑問,開發環境的計劃和更多已被考慮,並認爲太昂貴。而20-100的差異並不是我的經驗(但同樣,不是DBA),並且表明沒有任何方法可言。儘管如此,您仍然可能需要在數據庫上運行VACUUM

我沒有足夠的經驗和耐心去解碼完整的查詢,但是有沒有優化的非規範化/ NOSQL化點?

最大的瓶頸似乎是在90 MB的磁盤合併。如果生產環境具有足夠的內存,則可能需要增加相關設置(工作內存?)以在內存中執行此操作。它似乎是work_mem參數here,雖然你會想通讀其餘的。

我也建議在index usage statisticslook。存在許多具有部分和功能指標的選項。

+0

用'EXPLAIN ANALYZE'輸出編輯我的問題。 – vonconrad 2012-02-28 05:37:43

3

試着在我看來

SET enable_seqscan TO 'off' 

EXPLAIN ANALYZE

+9

請注意,如果您解釋說這是一種故障排除技術,以確定查詢事實上是否會更快地使用索引掃描,而不是盲目應用於數據庫的修復程序,這將是一個更好的答案。 – 2012-02-28 15:21:27

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[])) 

這意味着,在開發27447匹配的行進行了估計和前期行16528的確被發現。這不是一回事,OK。

在生產中已經預測了317174個匹配行,並且發現了431770行。還行。

但是比較開發產品意味着數字是10倍不同。正如其他答案指出的那樣,執行10倍以上的隨機搜索(由於索引訪問)可能確實比普通的表掃描更糟糕。

因此有趣的問題是:該表包含dev和prod中的這個表有多少行? dev和prod之間是否可比較爲number_returned_rows/number_total_rows

編輯不要忘記:我選擇了一個索引訪問爲例。快速瀏覽顯示其他索引訪問具有相同的症狀。