POSTGRESQL(PG) 性能优化之like全文检索优化

这篇具有很好参考价值的文章主要介绍了POSTGRESQL(PG) 性能优化之like全文检索优化。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

使用like操作可以进行字符串比较,全文检索等,性能相对比较差,有些情况下可以通过建立索引来提升性能。下面我们通过使用TPCH orders表作为例子,来进行说明。但是请注意, not like是不能用任何索引的,BTREE不支持!=操作,只能进行=和范围查找。

TPCH orders表的定义如下,其中o_comment列是varchar类型的字符串:

tpch10=# \d orders
                          Table "public.orders"
     Column      |         Type          | Collation | Nullable | Default
-----------------+-----------------------+-----------+----------+---------
 o_orderkey      | integer               |           | not null |
 o_custkey       | integer               |           | not null |
 o_orderstatus   | character(1)          |           | not null |
 o_totalprice    | numeric(15,2)         |           | not null |
 o_orderdate     | date                  |           | not null |
 o_orderpriority | character(15)         |           | not null |
 o_clerk         | character(15)         |           | not null |
 o_shippriority  | integer               |           | not null |
 o_comment       | character varying(79) |           | not null |
Indexes:
    "orders_pkey" PRIMARY KEY, btree (o_orderkey)
Foreign-key constraints:
    "orders_o_custkey_fkey" FOREIGN KEY (o_custkey) REFERENCES customer(c_custkey)
Referenced by:
    TABLE "lineitem" CONSTRAINT "lineitem_l_orderkey_fkey" FOREIGN KEY (l_orderkey) REFERENCES orders(o_orderkey)

1. 前匹配 LIKE ‘prefix%’范围查询

前匹配可以使用BTREE索引进行范围查询,在没有任何索引的情况下,PG默认使用seqscan顺序扫描:

tpch10=# explain analyze select * from orders where o_comment like 'request%';
                                                    QUERY PLAN
------------------------------------------------------------------------------------------------------------------
 Seq Scan on orders  (cost=0.00..448412.00 rows=75000 width=234) (actual time=0.662..5098.904 rows=56870 loops=1)
   Filter: ((o_comment)::text ~~ 'request%'::text)
   Rows Removed by Filter: 14943130
 Planning Time: 0.268 ms
 Execution Time: 5103.280 ms
(5 rows)

我们在o_comment列加一下BTREE索引,然后再次尝试:

tpch10=# create index on orders(o_comment);
CREATE INDEX
tpch10=# analyze orders;
ANALYZE
tpch10=# explain analyze select * from orders where o_comment like 'request%';
                                                    QUERY PLAN
------------------------------------------------------------------------------------------------------------------
 Seq Scan on orders  (cost=0.00..448412.00 rows=75000 width=234) (actual time=0.662..5098.904 rows=56870 loops=1)
   Filter: ((o_comment)::text ~~ 'request%'::text)
   Rows Removed by Filter: 14943130
 Planning Time: 0.268 ms
 Execution Time: 5103.280 ms
(5 rows)
tpch10=# explain analyze select * from orders where o_comment like 'request';
                                                          QUERY PLAN
-------------------------------------------------------------------------------------------------------------------------------
 Index Scan using orders_o_comment_idx on orders  (cost=0.56..8.58 rows=1 width=107) (actual time=0.188..0.188 rows=0 loops=1)
   Index Cond: ((o_comment)::text = 'request'::text)
   Filter: ((o_comment)::text ~~ 'request'::text)
 Planning Time: 0.408 ms
 Execution Time: 0.250 ms
(5 rows)

发现PG在like ‘prefix%’并没有使用BTREE索引,而精确匹配可以使用索引,问题在于默认的collate是en_US.UTF-8,而不是C,将o_comment列改为C collate再进行尝试。使用alter table命令将o_comment列改成collate C,可以查看到o_comment列的Collation属性是C。

tpch10=# alter table orders alter column o_comment TYPE VARCHAR(79) collate "C";
ALTER TABLE
tpch10=# \d orders
                          Table "public.orders"
     Column      |         Type          | Collation | Nullable | Default
