16

我定义了下表和索引:

CREATE TABLE ticket (
  wid bigint NOT NULL DEFAULT nextval('tickets_id_seq'::regclass),
  eid bigint,
  created timestamp with time zone NOT NULL DEFAULT now(),
  status integer NOT NULL DEFAULT 0,
  argsxml text,
  moduleid character varying(255),
  source_id bigint,
  file_type_id bigint,
  file_name character varying(255),
  status_reason character varying(255),
  ...
)

我在时间戳上创建了一个索引,created如下所示:

CREATE INDEX ticket_1_idx
  ON ticket
  USING btree
  (created );

这是我的查询:

select * from ticket 
where created between '2012-12-19 00:00:00' and  '2012-12-20 00:00:00'

在记录数量开始增长(大约 500 万条)之前,这一直运行良好,现在它需要永远返回。

解释分析揭示了这一点:

Index Scan using ticket_1_idx on ticket  (cost=0.00..10202.64 rows=52543 width=1297) (actual time=0.109..125.704 rows=53340 loops=1)
  Index Cond: ((created >= '2012-12-19 00:00:00+00'::timestamp with time zone) AND (created <= '2012-12-20 00:00:00+00'::timestamp with time zone))
Total runtime: 175.853 ms

到目前为止,我已经尝试过设置:

random_page_cost = 1.75 
effective_cache_size = 3 

还创建了:

create CLUSTER ticket USING ticket_1_idx;

没有任何效果。我究竟做错了什么?为什么选择顺序扫描?索引应该使查询快速。有什么可以优化的吗?

4

1 回答 1

29

CLUSTER

如果您打算使用CLUSTER,则显示的语法无效。

create CLUSTER ticket USING ticket_1_idx;

运行一次:

CLUSTER ticket USING ticket_1_idx;

更大的结果集有很大帮助。对于返回的单行或几行而言,并没有那么多。
Postgres 记住用于后续调用的索引。如果您的表不是只读的,则效果会随着时间的推移而恶化,您需要以一定的时间间隔重新运行:

CLUSTER ticket;

可能仅在易失性分区上。见下文。

但是,如果您有很多更新,CLUSTER(或VACUUM FULL)实际上可能对性能不利。适量的膨胀允许UPDATE将新的行版本放置在同一数据页上,并避免过于频繁地物理扩展底层物理文件的需要。您可以使用经过仔细调整FILLFACTOR的方式来获得两全其美:

pg_repack/pg_squeeze

CLUSTER对表进行排他锁,这在多用户环境中可能是个问题。引用手册:

当一个表被集群时,ACCESS EXCLUSIVE会在它上面获取一个锁。这可以防止任何其他数据库操作(读取和写入)在完成之前对表进行操作CLUSTER

大胆强调我的。考虑替代方案!

pg_repack

与它不同CLUSTERVACUUM FULL是它可以在线工作,在处理过程中不会对已处理的表持有独占锁。pg_repack 启动效率很高,性能堪比CLUSTER直接使用。

和:

pg_repack 需要在重组结束时采取排他锁。

当前版本 1.4.7 适用于 PostgreSQL 9.4 - 14。

pg_squeeze是一种较新的替代方案,它声称:

事实上,我们尝试替换pg_repack扩展名。

当前版本 1.4 适用于 Postgres 10 - 14。

询问

查询很简单,不会导致任何性能问题。

然而,关于正确性的一句话:BETWEEN结构包括边界。您的查询选择 12 月 19 日的所有记录,以及12 月 20 日 00:00 时的记录。这是一个极不可能的要求。很有可能,你真的想要:

SELECT *
FROM   ticket 
WHERE  created >= '2012-12-19 0:0'
AND    created <  '2012-12-20 0:0';

表现

首先,你问:

为什么选择顺序扫描?

您的EXPLAIN输出清楚地显示了Index Scan,而不是顺序表扫描。一定是有什么误解。

您也许能够提高性能,但必要的背景信息不存在问题。可能的选项包括:

  • 仅查询所需的列,而不是*降低传输成本(和其他性能优势)。

  • 查看分区并将实际时间片放入单独的表中。根据需要向分区添加索引。

  • 如果分区不是一种选择,另一种相关但侵入性较小的技术是添加一个或多个部分索引
    例如,如果您主要查询当前月份,则可以创建以下部分索引:

      CREATE INDEX ticket_created_idx ON ticket(created)
      WHERE created >= '2012-12-01 00:00:00'::timestamp;
    

    CREATE新月开始前的新指数。您可以使用 cron 作业轻松地自动执行任务。可以选择DROP为过去几个月的部分索引。

    保留索引CLUSTER(不能对部分索引进行操作)。如果旧记录永远不会改变,表分区将对这项任务有很大帮助,因为您只需要重新集群新分区。
    再说一次,如果记录根本不改变,你可能不需要CLUSTER.

性能基础

您可能缺少其中一项基础知识。所有通常的性能建议都适用:

于 2012-12-23T01:28:26.617 回答