使用 ORDER BY 日期和文本优化简单查询

Aeg*_*gis 4 postgresql performance index query-performance

我有一个基于日期字段和数字字段的订单查询 Postgres 中的表,该表有 1000000 条记录

表的数据类型为:

fcv_id = serial
fcv_fecha_comprobante = timestamp without time zone
fcv_numero_comprobante = varchar(60)
Run Code Online (Sandbox Code Playgroud)

查询是:

SELECT fcv_id, fcv_fecha_comprobante FROM factura_venta
ORDER BY fcv_fecha_comprobante, fcv_numero_comprobante
Run Code Online (Sandbox Code Playgroud)

这个查询大约需要 5 秒,但如果我取出“order by”,查询只需要 0.499 秒

我遇到的问题是我需要在尽可能短的时间内运行这个查询,所以我在谷歌上搜索我可以做什么并使用以下查询创建一个复合索引

CREATE INDEX factura_venta_orden ON factura_venta
USING btree (fcv_fecha_comprobante ASC NULLS LAST
           , fcv_numero_comprobante ASC NULLS LAST);
ALTER TABLE factura_venta CLUSTER ON factura_venta_orden;
Run Code Online (Sandbox Code Playgroud)

但是查询花费的时间相同甚至更多。

我使用的是 Postgres 9.0.13,这里是 73436 行的 EXPLAIN

Sort  (cost=11714.03..11897.62 rows=73436 width=27) (actual time=1260.759..1579.853 rows=73436 loops=1)
  Sort Key: fcv_fecha_comprobante, fcv_numero_comprobante
  Sort Method:  external merge  Disk: 2928kB
  ->  Seq Scan on factura_venta  (cost=0.00..4018.36 rows=73436 width=27) (actual time=0.363..162.558 rows=73436 loops=1)
Total runtime: 1694.882 ms
Run Code Online (Sandbox Code Playgroud)

Postgres 运行在具有 8 GB 内存和 500 GB 磁盘的 Phenon II 1055T(3 核)上。

如何优化此查询?

Erw*_*ter 7

根据您对已删除帖子的评论,您将所有行加载到一个 java 模块中以在那里进行搜索。但是搜索最好在数据库本身中完成 - 这就是数据库的擅长之处。只返回您实际需要的行。

如果你真的需要所有的行,有很多小东西可以使它更快。但是,1M 行永远不会很快。

Postgres 9.2 或更高版本

您可以通过附加来使索引覆盖fcv_id

CREATE INDEX factura_venta_orden
ON factura_venta (fcv_fecha_comprobante, fcv_numero_comprobante, fcv_id);
Run Code Online (Sandbox Code Playgroud)

这样,只要表没有更新太多,Postgres 就可以通过仅索引扫描来检索结果。

附加列排在最后,因为它不影响排序顺序。解释:

在 Postgres 11或更高版本中,您可以这样做:

CREATE INDEX factura_venta_orden
ON factura_venta (fcv_fecha_comprobante, fcv_numero_comprobante) INCLUDE (fcv_id);
Run Code Online (Sandbox Code Playgroud)

CLUSTER / pg_repack

我看你已经找到了CLUSTER。您知道这是一次性操作,应该有助于您的事业,但需要在足够的更新后重新运行?

还有社区工具pg_repack作为VACUUM FULL/ 的替代品CLUSTER

work_mem

EXPLAIN输出中的这一行:

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

告诉我们,排序不是在 RAM 中完成的,这很昂贵。您可能可以通过调整相应的设置来提高性能work_mem

work_mem (integer)

指定在写入临时磁盘文件之前由内部排序操作和哈希表使用的内存量。...

将此值设置得太高可能会产生不利影响。仔细阅读说明书。考虑仅针对具有大查询的事务增加设置

BEGIN;
SET LOCAL work_mem  = '50MB';
SELECT ...;
COMMIT;
Run Code Online (Sandbox Code Playgroud)

50 MB 是基于您EXPLAIN ANALYZE的 73k 行输出的估计值。测试 100 万行以获得您需要的实际数量。


dez*_*zso 6

您的查询的基本问题是它没有WHERE子句。通常,当 PostgreSQL 必须检索表中的所有(或至少大部分)行时,它将执行顺序扫描 - 仅仅是因为它比首先访问索引、定位所有行然后获取所有行要便宜他们从桌子上不管。

如果您碰巧使用了 9.2 版,则覆盖索引可能会有所帮助(如果它的列比表本身少得多)。

这个索引可能看起来像

CREATE INDEX factura_venta_orden
ON factura_venta USING btree (fcv_fecha_comprobante, fcv_numero_comprobante, fcv_id);
Run Code Online (Sandbox Code Playgroud)

但最大的问题是这仍然会相对缓慢,因为该索引的 100 万行占用了 100 MB。如果它没有被缓存,它在访问时必须从磁盘读取,并且 - 取决于你的 I/O 子系统 - 这可能会很慢。

查询中真正缓慢的部分是排序。查询计划显示磁盘上的外部合并排序。通过增加work_mem超过 3 MB(为了安全起见,让它为 10 MB),这可以很容易地推入内存。我希望这至少比你上面显示的 1.5 减少一秒钟。