-----------------+-----------------------+-----------+----------+---------
 o_orderkey      | integer               |           | not null |
 o_custkey       | integer               |           | not null |
 o_orderstatus   | character(1)          |           | not null |
 o_totalprice    | numeric(15,2)         |           | not null |
 o_orderdate     | date                  |           | not null |
 o_orderpriority | character(15)         |           | not null |
 o_clerk         | character(15)         |           | not null |
 o_shippriority  | integer               |           | not null |
 o_comment       | character varying(79) | C         | not null |
Indexes:
    "orders_pkey" PRIMARY KEY, btree (o_orderkey)
    "orders_o_comment_idx" btree (o_comment)
Foreign-key constraints:
    "orders_o_custkey_fkey" FOREIGN KEY (o_custkey) REFERENCES customer(c_custkey)
Referenced by:
    TABLE "lineitem" CONSTRAINT "lineitem_l_orderkey_fkey" FOREIGN KEY (l_orderkey) REFERENCES orders(o_orderkey)

tpch10=#

再执行一次之前的语句,发现使用了创建的BTREE索引,时间从5103ms提升到232ms,可以看到性能得到极大提升。

tpch10=# explain analyze select * from orders where o_comment like 'request%';
                                                                QUERY PLAN
------------------------------------------------------------------------------------------------------------------------------------------
 Bitmap Heap Scan on orders  (cost=3477.31..168092.77 rows=75000 width=234) (actual time=50.869..228.043 rows=56870 loops=1)
   Filter: ((o_comment)::text ~~ 'request%'::text)
   Heap Blocks: exact=51160
   ->  Bitmap Index Scan on orders_o_comment_idx  (cost=0.00..3458.56 rows=75000 width=0) (actual time=33.213..33.213 rows=56870 loops=1)
         Index Cond: (((o_comment)::text >= 'request'::text) AND ((o_comment)::text < 'requesu'::text))
 Planning Time: 0.528 ms
 Execution Time: 232.989 ms
(7 rows)

tpch10=#

2. 后匹配 LIKE ‘%prefix’范围查询

后匹配的方式不能使用BTREE索引,原因在于BTREE索引只能做大于、大于等于、等于、小于、小于等于等操作决定的。因此这时候使用reverse index来处理后匹配的文本检索。

比如我们使用相同的例子,只是like匹配的是‘%request’,PG依旧使用顺序扫描,而没有使用之前建的索引。

tpch10=# explain analyze select * from orders where o_comment like '%request';
                                                    QUERY PLAN
-------------------------------------------------------------------------------------------------------------------
 Seq Scan on orders  (cost=0.00..448412.00 rows=151515 width=107) (actual time=0.043..8640.280 rows=57305 loops=1)
   Filter: ((o_comment)::text ~~ '%request'::text)
   Rows Removed by Filter: 14942695
 Planning Time: 1.065 ms
 Execution Time: 8645.223 ms
(5 rows)

我们给o_comment列建一个reverse的索引,会看到执行语句会选用reverse索引,时间由8645ms降到162ms,提升效率非常高。因此在这种情况下可以考虑使用reverse索引来提高后匹配的检索效率。

tpch10=# create index on orders(reverse(o_comment));
CREATE INDEX
tpch10=# analyze orders;
ANALYZE

tpch10=# explain analyze select * from orders where reverse(o_comment) like reverse('%request');
                                                              QUERY PLAN
--------------------------------------------------------------------------------------------------------------------------------------
 Index Scan using orders_reverse_idx on orders  (cost=0.56..8.59 rows=1500 width=107) (actual time=0.142..157.412 rows=57305 loops=1)
   Index Cond: ((reverse((o_comment)::text) >= 'tseuqer'::text) AND (reverse((o_comment)::text) < 'tseuqes'::text))
   Filter: (reverse((o_comment)::text) ~~ 'tseuqer%'::text)
 Planning Time: 0.620 ms
 Execution Time: 162.048 ms
(5 rows)

tpch10=#

3. 中间匹配'%prefix%'范围查询

对于中间匹配的全文检索,以上两种索引都不能解决,PG提供了GIN索引,可以使用PG提供的pg_trgm插件来加速查询。GIN索引可以应用于前匹配、后匹配和中间匹配,但是因为GIN本身比较昂贵,对于前匹配和后匹配,可以使用上面提到的方式,中间匹配采用GIN索引,但是也需要考虑索引维护本身的代价。

