PostgreSQL:非常慢的ORDER BY,主键作为排序键

时间:2013-11-04 14:31:22

标签: sql postgresql postgresql-performance

我有这样的模型

enter image description here

具有以下表格大小:

+------------------+-------------+
| Table            |    Records  |
+------------------+-------------+
| JOB              |         8k  |
| DOCUMENT         |       150k  |
| TRANSLATION_UNIT |      14,5m  |
| TRANSLATION      |      18,3m  |
+------------------+-------------+

现在进行以下查询

select translation.id
from "TRANSLATION" translation
   inner join "TRANSLATION_UNIT" unit
     on translation.fk_id_translation_unit = unit.id
   inner join "DOCUMENT" document
     on unit.fk_id_document = document.id     
where document.fk_id_job = 11698
order by translation.id asc
limit 50 offset 0

需要 90秒才能完成。当我删除 ORDER BY LIMIT 子句时,它需要 19.5秒。在执行查询之前,已在所有表上运行 ANALYZE

对于此特定查询,这些是满足条件的记录数:

+------------------+-------------+
| Table            |     Records |
+------------------+-------------+
| JOB              |          1  |
| DOCUMENT         |       1200  |
| TRANSLATION_UNIT |    210,000  |
| TRANSLATION      |    210,000  |
+------------------+-------------+

查询计划:

enter image description here

没有 ORDER BY LIMIT 的修改的查询计划是here

数据库参数:

PostgreSQL 9.2

shared_buffers = 2048MB
effective_cache_size = 4096MB
work_mem = 32MB

Total memory: 32GB
CPU: Intel Xeon X3470 @ 2.93 GHz, 8MB cache

有人能看到这个查询有什么问题吗?

更新Query plan用于没有 ORDER BY 的相同查询(但仍然使用 LIMIT 子句)。

3 个答案:

答案 0 :(得分:2)

评论时间太长了。删除order by子句时,您正在比较苹果和橙子。如果没有order by,查询的处理部分只需要提供50行。

使用order by,所有行都需要在排序之前生成,并选择前几行。如果删除order by limit子句,查询需要多长时间?

translation.id是主键的事实并没有什么区别,因为处理需要经过多次连接(过滤结果)。

编辑:

我想知道如何使用CTE首先创建表,然后另一个用于排序和获取结果:

with CTE as (
     select translation.id
     from "TRANSLATION" translation
          inner join "TRANSLATION_UNIT" unit
          on translation.fk_id_translation_unit = unit.id
          inner join "DOCUMENT" document
          on unit.fk_id_document = document.id     
     where document.fk_id_job = 11698
    )
select *
from CTE
order by translation.id asc
limit 50 offset 0;

答案 1 :(得分:1)

翻译时是否有复合索引(fk_id_translation_unit,id)?在我看来,这将有助于避免通过表格访问translation.id的需要。

答案 2 :(得分:1)

如果有人遇到同样的问题。它发生在我身上,我通过将索引更改为有序索引来解决它。索引通过列 ID(PK 列)和顺序方向进行扩展。

像这样:

create index index_name on SCHEMA.TABLE (id asc, (sent_time IS NULL), some_id_ref, type);