PostgreSQL使用pg_trgm慢于完全扫描

PostgreSQL使用pg_trgm慢于完全扫描,postgresql,Postgresql,我玩pg_trgm扩展,我有点困惑。以下是会议: postgres=# create table t(i int, x text); CREATE TABLE postgres=# insert into t select i, random()::text from generate_series(1,50000000) as i; INSERT 0 50000000 postgres=# explain analyze select * from t where x ilike '%6666

我玩pg_trgm扩展,我有点困惑。以下是会议:

postgres=# create table t(i int, x text);
CREATE TABLE
postgres=# insert into t select i, random()::text from generate_series(1,50000000) as i;
INSERT 0 50000000
postgres=# explain analyze select * from t where x ilike '%666666%';
                                                        QUERY PLAN                                                         
---------------------------------------------------------------------------------------------------------------------------
 Gather  (cost=1000.00..531870.29 rows=12954 width=36) (actual time=131.436..11408.176 rows=432 loops=1)
   Workers Planned: 2
   Workers Launched: 2
   ->  Parallel Seq Scan on t  (cost=0.00..529574.89 rows=5398 width=36) (actual time=108.771..11304.946 rows=144 loops=3)
         Filter: (x ~~* '%666666%'::text)
         Rows Removed by Filter: 16666523
 Planning Time: 0.121 ms
 Execution Time: 11408.279 ms
(8 rows)

postgres=# explain analyze select * from t where x ilike '%666666%';
                                                        QUERY PLAN                                                        
--------------------------------------------------------------------------------------------------------------------------
 Gather  (cost=1000.00..580654.94 rows=5000 width=21) (actual time=124.986..11070.983 rows=432 loops=1)
   Workers Planned: 2
   Workers Launched: 2
   ->  Parallel Seq Scan on t  (cost=0.00..579154.94 rows=2083 width=21) (actual time=72.207..11010.876 rows=144 loops=3)
         Filter: (x ~~* '%666666%'::text)
         Rows Removed by Filter: 16666523
 Planning Time: 0.283 ms
 Execution Time: 11071.065 ms
(8 rows)

postgres=# create index i on t using gin (x gin_trgm_ops);
CREATE INDEX
postgres=# analyze t;
ANALYZE
postgres=# explain analyze select * from t where x ilike '%666666%';
                                                     QUERY PLAN                                                      
---------------------------------------------------------------------------------------------------------------------
 Bitmap Heap Scan on t  (cost=54.75..18107.93 rows=5000 width=21) (actual time=116.114..26995.773 rows=432 loops=1)
   Recheck Cond: (x ~~* '%666666%'::text)
   Rows Removed by Index Recheck: 36257910
   Heap Blocks: exact=39064 lossy=230594
   ->  Bitmap Index Scan on i  (cost=0.00..53.50 rows=5000 width=0) (actual time=75.363..75.363 rows=592216 loops=1)
         Index Cond: (x ~~* '%666666%'::text)
 Planning Time: 0.389 ms
 Execution Time: 26996.429 ms
(8 rows)

postgres=# explain analyze select * from t where x ilike '%666666%';
                                                     QUERY PLAN                                                      
---------------------------------------------------------------------------------------------------------------------
 Bitmap Heap Scan on t  (cost=54.75..18107.93 rows=5000 width=21) (actual time=128.859..29231.765 rows=432 loops=1)
   Recheck Cond: (x ~~* '%666666%'::text)
   Rows Removed by Index Recheck: 36257910
   Heap Blocks: exact=39064 lossy=230594
   ->  Bitmap Index Scan on i  (cost=0.00..53.50 rows=5000 width=0) (actual time=79.147..79.147 rows=592216 loops=1)
         Index Cond: (x ~~* '%666666%'::text)
 Planning Time: 0.252 ms
 Execution Time: 29231.945 ms
(8 rows)
正如您所看到的,没有索引时,查询速度比使用索引时快两倍多。目前,有默认的PostgreSQL设置、共享缓冲区、工作内存等

我错过了什么

PS:x86_64-pc-linux-gnu上的PostgreSQL 11.5 Ubuntu 11.5-1.pgdg18.04+1,由gcc Ubuntu 7.4.0-1ubuntu1~18.04.1 7.4.0编译,64位


PPS:使用gist索引速度更慢。

tldr:三角形可能不擅长搜索由单个字符重复N次组成的模式,例如666666,因为只有一个非末端三角形,并且可能在搜索空间中出现率较高

使用gin索引时,行的位图太大,无法放入内存,因此它存储对页面的引用,数据库必须对这些页面执行进一步的重新检查扫描。如果重新检查的页面数较少,则使用索引仍然是有益的,但是,如果重新检查的页面数较多,则索引的性能较差。explain输出中的以下几行突出显示了这一点

   Recheck Cond: (x ~~* '%666666%'::text)
   Rows Removed by Index Recheck: 36257910
   Heap Blocks: exact=39064 lossy=230594
该问题与您的搜索字符串(即666666)有关,与测试数据有关

如果运行select pg_trgm“666666”,您将发现:

        show_trgm        
-------------------------
 {"  6"," 66","66 ",666}
