2

我是 Postgres 的新手,我正在尝试更多地了解索引。我使用的是 12.5 版,这是我的代码:

CREATE TABLE textfun(content TEXT);
CREATE UNIQUE INDEX text_b ON textfun(content);

INSERT INTO textfun (content)
SELECT (CASE WHEN (random()<=0.3) THEN 'https://mywebsite/nanana/'
WHEN (random()<=0.6) THEN 'https://mywebsite/friendy/'
ELSE 'https://mywebsite/mina/' END) || generate_series(1000000,2000000);

在这里,我创建了一百万条记录,希望看到索引的效果。

当我尝试获取查询计划时:

explain analyze
SELECT content FROM textfun WHERE content LIKE 'mina%'; 

我得到了这个:

Gather  (cost=1000.00..14300.34 rows=100 width=32) (actual time=77.574..80.054 rows=0 loops=1)
  Workers Planned: 2    
  Workers Launched: 2
  Parallel Seq Scan on textfun  (cost=0.00..13290.34 rows=42 width=32) (actual time=69.022..69.022 rows=0 loops=3)
     Filter: (content ~~ 'mina%'::text)
     Rows Removed by Filter: 333334  
Planning Time: 0.254 ms  
Execution Time: 80.071 ms 
(8 rows)

我期待一个并行索引扫描。

我试过了:

explain analyze
SELECT content FROM textfun WHERE content LIKE '1500000%';

和:

explain analyze
SELECT content FROM textfun WHERE content LIKE '%mina';

但两者都给了我一个顺序扫描计划。

这里有没有我遗漏的细节,为什么我没有得到索引扫描?

4

2 回答 2

4

要支持 LIKE 条件,您需要使用text_pattern_ops创建索引

CREATE UNIQUE INDEX text_b ON textfun(content text_pattern_ops);

这样,结果是以下执行计划:

Bitmap Heap Scan on textfun  (cost=191.68..7654.53 rows=5000 width=32) (actual time=2.553..2.554 rows=0 loops=1)
  Filter: (content ~~ '1500000%'::text)
  ->  Bitmap Index Scan on text_b  (cost=0.00..190.43 rows=5000 width=0) (actual time=2.550..2.550 rows=0 loops=1)
        Index Cond: ((content ~>=~ '1500000'::text) AND (content ~<~ '1500001'::text))
Planning Time: 6.247 ms
Execution Time: 6.809 ms

在线示例

于 2021-07-14T20:39:42.067 回答
3

先说正确。您的谓词将找不到“https://mywebsite/mina/”:

content LIKE 'mina%'

其中之一将起作用:

content LIKE '%mina%'
content ~ 'mina'

但是 btree 索引都不能支持两者。三元组索引可以做到这一点:

CREATE INDEX ON textfun USING gin (content gin_trgm_ops);

看:

或者可能是文本搜索索引(在您的 URI 中分隔单词之后......)

但是 GIN 索引不能强制唯一性。您可能需要第二个 B 树索引。(或暗示这种索引的约束)。

为了真正支持您原始的左锚谓词,我将使用 B-tree 索引COLLATE "C"

CREATE UNIQUE INDEX text_b ON textfun(content COLLATE "C");

Postgres 9.1 添加的每列排序规则支持在很大程度上淘汰了旧的xxx_pattern_ops操作符类。看:

db<>fiddle here - 使用COLLATE "C", 和更少的行(足以证明这一点)。

以下是 Postgres 中模式匹配选项的全面概述:


除此之外,只有三个不同值的测试用例不是很有用。当 Postgres 期望获取超过百分之几的所有行时,它通常不会使用任何索引,因为顺序扫描通常会更快。它会知道几个“最常见的值”,因为它会保持统计数据更新ANALYZE(或autovacuum默认)。

搜索content LIKE '1500000%'在这方面是不同的,因为 Postgres 会知道它并不常见并使用适用的索引......

索引优化取决于全貌:环境和要求...

于 2021-07-14T21:00:44.800 回答