在没有使用GIN索引时候,PG依旧使用顺序扫描:

tpch10=# explain analyze select * from orders where o_comment like '%request%';
                                                      QUERY PLAN
----------------------------------------------------------------------------------------------------------------------
 Seq Scan on orders  (cost=0.00..448442.18 rows=2273093 width=107) (actual time=0.108..8773.358 rows=2318296 loops=1)
   Filter: ((o_comment)::text ~~ '%request%'::text)
   Rows Removed by Filter: 12681704
 Planning Time: 0.836 ms
 Execution Time: 8901.078 ms
(5 rows)

tpch10=# explain analyze select * from orders where reverse(o_comment) like reverse('%request%');
                                                      QUERY PLAN
-----------------------------------------------------------------------------------------------------------------------
 Seq Scan on orders  (cost=0.00..485948.21 rows=3030791 width=107) (actual time=0.069..18095.022 rows=2318296 loops=1)
   Filter: (reverse((o_comment)::text) ~~ '%tseuqer%'::text)
   Rows Removed by Filter: 12681704
 Planning Time: 4.783 ms
 Execution Time: 18218.893 ms
(5 rows)

但是GIN索引的创建和维护非常昂贵,因为GIN索引对每组三个连续字符(三元组)进行索引,以便能够查找任何匹配模式。而且GIN索引也可以应用于前后匹配的组合使用,这种情况下效率也很高。

加载pg_trgm插件后,在o_comment列创建GIN索引,重新进行查询,时间由8901ms提升到3939ms,效率得到了提升,同时我们也可以看到GIN索引也可以进行‘%prefix%prefix%’的查询,效率也是很明显的提升。文章来源地址https://www.toymoban.com/news/detail-755320.html

tpch10=# create extension if not exists pg_trgm;
CREATE EXTENSION
tpch10=# create index on orders using gin (o_comment gin_trgm_ops);
CREATE INDEX
tpch10=# analyze orders;
ANALYZE

tpch10=# explain analyze select * from orders where o_comment like '%request%';
                                                                     QUERY PLAN
----------------------------------------------------------------------------------------------------------------------------------------------------
 Bitmap Heap Scan on orders  (cost=29361.97..330044.89 rows=3181674 width=107) (actual time=1727.012..3828.371 rows=2318296 loops=1)
   Recheck Cond: ((o_comment)::text ~~ '%request%'::text)
   Rows Removed by Index Recheck: 2088
   Heap Blocks: exact=260892
   ->  Bitmap Index Scan on orders_o_comment_idx1  (cost=0.00..28566.55 rows=3181674 width=0) (actual time=1604.355..1604.355 rows=2320384 loops=1)
         Index Cond: ((o_comment)::text ~~ '%request%'::text)
 Planning Time: 0.473 ms
 Execution Time: 3939.557 ms
(8 rows)


tpch10=# explain analyze select * from orders where o_comment like '%special%request%';
                                                                   QUERY PLAN
-------------------------------------------------------------------------------------------------------------------------------------------------
 Bitmap Heap Scan on orders  (cost=1478.19..236736.24 rows=151508 width=107) (actual time=1794.711..2266.371 rows=168338 loops=1)
   Recheck Cond: ((o_comment)::text ~~ '%special%request%'::text)
   Rows Removed by Index Recheck: 80603
   Heap Blocks: exact=161167
   ->  Bitmap Index Scan on orders_o_comment_idx1  (cost=0.00..1440.31 rows=151508 width=0) (actual time=1731.915..1731.915 rows=248941 loops=1)
         Index Cond: ((o_comment)::text ~~ '%special%request%'::text)
 Planning Time: 0.501 ms
 Execution Time: 2278.128 ms
(8 rows)

