由于临时文件而导致查询性能下降?

bnu*_*sey 9 postgresql performance configuration index-tuning query-performance

这是查询:

SELECT "products".* 
FROM "products"
WHERE (status > 100) 
AND "products"."above_revenue_average" = 't' 
AND ("products"."category_id" NOT IN (5))
ORDER BY "products"."start_date" DESC 
Run Code Online (Sandbox Code Playgroud)

我在status和上有一个索引start_date

每次从我的应用程序运行查询时,我都会在日志中得到以下信息:

SELECT "products".* 
FROM "products"
WHERE (status > 100) 
AND "products"."above_revenue_average" = 't' 
AND ("products"."category_id" NOT IN (5))
ORDER BY "products"."start_date" DESC 
Run Code Online (Sandbox Code Playgroud)

我相信这个临时文件的创建是性能缓慢的原因。

运行一个EXPLAIN ANALYZE我得到以下结果:

[WHITE] temporary file:
path "pg_tblspc/16386/PG_9.3_201306121/pgsql_tmp/pgsql_tmp2544.0", size 37093376 
Query: SELECT "products".* FROM "products"  WHERE (status > 100) 
AND "products"."above_revenue_average" = 't' 
AND ("products"."category_id" NOT IN (5))  ORDER BY "products"."start_date" DESC 
Run Code Online (Sandbox Code Playgroud)

然后我使用http://explain.depesz.com/使其更具可读性:

每个节点类型的统计信息

QUERY PLAN
 Sort  (cost=63395.28..63403.51 rows=16460 width=524)
       (actual time=524.134..562.635 rows=65294 loops=1)
   Sort Key: start_date
   Sort Method: external merge  Disk: 36224kB
   ->  Bitmap Heap Scan on products  
        (cost=4803.40..60389.73 rows=16460 width=524)
        (actual time=27.390..397.879 rows=65294 loops=1)
         Recheck Cond: (status > 100)
         Filter: (above_revenue_average AND (category_id <> 5))
         Rows Removed by Filter: 25115
         ->  Bitmap Index Scan on index_products_on_status
            (cost=0.00..4802.58 rows=89662 width=0) 
            (actual time=18.006..18.006 rows=90411 loops=1)
               Index Cond: (status > 100)
 Total runtime: 577.870 ms
(10 rows)
Run Code Online (Sandbox Code Playgroud)

每桌统计

+-------------------+-------+--------------+------------+
|     node type     | count | sum of times | % of query |
+-------------------+-------+--------------+------------+
| Bitmap Heap Scan  |     1 | 379.873 ms   | 67.5 %     |
| Bitmap Index Scan |     1 | 18.006 ms    | 3.2 %      |
| Sort              |     1 | 164.756 ms   | 29.3 %     |
+-------------------+-------+--------------+------------+
Run Code Online (Sandbox Code Playgroud)

我可以通过添加更多索引来提高数据库性能吗?也许一些复合的?有任何想法吗?

Erw*_*ter 6

work_mem

显然,排序操作会溢出到磁盘:

Sort Method: external merge  Disk: 36224kB
Run Code Online (Sandbox Code Playgroud)

更多work_mem可以帮助查询,就像@Kassandry 已经建议的那样。增加设置,直到您在输出中看到Memory代替。但根据一个查询增加常规设置可能不是一个好主意。正确的设置取决于可用的 RAM 和您的整体情况。首先阅读Postgres WikiDiskEXPLAIN

要修复您的查询,请仅在同一事务work_mem中为您的事务设置足够高的值。SET LOCAL

BEGIN;

SET LOCAL work_mem = '45MB';

SELECT ...

COMMIT;   -- or ROLLBACK
Run Code Online (Sandbox Code Playgroud)

您可能需要 40 MB 多一点。内存中的表示比磁盘上的表示要大一些。有关的:

询问

您的查询(消除一些噪音后):

SELECT * 
FROM   products
WHERE  status > 100
AND    above_revenue_average  -- boolean can be used directly
AND    category_id <> 5
ORDER  BY start_date DESC;
Run Code Online (Sandbox Code Playgroud)

您的行宽为半千字节 ( width=524)。您需要返回所有列吗?(通常情况下,您不需要。)仅在SELECT列表中列出查询中需要的列,以提高整体性能,尤其是在您work_mem已经遇到问题的情况下。

所涉及的任何列都可以为 NULL 吗?category_id对于和尤为重要start_date。在这种情况下你可能想要适应......

指数

多列索引当然可以提高性能。(就像@Paul 概述的那样)。你必须权衡成本和收益。如果此查询的性能很重要或者很常见,那就去做吧。不要为每个查询创建特殊索引。尽可能少,必要时多。共享索引时功能更强大,这增加了更多索引保留在缓存中的机会。

像这样的booleanabove_revenue_average是部分索引中条件的典型候选者,而不是索引列。

根据不完整的信息我的疯狂猜测:

CREATE INDEX prod_special_idx ON products (start_date DESC)
WHERE  above_revenue_average
AND    status > 100
AND    category_id <> 5;
Run Code Online (Sandbox Code Playgroud)

如果可以为 NULL,DESC NULLS LAST则在索引和查询中使用。start_date