什么时候使用多列索引?
When to use multi column indexes?
我有以下 table 大约有 1000 万行
CREATE TABLE "autocomplete_books"
(
id uuid PRIMARY KEY DEFAULT uuid_generate_v4 (),
author_id uuid NOT NULL REFERENCES "author"(id) ON DELETE CASCADE,
language VARCHAR(30) NOT NULL,
name VARCHAR(100) NOT NULL,
importance_rank SMALLINT NOT NULL DEFAULT 1
);
我有以下查询
SELECT DISTINCT ON (author_id)
author_id,
similarity(name, ) as score,
language, name, importance_rank
FROM
"autocomplete_books"
WHERE
% name AND language IN (, , )
ORDER BY
author_id, score DESC, importance_rank DESC
LIMIT
10
我主要查询相似性,因为这是一个自动完成端点,所以我在名称上有一个三元组索引。我也在对其他一些领域进行排序。我不确定 score
字段将如何与我的其他索引混合,以及是否有一个像这样的复合索引更好
选项 1
CREATE INDEX ON "autocomplete_books" USING GIN (name gin_trgm_ops);
CREATE INDEX ON "autocomplete_books" USING BTREE (author_id, language, importance_rank DESC);
或者如果我应该像这样把它们分解出来
选项 2
CREATE INDEX ON "autocomplete_books" USING GIN (name gin_trgm_ops);
CREATE INDEX ON "autocomplete_books" USING BTREE (author_id, language, importance_rank DESC);
CREATE INDEX ON "autocomplete_books" USING BTREE (language);
CREATE INDEX ON "autocomplete_books" USING BTREE (importance_rank DESC);
这是 explain analyze
运行 在 220k 行上的输出,索引如下
CREATE INDEX ON "autocomplete_books" USING BTREE (author_id, language);
CREATE INDEX ON "autocomplete_books" USING BTREE (importance_rank DESC);
-
Limit (cost=762.13..762.38 rows=50 width=82) (actual time=12.230..13.024 rows=50 loops=1)
-> Unique (cost=762.13..763.23 rows=217 width=82) (actual time=12.223..12.686 rows=50 loops=1)
-> Sort (cost=762.13..762.68 rows=220 width=82) (actual time=12.216..12.373 rows=50 loops=1)
Sort Key: author_id, ((similarity((name)::text, \'sale\'::text)) DESC, importance_rank DESC
Sort Method: quicksort Memory: 45kB
-> Bitmap Heap Scan on "books_autocomplete" mat (cost=45.71..753.57 rows=220 width=82) (actual time=1.905..11.610 rows=149 loops=1)
Recheck Cond: (\'sale\'::text % (name)::text)
Rows Removed by Index Recheck: 2837
Filter: ((language)::text = ANY (\'{language1,language2,language3}\'::text[]))
Heap Blocks: exact=2078
-> Bitmap Index Scan on "books_autocomplete_name_idx" (cost=0.00..45.65 rows=220 width=0) (actual time=1.551..1.557 rows=2986 loops=1)
Index Cond: (\'sale\'::text % (name)::text)
Planning time: 13.976 ms
Execution time: 13.545 ms'
只有当 ORDER BY
子句中的 所有 表达式都在索引中时,索引才会帮助您进行排序,而您不能这样做,因为第二个表达式。
此外,只有 b-tree 索引可用于支持 ORDER BY
。现在当你想使用 ORDER BY
时你不能组合多个索引,你说 % name
是你最有选择性的标准,所以你可能想在上面使用索引。
此查询可以采用两种方式:
使用 name
上的三元组 GIN 索引寻找 % name
条件。
这就是您问题中的执行计划所做的。
然后你将不得不忍受那个 Sort
,因为你不能为它使用索引。这里的危险是位图索引扫描会找到太多的行,以至于位图堆扫描非常昂贵。
如果存在与ORDER BY
子句完全匹配的索引:
CREATE INDEX ON autocomplete_books
(author_id, score DESC, importance_rank DESC);
您可以按 ORDER BY
的顺序扫描索引并获取行,直到有 10 个匹配过滤条件 % name
。这里的危险是找到 10 行可能需要比预期更长的时间。
首先尝试仅使用一个索引,然后仅使用另一个索引,然后 运行 在实际大小的数据集上使用不同参数进行查询,以查看最佳效果。
除这两个索引外,您应该删除所有其他索引,因为它们对这个查询没有任何好处。
如果两种策略中的一种明显胜出,则删除另一个索引,这样优化器就不会使用它。否则保留两者并希望优化器根据参数选择正确的。
我有以下 table 大约有 1000 万行
CREATE TABLE "autocomplete_books"
(
id uuid PRIMARY KEY DEFAULT uuid_generate_v4 (),
author_id uuid NOT NULL REFERENCES "author"(id) ON DELETE CASCADE,
language VARCHAR(30) NOT NULL,
name VARCHAR(100) NOT NULL,
importance_rank SMALLINT NOT NULL DEFAULT 1
);
我有以下查询
SELECT DISTINCT ON (author_id)
author_id,
similarity(name, ) as score,
language, name, importance_rank
FROM
"autocomplete_books"
WHERE
% name AND language IN (, , )
ORDER BY
author_id, score DESC, importance_rank DESC
LIMIT
10
我主要查询相似性,因为这是一个自动完成端点,所以我在名称上有一个三元组索引。我也在对其他一些领域进行排序。我不确定 score
字段将如何与我的其他索引混合,以及是否有一个像这样的复合索引更好
选项 1
CREATE INDEX ON "autocomplete_books" USING GIN (name gin_trgm_ops);
CREATE INDEX ON "autocomplete_books" USING BTREE (author_id, language, importance_rank DESC);
或者如果我应该像这样把它们分解出来
选项 2
CREATE INDEX ON "autocomplete_books" USING GIN (name gin_trgm_ops);
CREATE INDEX ON "autocomplete_books" USING BTREE (author_id, language, importance_rank DESC);
CREATE INDEX ON "autocomplete_books" USING BTREE (language);
CREATE INDEX ON "autocomplete_books" USING BTREE (importance_rank DESC);
这是 explain analyze
运行 在 220k 行上的输出,索引如下
CREATE INDEX ON "autocomplete_books" USING BTREE (author_id, language);
CREATE INDEX ON "autocomplete_books" USING BTREE (importance_rank DESC);
-
Limit (cost=762.13..762.38 rows=50 width=82) (actual time=12.230..13.024 rows=50 loops=1)
-> Unique (cost=762.13..763.23 rows=217 width=82) (actual time=12.223..12.686 rows=50 loops=1)
-> Sort (cost=762.13..762.68 rows=220 width=82) (actual time=12.216..12.373 rows=50 loops=1)
Sort Key: author_id, ((similarity((name)::text, \'sale\'::text)) DESC, importance_rank DESC
Sort Method: quicksort Memory: 45kB
-> Bitmap Heap Scan on "books_autocomplete" mat (cost=45.71..753.57 rows=220 width=82) (actual time=1.905..11.610 rows=149 loops=1)
Recheck Cond: (\'sale\'::text % (name)::text)
Rows Removed by Index Recheck: 2837
Filter: ((language)::text = ANY (\'{language1,language2,language3}\'::text[]))
Heap Blocks: exact=2078
-> Bitmap Index Scan on "books_autocomplete_name_idx" (cost=0.00..45.65 rows=220 width=0) (actual time=1.551..1.557 rows=2986 loops=1)
Index Cond: (\'sale\'::text % (name)::text)
Planning time: 13.976 ms
Execution time: 13.545 ms'
只有当 ORDER BY
子句中的 所有 表达式都在索引中时,索引才会帮助您进行排序,而您不能这样做,因为第二个表达式。
此外,只有 b-tree 索引可用于支持 ORDER BY
。现在当你想使用 ORDER BY
时你不能组合多个索引,你说 % name
是你最有选择性的标准,所以你可能想在上面使用索引。
此查询可以采用两种方式:
使用
name
上的三元组 GIN 索引寻找% name
条件。 这就是您问题中的执行计划所做的。 然后你将不得不忍受那个Sort
,因为你不能为它使用索引。这里的危险是位图索引扫描会找到太多的行,以至于位图堆扫描非常昂贵。如果存在与
ORDER BY
子句完全匹配的索引:CREATE INDEX ON autocomplete_books (author_id, score DESC, importance_rank DESC);
您可以按
ORDER BY
的顺序扫描索引并获取行,直到有 10 个匹配过滤条件% name
。这里的危险是找到 10 行可能需要比预期更长的时间。
首先尝试仅使用一个索引,然后仅使用另一个索引,然后 运行 在实际大小的数据集上使用不同参数进行查询,以查看最佳效果。
除这两个索引外,您应该删除所有其他索引,因为它们对这个查询没有任何好处。
如果两种策略中的一种明显胜出,则删除另一个索引,这样优化器就不会使用它。否则保留两者并希望优化器根据参数选择正确的。