【问题标题】:Postgres: Sorting by an immutable function index doesn't use indexPostgres:按不可变函数索引排序不使用索引
【发布时间】:2016-01-24 12:59:20
【问题描述】:

我有一张简单的桌子。

CREATE TABLE posts
(
    id uuid NOT NULL,
    vote_up_count integer,
    vote_down_count integer,
    CONSTRAINT post_pkey PRIMARY KEY(id)
);

我有一个 IMMUTABLE 函数,它可以进行简单(但可能很复杂)的算术运算。

CREATE OR REPLACE FUNCTION score(
    ups integer,
    downs integer)
  RETURNS integer AS
$BODY$
    select $1 - $2
$BODY$
  LANGUAGE sql IMMUTABLE
  COST 100;
ALTER FUNCTION score(integer, integer)
  OWNER TO postgres;

我在使用我的函数的posts 表上创建了一个索引。

CREATE INDEX posts_score_index ON posts(score(vote_up_count, vote_down_count), date_created);

当我EXPLAIN下面的查询时,好像没有使用索引。

SELECT * FROM posts ORDER BY score(vote_up_count, vote_down_count), date_created

Sort  (cost=1.02..1.03 rows=1 width=310)
  Output: id, date_created, last_edit_date, slug, sub_id, user_id, user_ip, type, title, content, url, domain, send_replies, vote_up_count, vote_down_count, verdict, approved_by, removed_by, verdict_message, number_of_reports, ignore_reports, number_of_com (...)"
  Sort Key: ((posts.vote_up_count - posts.vote_down_count)), posts.date_created
  ->  Seq Scan on public.posts  (cost=0.00..1.01 rows=1 width=310)
        Output: id, date_created, last_edit_date, slug, sub_id, user_id, user_ip, type, title, content, url, domain, send_replies, vote_up_count, vote_down_count, verdict, approved_by, removed_by, verdict_message, number_of_reports, ignore_reports, number_ (...)

如何让我的 ORDER BY 使用来自 IMMUTABLE 函数的索引,该函数可能有一些非常复杂的算术?

编辑:根据@Егор-Рогов 的建议,我稍微更改了查询,看看是否可以让它使用索引。还是没有运气。

set enable_seqscan=off;
EXPLAIN VERBOSE select date_created from posts ORDER BY (hot(vote_up_count, vote_down_count, date_created),date_created);

这是输出。

Sort  (cost=10000000001.06..10000000001.06 rows=1 width=16)
  Output: date_created, (ROW(round((((log((GREATEST(abs((vote_up_count - vote_down_count)), 1))::double precision) * sign(((vote_up_count - vote_down_count))::double precision)) + ((date_part('epoch'::text, date_created) - 1134028003::double precision) / 4 (...)
  Sort Key: (ROW(round((((log((GREATEST(abs((posts.vote_up_count - posts.vote_down_count)), 1))::double precision) * sign(((posts.vote_up_count - posts.vote_down_count))::double precision)) + ((date_part('epoch'::text, posts.date_created) - 1134028003::dou (...)
  ->  Seq Scan on public.posts  (cost=10000000000.00..10000000001.05 rows=1 width=16)
        Output: date_created, ROW(round((((log((GREATEST(abs((vote_up_count - vote_down_count)), 1))::double precision) * sign(((vote_up_count - vote_down_count))::double precision)) + ((date_part('epoch'::text, date_created) - 1134028003::double precision (...)

EDIT2:我似乎没有使用索引,因为 date_created 的第二次排序。

【问题讨论】:

  • 嗯,您正在从表中读取 所有 行。索引主要用于减少行数。它很少用于支持排序。如果您需要一个包含您选择的所有列的覆盖索引。

标签: postgresql indexing sql-order-by


【解决方案1】:

我可以看到一些不鼓励计划者使用索引的点。

1。 查看解释输出中的这一行:

Seq Scan on public.posts  (cost=0.00..1.01 rows=1 width=310)

它表示计划者认为表中只有一行。在这种情况下,使用索引扫描是没有意义的,因为顺序扫描更快。

尝试向表中添加更多行,执行analyze 并重试。您也可以通过set enable_seqscan=off; 暂时禁用顺序扫描来测试它。

2。 您使用您的功能对结果进行排序。因此规划器可能决定使用索引来以正确的顺序获取元组 ID。但是它需要从表中获取每个元组以获取所有列的值(因为select *)。

您可以通过向其添加所有必要的列来使索引对规划器更具吸引力,这可以避免表扫描。这称为仅索引扫描

CREATE INDEX posts_score_index ON posts(
  score(vote_up_count, vote_down_count),
  date_created,
  id,             -- do you actually need it in result set?
  vote_up_count,  -- do you actually need it in result set?
  vote_down_count -- do you actually need it in result set?
);

并确保在插入/更新/删除行后运行vacuum 以更新visibility map

当然,缺点是增加了索引大小。

【讨论】:

  • 我刚刚更新了我的问题。查询set enable_seqscan=off; EXPLAIN VERBOSE select date_created from posts ORDER BY (hot(vote_up_count, vote_down_count, date_created),date_created); 仍然没有使用索引。
  • 这里的 hot() 是什么?请提供所有脚本以重现该问题。您的原始查询使用索引,我对此进行了测试。
  • 对不起。我有多个使用索引的函数。我把它们混在一起。都是算术。
  • 更新:似乎我的索引没有被使用,因为我的查询有第二个 order by 子句。我删除了第二个date_created order by,它现在正在使用索引。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-11-07
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多