PostgreSQL:以主键作为排序键的排序速度非常慢

PostgreSQL:以主键作为排序键的排序速度非常慢,sql,postgresql,postgresql-performance,Sql,Postgresql,Postgresql Performance,我有一个这样的模型 具有以下表格大小: +------------------+-------------+ | Table | Records | +------------------+-------------+ | JOB | 8k | | DOCUMENT | 150k | | TRANSLATION_UNIT | 14,5m | | TRANSLATION

我有一个这样的模型

具有以下表格大小:

+------------------+-------------+
| 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 BYLIMIT子句时,需要19.5秒ANALYZE在执行查询之前已在所有表上运行

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

+------------------+-------------+
| Table            |     Records |
+------------------+-------------+
| JOB              |          1  |
| DOCUMENT         |       1200  |
| TRANSLATION_UNIT |    210,000  |
| TRANSLATION      |    210,000  |
+------------------+-------------+
查询计划:

不带
订单和限额的修改查询计划为

数据库参数:

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
有人知道这个问题出了什么问题吗


更新:对于相同的查询,没有ORDER BY(但仍然有LIMIT子句)。

您是否有一个关于翻译的复合索引(fk\U id\U翻译单位,id)?在我看来,这将有助于避免通过表访问translation.id。

这对于注释来说有点太长了。删除
orderby
子句时,您正在比较苹果和橙子。如果没有排序依据,查询的处理部分只需要列出50行

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

translation.id
是主键这一事实没有什么区别,因为处理需要经过几个连接(过滤结果)

编辑:

我想知道如何使用CTE首先创建表,然后使用另一个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;

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

就像这样:

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

乐观主义者是如何为Postgre工作的?例如,你能从你的选择中选择,并且在没有乐观主义者知道它是两便士的情况下订购这个吗?这只是一个幸运的猜测。可以尝试移动联接中的where子句吗?在这种情况下,只需将单词
where
替换为
@foibs:这不会有任何区别。Postgres优化器足够聪明,可以检测到两个版本是相同的。@twoflower:您是否也可以在没有
order by的情况下发布执行计划(最快的一个)?理想情况下,将其上载到计算机,这样可读性会更好。@foibs结果完全相同。您是指组合了
fk\u id\u translation\u单元
id
列的复合索引?我不需要,但可能会尝试一下。我看到的好处是,为了检索所需的数据,根本不需要访问翻译表本身。确实如此,因为我需要在结果集中使用
translation.id
。PostgreSQL性能论坛上的一个家伙刚刚建议对数据库进行非规范化,并将
fk_id_job
直接添加到
TRANSLATION
。不过,这应该可以从索引中访问,而无需访问表。我明白了。实际查询从
TRANSLATION
获取所有列。没错,戈登,这两个查询是不可比较的。我刚刚运行了这个查询,没有使用
orderby
LIMIT
,它需要19.5秒。查询计划是@twoflower。你有大量的数据。我不太熟悉Postgres的优化参数,但如果你能增加缓冲区大小以使用更多内存,你可能会看到性能的提高。是的,卷相当大,这就是为什么我对提取所有记录需要19.5秒并不感到惊讶。然而,让我感到奇怪的是,仅仅对这个数据集(cca 212000记录)进行排序就增加了70秒。我现在尝试了您建议的CTE方法,它运行了20-30秒,比原始查询快得多。非常感谢你。然而,在一个小5倍(即40000条记录)的数据集上,需要120ms(即快240倍)的时间,这是否正常。数据库性能取决于许多因素。特别是,一个关键因素是可以在内存中进行的处理的比例。随着磁盘的使用,性能会急剧下降。您可能需要为各种缓冲区分配更多内存以提高性能。