PostgreSQL-使用索引进行非常慢的获取

PostgreSQL-使用索引进行非常慢的获取,sql,postgresql,Sql,Postgresql,我正在Centos 6.7上运行postgresql 9.4。其中一个表包含数百万条记录,这是DDL: CREATE TABLE domain.examples ( id SERIAL, sentence VARCHAR, product_id BIGINT, site_id INTEGER, time_stamp BIGINT, category_id INTEGER, CONSTRAINT examples_pkey PRIMARY KEY(id) ) WITH

我正在Centos 6.7上运行postgresql 9.4。其中一个表包含数百万条记录,这是DDL:

CREATE TABLE domain.examples (
  id SERIAL,
  sentence VARCHAR,
  product_id BIGINT,
  site_id INTEGER,
  time_stamp BIGINT,
  category_id INTEGER,
  CONSTRAINT examples_pkey PRIMARY KEY(id)
) 
WITH (oids = false);

CREATE INDEX examples_categories ON domain.examples
  USING btree (category_id);

CREATE INDEX examples_site_idx ON domain.examples
  USING btree (site_id);
使用数据的应用程序使用分页来完成这项工作,因此我们要获取大量的1000条记录。但是,即使通过索引列进行提取,提取时间也非常缓慢:

explain analyze
select *
from domain.examples e
where e.category_id = 105154
order by id asc 
limit 1000;

Limit  (cost=0.57..331453.23 rows=1000 width=280) (actual time=2248261.276..2248296.600 rows=1000 loops=1)
  ->  Index Scan using examples_pkey on examples e  (cost=0.57..486638470.34 rows=1468199 width=280) (actual time=2248261.269..2248293.705 rows=1000 loops=1)
        Filter: (category_id = 105154)
        Rows Removed by Filter: 173306740
Planning time: 70.821 ms
Execution time: 2248328.457 ms
是什么导致查询速度慢?如何改进


谢谢

这不是您想要的计划,postgresql正在扫描整个索引
examples\u pkey
并过滤出条件为
category\u id=105154
的记录,您可以尝试使用
ANALYZE
或使用系统GUCs(我确实不建议这样做)让计划员选择正确的索引

或者,如果
category\u id=105154
的行数不太高,我建议使用第一行,这样规划者就不得不使用
examples\u categories
索引

with favorite_category as (
    select *
    from domain.examples e
    where e.category_id = 105154)
select *
from favorite_category
order by id asc
limit 1000;

这将获取类别id=105154的所有记录,并按id进行内存排序(如果获取的大小小于您的工作内存,
show work\u mem;
,查看其大小。默认值为4MB)。

您可以在类别id和id两个字段上创建索引:

CREATE INDEX examples_site_idx2 ON domain.examples
  USING btree (category_id, id);
我尝试用300万行的查询进行解释分析

使用旧索引:

                                                                  QUERY PLAN                                                                  
----------------------------------------------------------------------------------------------------------------------------------------------
 Limit  (cost=0.43..9234.56 rows=1000 width=60) (actual time=0.655..597.193 rows=322 loops=1)
   ->  Index Scan using examples_pkey on examples e  (cost=0.43..138512.43 rows=15000 width=60) (actual time=0.654..597.142 rows=322 loops=1)
         Filter: (category_id = 105154)
         Rows Removed by Filter: 2999678
 Planning time: 2.295 ms
 Execution time: 597.257 ms
(6 rows)
新增索引:

                                                                   QUERY PLAN                                                                    
-------------------------------------------------------------------------------------------------------------------------------------------------
 Limit  (cost=0.43..2585.13 rows=1000 width=60) (actual time=0.027..28.814 rows=322 loops=1)
   ->  Index Scan using examples_site_idx2 on examples e  (cost=0.43..38770.93 rows=15000 width=60) (actual time=0.026..28.777 rows=322 loops=1)
         Index Cond: (category_id = 105154)
 Planning time: 1.471 ms
 Execution time: 28.860 ms
(5 rows)

所有那些
\u id
列都应该是外键吗?他们似乎没有被宣布为这样。句子中的东西有多大?您的缓存可能是冷的,或者服务器的磁盘过载。请再试一次。它们是,如果将被声明为这样,应该会提高性能吗?提取仅来自该表,不涉及连接。”语句“是非常短的字符串,并且一次又一次地查询结果,但性能都很差。您有有效的统计信息吗?--><代码>真空分析域。示例BTW是
e.category\u id
低基数列?@Seffy是的,所有外键都会自动索引。撇开性能不谈,您应该始终声明外键,以便数据库能够强制引用完整性,以便读取模式的人员和对象知道所有内容是如何连接的,因此,您可以利用这些关系来确保您不会离开孤立状态,或者通过类似于
删除时级联的方式来清理关系。顺便说一句:对于这个特定的查询,复合索引可能是有益的:
在域上创建唯一的索引示例\u类别。使用btree(category\u id,id)的示例
对于Postgres,CTE通常不是性能问题的解决方案。@wildplasser我同意!但是在这种情况下,如果category_id=105154的行数很小,我认为这是有意义的。另外,我很好奇,你能提供一个例子来支持你的主张吗?我不是说我不同意!在这种情况下,没有CTE的查询可能会受益更多。(CTE是optmiser的一个障碍)但是,在这种情况下,它可能不会有什么不同。谢谢!作为魅力的作品:-)