到了这里,关于POSTGRESQL(PG) 性能优化之like全文检索优化的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 全文检索-Elasticsearch-进阶检索

    本文记录谷粒商城高级篇的 Elasticsearch 进阶检索部分,续上之前记录的 Elasticsearch入门篇。 ES 支持两种基本方式检索 : 一个是通过使用 REST request URI 发送搜索参数(uri + 检索参数) 另一个是通过使用 REST request body 来发送它们(uri + 请求体) 请求体中写查询条件,语法: 示例

    2024年02月03日
    浏览(79)
  • 【全文检索】sqlite-fts4和pgsql的全文检索对比

    因为是Android项目,老系统中的全文检索是采用sqlite自带的fts4,然而后续由于地图要素全部转为线上,全文检索也需要同步在线查询,所以将整个全文检索的功能迁移到pgsql中。目前这块功能基本结束,这里来对两种全文检索方案做一个对比总结。 相比与fts5,fts4的好处是原生

    2024年02月05日
    浏览(37)
  • 全文检索-Es-初步检索(三)

    #为jmeter返回的结果 jmeter测试结果 请求头 http请求 put 返回结果 再次发送请求 post不带/带id保存 不带id 结果 二次请求结果 带id保存 结果 二次请求结果 结论 发送请求 查询-查看结果树 增加判断,确定是否修改 结果 查看修改是否成功 结果 更新文档 post/put带_update的请求(会比

    2024年02月14日
    浏览(32)
  • Elasticsearch 全文检索 分词检索-Elasticsearch文章四

    https://www.elastic.co/guide/en/enterprise-search/current/start.html https://www.elastic.co/guide/en/elasticsearch/reference/7.17/query-dsl-match-query.html Full text Query中,我们只需要把如下的那么多点分为3大类,你的体系能力会大大提升 很多api都可以查得到,我们只要大概知道有支持哪些功能 Elasticsearch 执行

    2024年02月14日
    浏览(43)
  • ElasticSearch-全文检索

    https://www.elastic.co/cn/what-is/elasticsearch 全文搜索属于最常见的需求,开源的Elasticsearch是目前全文搜索引擎的首选。 它可以快速地储存、搜索和分析海量数据。 维基百科、StackOverflow、Github都采用它。 Elastic的底层是开源库Lucene。但是,你没法直接用Lucene,必须自己写代码去调用

    2024年04月17日
    浏览(32)
  • mysql全文检索使用

    数据库数据量10万左右,使用like \\\'%test%\\\'要耗费30秒左右,放弃该办法 使用mysql的全文检索 第一步:建立索引 首先修改一下设置: my.ini中ngram_token_size = 1 可以通过    show variables like \\\'%token%\\\';来查看 接下来建立索引:alter  table 表名 add fulltext titlefull (字段名) with parser ngram; 第二步

    2024年02月12日
    浏览(30)
  • MySQL中文全文检索

    常规数据库搜索都是用 like 语句,但是like 语句是不能利用索引的,查询效率极其低下。这也就是为什么很多功能都只提供标题搜索的原因,因为如果搜索内容,几万数据就跑不动了。 Mysql 全文索引是专门为了解决模糊查询提供的,可以对整篇文章预先按照词进行索引,搜索

    2024年02月14日
    浏览(37)
  • Lucene全文检索

    Lucene 是一个基于 Java 的全文信息检索工具包,目前主流的搜索系统 Elasticsearch 和 solr 都是基于 lucene 的索引和搜索能力进行。 Solr与Lucene的区别: Solr和Lucene的本质区别三点:搜索服务器,企业级和管理。 Lucene本质上是搜索库,不是独立的应用程序,而Solr是。 Lucene专注于搜索

    2024年02月09日
    浏览(36)
  • 实现全文检索的方法

    实现网站全文检索功能,可以采取多种方法,从简单的基于数据库的搜索到使用专门的全文检索系统。以下是一些常见的实现全文检索的方法: 1. **数据库全文索引**:    如果你的网站后端使用的是关系型数据库(如MySQL),大多数数据库管理系统都提供了全文索引的功能。

    2024年04月26日
    浏览(39)
  • MySQL——全文检索

    不是所有的数据表都支持全文检索 MySQL支持多种底层数据库引擎,但是并非所有的引擎支持全文检索 ,目前最常用引擎是是MyISAM和InnoDB;前者支持全文检索,后者不支持。 表productnotes : 1. 查询包含 rabbit 的行,并按照相关性排序  2.显示每一条的相关性值 3.有heavy 但是没有

    2024年04月15日
    浏览(37)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包