(1 row)
在用户建议的类似ilike的上下文更正中,甚至不会生成前3个三叉树。在索引上搜索将生成包含666的所有页面。您可以通过使用运行explain ANALYSE查询来验证这一点。。。我喜欢“%666%”,并获得与上述相同的堆块输出

如果您使用模式123456进行搜索,您将看到它的性能要好得多,因为它生成了一组更大的三角图来进行搜索:

              show_trgm              
-------------------------------------
 {"  1"," 12",123,234,345,456,"56 "}
(1 row)
在我的机器上,我得到以下信息:

|------------------------------------|
| pattern | pages rechecked          |
|         | exact | lossy  | total   |
|------------------------------------|
| 123456  |   600 |        |    600  |
| 666666  | 39454 | 230592 | 270046* |
|    666  | 39454 | 230592 | 270046* |
|------------------------------------|
*this is rougly 85% of the total # of pages used for the table 't'
以下是解释输出:

postgres=> explain analyze select * from t where x ~ '123456';
                                                        QUERY PLAN                                                        
--------------------------------------------------------------------------------------------------------------------------
 Bitmap Heap Scan on t  (cost=90.75..18143.92 rows=5000 width=22) (actual time=110.962..113.509 rows=518 loops=1)
   Recheck Cond: (x ~ '123456'::text)
   Rows Removed by Index Recheck: 83
   Heap Blocks: exact=600
   ->  Bitmap Index Scan on t_x_idx  (cost=0.00..89.50 rows=5000 width=0) (actual time=110.868..110.868 rows=601 loops=1)
         Index Cond: (x ~ '123456'::text)
 Planning time: 0.703 ms
 Execution time: 113.564 ms
(8 rows)

postgres=> explain analyze select * from t where x ~ '666666';
                                                         QUERY PLAN                                                          
-----------------------------------------------------------------------------------------------------------------------------
 Bitmap Heap Scan on t  (cost=54.75..18107.92 rows=5000 width=22) (actual time=137.143..18111.609 rows=462 loops=1)
   Recheck Cond: (x ~ '666666'::text)
   Rows Removed by Index Recheck: 36258389
   Heap Blocks: exact=39454 lossy=230592
   ->  Bitmap Index Scan on t_x_idx  (cost=0.00..53.50 rows=5000 width=0) (actual time=105.962..105.962 rows=593708 loops=1)
         Index Cond: (x ~ '666666'::text)
 Planning time: 0.420 ms
 Execution time: 18111.739 ms
(8 rows)

postgres=> explain analyze select * from t where x ~ '666';
                                                        QUERY PLAN                                                         
---------------------------------------------------------------------------------------------------------------------------
 Bitmap Heap Scan on t  (cost=54.75..18107.92 rows=5000 width=22) (actual time=102.813..17285.086 rows=593708 loops=1)
   Recheck Cond: (x ~ '666'::text)
   Rows Removed by Index Recheck: 35665143
   Heap Blocks: exact=39454 lossy=230592
   ->  Bitmap Index Scan on t_x_idx  (cost=0.00..53.50 rows=5000 width=0) (actual time=96.100..96.100 rows=593708 loops=1)
         Index Cond: (x ~ '666'::text)
 Planning time: 0.500 ms
 Execution time: 17300.440 ms
(8 rows)

您已经有了一个很好的答案,它解释了为什么“%666666%”几乎是pg_trgm使用示例数据的最坏情况

很难说这个最坏的情况是否是一个公平的测试。有时,最坏的情况是不可避免的,并且对性能敏感。如果你是这样的话,那么也许这是一个公平的测试。另一方面,担心性能恶魔式的查询而不是实际的查询往往是浪费时间

但是,您可以做一些事情来提高最坏情况下的性能

堆块:精确=39064有损=230594

这里的有损块对性能很糟糕。如果您增加work_mem直到它们消失,它可能会缩小索引和seq扫描之间的大部分差距,甚至可能将其反转。而且它不需要很大的设置,在我手里20MB就足够了。在现代服务器上,这是一个相当保守的设置

如果您的表比RAM中缓存的表大,那么您将花费大量时间从磁盘读取数据。如果是这种情况,增加有效io并发的设置也可能有助于缩小索引的最坏使用情况与顺序扫描之间的差距

要知道的另一件事是seq扫描使用2个并行工作线程。因此,虽然它完成的速度是原来的两倍,但它可能要使用3倍的资源来完成。我不明白为什么索引没有使用并行位图扫描-我认为它是合格的


如果您可以得到索引的最坏使用情况与seq scan大致相同,并且平均使用情况要好得多,那么您已经走在了前面。

我们知道,没有任何行与前3个三角形匹配,因为测试数据中不存在空格。即使他在测试数据中有空格,也不会被搜索。在类似ILIKE的上下文中,不会生成带空格的三叉树。表示你不知道上下文,所以它没有显示这个事实。谢谢你的回答,问题在它之后变得很明显。但是你回答了为什么的问题?但不是为了什么?似乎PostgreSQL规划师在使用索引与否方面做出了错误的决定。也为了@LaurenzAlbe@Abelisto这隐藏在普通人的内心深处,让我难以猜测。但由于估计准确率为0.01%,似乎有一些经验法则在起作用。对于不同的模式,估计值是否有所不同?