PolarDB 开源版通过 rum 实现高效率搜索和高效率排序的解决方案

本文涉及的产品
云原生数据库 PolarDB PostgreSQL 版,标准版 2核4GB 50GB
云原生数据库 PolarDB MySQL 版,通用型 2核4GB 50GB
简介: PolarDB 的云原生存算分离架构, 具备低廉的数据存储、高效扩展弹性、高速多机并行计算能力、高速数据搜索和处理; PolarDB与计算算法结合, 将实现双剑合璧, 推动业务数据的 价值产出, 将数据变成生产力. 本文将介绍PolarDB 开源版通过 rum 实现高效率搜索和高效率排序的解决方案

背景

PolarDB 的云原生存算分离架构, 具备低廉的数据存储、高效扩展弹性、高速多机并行计算能力、高速数据搜索和处理; PolarDB与计算算法结合, 将实现双剑合璧, 推动业务数据的
价值产出, 将数据变成生产力.

本文将介绍PolarDB 开源版通过 rum 实现高效率搜索和高效率排序的解决方案

测试环境为macos+docker, polardb部署请参考:

为什么DBA最怕搜索需求

如果你要做个搜索功能, 用户在搜索时通常会有多个满足搜索条件的结果, 这些结果往往还需要根据其他条件进行排序, 这种搜索在一般的数据库中, 只能全表扫描实现, 性能差到极点. 随便几个查询就会把数据库CPU、内存、IO资源耗光, 影响业务. 最后一般是DBA背锅, 实际上是数据库没有好的算法和存储结构, 所以这锅有点冤枉.

根据代际转移理论, 今天我们都是提前消费未来的创造力, 而以前通过暴力(堆机器)来解决问题的方法, 放在今天必须要有更高效、节能的方式来解决.

这也是今天我想要跟大家介绍的《rum+PolarDB实现高效率搜索和高效率排序的解决方案》, 性能轻松提升70倍(大数据量性能提升还会更多). 以后有这种需求, DBA就可以硬气了.

rum的原理

rum是个倒排结构, 但是在期elements里面又增加了addon value的存储, 每个匹配的element条目再根据addon column value重新排序编排其ctid(s).

同时由于多值列本身的等值匹配较为常见, 所以rum有个变种, element可以被hash化来进行存储, 提高索引存储效率. hash化之后仅仅支持element的等值匹配, 类似的hash index也一样.

1、rum storage:

1.1、ops

  • k,v结构: ele,ctid(s)

1.2、addon_ops

  • k,v结构: ele,kv(s)

    • kv(s)结构: addon,ctid(s) (根据addon value构建btree)

1.3、hash_ops

  • k,v结构: ele_hash:ctid(s)

1.4、hash_addon_ops

  • k,v结构: ele_hash:kv(s)

    • kv(s)结构: addon:ctid(s) (根据addon value构建btree)

hash ops和hash addon ops不支持按prefix搜索ele, 因为element已经转换成hashvalue存储在索引中, 只能做等值匹配.

hash ops支持按距离排序(<=>), 因为距离计算取决于等值匹配到的ele比例. 可以想象, 不支持有方向的排序和搜索, 例如prefix search和prefix sort (<=| and |=>).

2、sort compute:

2.1、ele key sort:

  • ele distance : dist_fun(ctid , $)

2.2、addon ele sort:

  • addon distance : dist_fun(ctid , $)

按ele排序 或 匹配ele条件后按addon排序

hash ops可以按addon排序(prefix, 相似都支持. 包括 <=>, <=| and |=> ), 因为addon column未被hash化.

将rum部署到PolarDB中

git clone --depth 1 https://github.com/postgrespro/rum  
  
cd rum  
  
USE_PGXS=1 make  
USE_PGXS=1 make install  
USE_PGXS=1 make installcheck  
  
  
[postgres@aa25c5be9681 rum]$ USE_PGXS=1 make installcheck  
/home/postgres/tmp_basedir_polardb_pg_1100_bld/lib/pgxs/src/makefiles/../../src/test/regress/pg_regress --inputdir=./ --bindir='/home/postgres/tmp_basedir_polardb_pg_1100_bld/bin'      --dbname=contrib_regression security rum rum_validate rum_hash ruminv timestamp orderby orderby_hash altorder altorder_hash limits int2 int4 int8 float4 float8 money oid time timetz date interval macaddr inet cidr text varchar char bytea bit varbit numeric rum_weight array  
(using postmaster on 127.0.0.1, default port)  
============== dropping database "contrib_regression" ==============  
NOTICE:  database "contrib_regression" does not exist, skipping  
DROP DATABASE  
============== creating database "contrib_regression" ==============  
CREATE DATABASE  
ALTER DATABASE  
============== running regression test queries        ==============  
test security                     ... ok  
test rum                          ... ok  
test rum_validate                 ... ok  
test rum_hash                     ... ok  
test ruminv                       ... ok  
test timestamp                    ... ok  
test orderby                      ... ok  
test orderby_hash                 ... ok  
test altorder                     ... ok  
test altorder_hash                ... ok  
test limits                       ... ok  
test int2                         ... ok  
test int4                         ... ok  
test int8                         ... ok  
test float4                       ... ok  
test float8                       ... ok  
test money                        ... ok  
test oid                          ... ok  
test time                         ... ok  
test timetz                       ... ok  
test date                         ... ok  
test interval                     ... ok  
test macaddr                      ... ok  
test inet                         ... ok  
test cidr                         ... ok  
test text                         ... ok  
test varchar                      ... ok  
test char                         ... ok  
test bytea                        ... ok  
test bit                          ... ok  
test varbit                       ... ok  
test numeric                      ... ok  
test rum_weight                   ... ok  
test array                        ... ok  
  
  
===========================================================  
 All 34 tests passed.   
  
 POLARDB:  
 All 34 tests, 0 tests in ignore, 0 tests in polar ignore.   
===========================================================  
  
  
  
  
  
psql  
create extension rum;  

场景化讲解

画像业务、搜索业务:

  • 标签匹配+权重排序
  • 标签匹配+时间排序

例如文章搜索, 算法举例:

  • 关注文章内容的相关性, 同时要按文章的发布时间顺序排序返回前10条.
  • 关注文章内容的相关性, 同时要按文章的权重(如按广告费等计算出来的权重)顺序排序返回前10条.

1、创建测试表

create table tbl (id int, info tsvector, weight float4);  

2、写入测试数据

insert into tbl select id, to_tsvector('hello i am tom lane, i love postgresql'), random()*100 from generate_series(1,100000) id;  
insert into tbl select id, to_tsvector('hello i am digoal, i love polardb at aliyun at china.'), random()*100 from generate_series(1,2000) id;  

3、创建rum索引, 将权重attach到rum索引的ctid(s)中.

create index on tbl using rum (info rum_tsvector_hash_addon_ops, weight) with (attach = 'weight', to = 'info');  

4、使用rum高效搜索和排序

4.1、匹配字符串, 并且权重越大越好的?

select *, weight <=| '100000'::float4 from tbl where info @@ 'digoal&polardb' ORDER BY weight <=| '100000'::float4 limit 10;  
  
  id  |                              info                               | weight  |     ?column?       
------+-----------------------------------------------------------------+---------+------------------  
 1078 | 'aliyun':9 'china':11 'digoal':4 'hello':1 'love':6 'polardb':7 | 99.9273 | 99900.0727005005  
  877 | 'aliyun':9 'china':11 'digoal':4 'hello':1 'love':6 'polardb':7 | 99.8128 |  99900.187171936  
  118 | 'aliyun':9 'china':11 'digoal':4 'hello':1 'love':6 'polardb':7 | 99.8049 | 99900.1951217651  
  881 | 'aliyun':9 'china':11 'digoal':4 'hello':1 'love':6 'polardb':7 | 99.4699 | 99900.5300979614  
 1257 | 'aliyun':9 'china':11 'digoal':4 'hello':1 'love':6 'polardb':7 | 99.4317 | 99900.5682678223  
  459 | 'aliyun':9 'china':11 'digoal':4 'hello':1 'love':6 'polardb':7 | 99.3815 | 99900.6185073853  
 1306 | 'aliyun':9 'china':11 'digoal':4 'hello':1 'love':6 'polardb':7 | 99.3271 | 99900.6729354858  
  300 | 'aliyun':9 'china':11 'digoal':4 'hello':1 'love':6 'polardb':7 | 99.2863 |  99900.713722229  
  313 | 'aliyun':9 'china':11 'digoal':4 'hello':1 'love':6 'polardb':7 | 99.2731 | 99900.7268676758  
  618 | 'aliyun':9 'china':11 'digoal':4 'hello':1 'love':6 'polardb':7 | 99.2371 | 99900.7629318237  
(10 rows)  
  
  
  
select *, weight <=| '100000'::float4 from tbl where info @@ 'digoal' ORDER BY weight <=| '100000'::float4 limit 10;  
  
  id  |                              info                               | weight  |     ?column?       
------+-----------------------------------------------------------------+---------+------------------  
 1078 | 'aliyun':9 'china':11 'digoal':4 'hello':1 'love':6 'polardb':7 | 99.9273 | 99900.0727005005  
  877 | 'aliyun':9 'china':11 'digoal':4 'hello':1 'love':6 'polardb':7 | 99.8128 |  99900.187171936  
  118 | 'aliyun':9 'china':11 'digoal':4 'hello':1 'love':6 'polardb':7 | 99.8049 | 99900.1951217651  
  881 | 'aliyun':9 'china':11 'digoal':4 'hello':1 'love':6 'polardb':7 | 99.4699 | 99900.5300979614  
 1257 | 'aliyun':9 'china':11 'digoal':4 'hello':1 'love':6 'polardb':7 | 99.4317 | 99900.5682678223  
  459 | 'aliyun':9 'china':11 'digoal':4 'hello':1 'love':6 'polardb':7 | 99.3815 | 99900.6185073853  
 1306 | 'aliyun':9 'china':11 'digoal':4 'hello':1 'love':6 'polardb':7 | 99.3271 | 99900.6729354858  
  300 | 'aliyun':9 'china':11 'digoal':4 'hello':1 'love':6 'polardb':7 | 99.2863 |  99900.713722229  
  313 | 'aliyun':9 'china':11 'digoal':4 'hello':1 'love':6 'polardb':7 | 99.2731 | 99900.7268676758  
  618 | 'aliyun':9 'china':11 'digoal':4 'hello':1 'love':6 'polardb':7 | 99.2371 | 99900.7629318237  
(10 rows)  
postgres=# explain (analyze,verbose,timing,costs,buffers) select *, weight <=| '100000'::float4 from tbl where info @@ 'digoal&polardb' ORDER BY weight <=| '100000'::float4 limit 10;  
                                                                QUERY PLAN                                                                  
------------------------------------------------------------------------------------------------------------------------------------------  
 Limit  (cost=24.00..40.06 rows=3 width=48) (actual time=2.081..2.097 rows=10 loops=1)  
   Output: id, info, weight, ((weight <=| '100000'::real))  
   Buffers: shared hit=21  
   ->  Index Scan using tbl_info_weight_idx on public.tbl  (cost=24.00..40.06 rows=3 width=48) (actual time=2.079..2.093 rows=10 loops=1)  
         Output: id, info, weight, (weight <=| '100000'::real)  
         Index Cond: (tbl.info @@ '''digoal'' & ''polardb'''::tsquery)  
         Order By: (tbl.weight <=| '100000'::real)  
         Buffers: shared hit=21  
 Planning Time: 0.160 ms  
 Execution Time: 2.149 ms  
(10 rows)  
  
  
  
postgres=# explain (analyze,verbose,timing,costs,buffers) select *, weight <=| '100000'::float4 from tbl where info @@ 'digoal' ORDER BY weight <=| '100000'::float4 limit 10;  
                                                                  QUERY PLAN                                                                     
-----------------------------------------------------------------------------------------------------------------------------------------------  
 Limit  (cost=12.00..35.81 rows=10 width=89) (actual time=1.598..1.616 rows=10 loops=1)  
   Output: id, info, weight, ((weight <=| '100000'::real))  
   Buffers: shared hit=15  
   ->  Index Scan using tbl_info_weight_idx on public.tbl  (cost=12.00..4869.90 rows=2040 width=89) (actual time=1.596..1.612 rows=10 loops=1)  
         Output: id, info, weight, (weight <=| '100000'::real)  
         Index Cond: (tbl.info @@ '''digoal'''::tsquery)  
         Order By: (tbl.weight <=| '100000'::real)  
         Buffers: shared hit=15  
 Planning Time: 0.104 ms  
 Execution Time: 1.655 ms  
(10 rows)  

4.2、反过来排, 权重越小越好的?

select *, weight |=> '-1'::float4 from tbl where info @@ 'digoal' ORDER BY weight |=> '-1'::float4 limit 10;   
  
  id  |                              info                               |  weight   |     ?column?       
------+-----------------------------------------------------------------+-----------+------------------  
  554 | 'aliyun':9 'china':11 'digoal':4 'hello':1 'love':6 'polardb':7 | 0.0363963 | 1.03639627248049  
  192 | 'aliyun':9 'china':11 'digoal':4 'hello':1 'love':6 'polardb':7 | 0.0421133 | 1.04211333394051  
  757 | 'aliyun':9 'china':11 'digoal':4 'hello':1 'love':6 'polardb':7 |  0.124864 | 1.12486390769482  
 1855 | 'aliyun':9 'china':11 'digoal':4 'hello':1 'love':6 'polardb':7 |  0.125145 |  1.1251448392868  
  191 | 'aliyun':9 'china':11 'digoal':4 'hello':1 'love':6 'polardb':7 |  0.134997 |  1.1349972486496  
   60 | 'aliyun':9 'china':11 'digoal':4 'hello':1 'love':6 'polardb':7 |  0.179037 |  1.1790367513895  
 1580 | 'aliyun':9 'china':11 'digoal':4 'hello':1 'love':6 'polardb':7 |   0.21992 | 1.21991994976997  
 1432 | 'aliyun':9 'china':11 'digoal':4 'hello':1 'love':6 'polardb':7 |  0.244062 | 1.24406225979328  
  719 | 'aliyun':9 'china':11 'digoal':4 'hello':1 'love':6 'polardb':7 |  0.244155 |  1.2441546022892  
   81 | 'aliyun':9 'china':11 'digoal':4 'hello':1 'love':6 'polardb':7 |  0.329849 | 1.32984939217567  
(10 rows)  
  
  
postgres=# explain select *, weight |=> '-1'::float4 from tbl where info @@ 'digoal' ORDER BY weight |=> '-1'::float4 limit 10;   
                                         QUERY PLAN                                            
---------------------------------------------------------------------------------------------  
 Limit  (cost=12.00..35.53 rows=10 width=89)  
   ->  Index Scan using tbl_info_weight_idx on tbl  (cost=12.00..4955.27 rows=2101 width=89)  
         Index Cond: (info @@ '''digoal'''::tsquery)  
         Order By: (weight |=> '-1'::real)  
(4 rows)  

4.3、或者离某个指定权重点越近越好的?

postgres=# select *, weight <=> '50'::float4 from tbl where info @@ 'digoal' ORDER BY weight <=> '50'::float4 limit 10;   
  id  |                              info                               | weight  |      ?column?        
------+-----------------------------------------------------------------+---------+--------------------  
   38 | 'aliyun':9 'china':11 'digoal':4 'hello':1 'love':6 'polardb':7 | 49.9803 | 0.0197181701660156  
 1590 | 'aliyun':9 'china':11 'digoal':4 'hello':1 'love':6 'polardb':7 | 50.1099 |  0.109916687011719  
 1153 | 'aliyun':9 'china':11 'digoal':4 'hello':1 'love':6 'polardb':7 | 50.1187 |  0.118724822998047  
  884 | 'aliyun':9 'china':11 'digoal':4 'hello':1 'love':6 'polardb':7 | 50.1466 |  0.146591186523438  
 1329 | 'aliyun':9 'china':11 'digoal':4 'hello':1 'love':6 'polardb':7 | 50.1551 |  0.155113220214844  
  303 | 'aliyun':9 'china':11 'digoal':4 'hello':1 'love':6 'polardb':7 | 49.8312 |  0.168792724609375  
  568 | 'aliyun':9 'china':11 'digoal':4 'hello':1 'love':6 'polardb':7 | 50.1816 |  0.181587219238281  
 1706 | 'aliyun':9 'china':11 'digoal':4 'hello':1 'love':6 'polardb':7 | 49.8142 |  0.185768127441406  
 1136 | 'aliyun':9 'china':11 'digoal':4 'hello':1 'love':6 'polardb':7 | 49.8095 |  0.190475463867188  
 1838 | 'aliyun':9 'china':11 'digoal':4 'hello':1 'love':6 'polardb':7 | 49.7829 |  0.217105865478516  
(10 rows)  
  
postgres=# explain select *, weight <=> '50'::float4 from tbl where info @@ 'digoal' ORDER BY weight <=> '50'::float4 limit 10;   
                                         QUERY PLAN                                            
---------------------------------------------------------------------------------------------  
 Limit  (cost=12.00..35.53 rows=10 width=89)  
   ->  Index Scan using tbl_info_weight_idx on tbl  (cost=12.00..4955.27 rows=2101 width=89)  
         Index Cond: (info @@ '''digoal'''::tsquery)  
         Order By: (weight <=> '50'::real)  
(4 rows)  

如果没有rum, 那么这个搜索需要进行全表匹配, 性能非常差.

仅使用GIN则只能对多值元素进行搜索, 无法实现权重排序的索引加速, 需要回表后排序, 性能也比较差.

postgres=# select relpages from pg_class where relname='tbl';  
 relpages   
----------  
     1462  
(1 row)  
  
  
postgres=# set enable_indexscan = off;  
  
postgres=# set enable_bitmapscan = off;  
  
postgres=# explain (analyze,verbose,timing,costs,buffers) select *, weight <=| '100000'::float4 from tbl where info @@ 'digoal' ORDER BY weight <=| '100000'::float4 limit 10;  
                                                         QUERY PLAN                                                           
----------------------------------------------------------------------------------------------------------------------------  
 Limit  (cost=2787.65..2787.68 rows=10 width=89) (actual time=27.140..27.144 rows=10 loops=1)  
   Output: id, info, weight, ((weight <=| '100000'::real))  
   Buffers: shared hit=1462  
   ->  Sort  (cost=2787.65..2792.91 rows=2101 width=89) (actual time=27.139..27.141 rows=10 loops=1)  
         Output: id, info, weight, ((weight <=| '100000'::real))  
         Sort Key: ((tbl.weight <=| '100000'::real))  
         Sort Method: top-N heapsort  Memory: 27kB  
         Buffers: shared hit=1462  
         ->  Seq Scan on public.tbl  (cost=0.00..2742.25 rows=2101 width=89) (actual time=25.883..26.810 rows=2000 loops=1)  
               Output: id, info, weight, (weight <=| '100000'::real)  
               Filter: (tbl.info @@ '''digoal'''::tsquery)  
               Rows Removed by Filter: 100000  
               Buffers: shared hit=1462  
 Planning Time: 0.078 ms  
 Execution Time: 27.171 ms  
(15 rows)  

rum实现了精准多值列搜索, 同时支持索引内排序, 性能最佳.

以上例子才10.2万条记录, 使用rum扫描的数据块已减少70倍, 数据量再增大, 扫描的数据块将更少, 性能提升将会更加明显.

参考

相关实践学习
使用PolarDB和ECS搭建门户网站
本场景主要介绍基于PolarDB和ECS实现搭建门户网站。
阿里云数据库产品家族及特性
阿里云智能数据库产品团队一直致力于不断健全产品体系,提升产品性能,打磨产品功能,从而帮助客户实现更加极致的弹性能力、具备更强的扩展能力、并利用云设施进一步降低企业成本。以云原生+分布式为核心技术抓手,打造以自研的在线事务型(OLTP)数据库Polar DB和在线分析型(OLAP)数据库Analytic DB为代表的新一代企业级云原生数据库产品体系, 结合NoSQL数据库、数据库生态工具、云原生智能化数据库管控平台,为阿里巴巴经济体以及各个行业的企业客户和开发者提供从公共云到混合云再到私有云的完整解决方案,提供基于云基础设施进行数据从处理、到存储、再到计算与分析的一体化解决方案。本节课带你了解阿里云数据库产品家族及特性。
目录
相关文章
|
16天前
|
数据库
|
1月前
|
存储 关系型数据库 分布式数据库
使用开源PolarDB和imgsmlr进行高效的图片存储和相似度搜索
使用开源PolarDB和imgsmlr进行高效的图片存储和相似度搜索
|
1月前
|
SQL JSON 关系型数据库
MySQL是一个广泛使用的开源关系型数据库管理系统,它有许多不同的版本
【10月更文挑战第3天】MySQL是一个广泛使用的开源关系型数据库管理系统,它有许多不同的版本
133 5
|
1月前
|
关系型数据库 分布式数据库 数据库
PolarDB 开源:推动数据库技术新变革
在数字化时代,数据成为核心资产,数据库的性能和可靠性至关重要。阿里云的PolarDB作为新一代云原生数据库,凭借卓越性能和创新技术脱颖而出。其开源不仅让开发者深入了解内部架构,还促进了数据库生态共建,提升了稳定性与可靠性。PolarDB采用云原生架构,支持快速弹性扩展和高并发访问,具备强大的事务处理能力及数据一致性保证,并且与多种应用无缝兼容。开源PolarDB为国内数据库产业注入新活力,打破国外垄断,推动国产数据库崛起,降低企业成本与风险。未来,PolarDB将在生态建设中持续壮大,助力企业数字化转型。
85 2
|
2月前
惊世骇俗!开源 PolarDB-X 部署安装大冒险,全程心跳与惊喜不断!
【9月更文挑战第8天】作为技术爱好者的我,近期成功完成了开源 PolarDB-X 的部署安装。尽管过程中遇到不少挑战,但通过精心准备环境、下载安装包、配置参数及启动服务等步骤,最终顺利实现部署。本文将详细介绍部署全过程及可能遇到的问题,为您的 PolarDB-X 探索之旅提供参考与启发,希望能让大家在技术海洋里畅游得更加顺利!
148 2
|
1月前
|
关系型数据库 MySQL 分布式数据库
零基础教你用云数据库PolarDB搭建企业网站,完成就送桌面收纳桶!
零基础教你用云数据库PolarDB搭建企业网站,完成就送桌面收纳桶,邀请好友完成更有机会获得​小米Watch S3、小米体重称​等诸多好礼!
零基础教你用云数据库PolarDB搭建企业网站,完成就送桌面收纳桶!
|
2月前
|
关系型数据库 MySQL Serverless
探索PolarDB MySQL版:Serverless数据库的灵活性与性能
本文介绍了个人开发者对阿里云PolarDB MySQL版,特别是其Serverless特性的详细评测体验。评测涵盖了产品初体验、性能观测、Serverless特性深度评测及成本效益分析等方面。尽管试用过程中遇到一些小问题,但总体而言,PolarDB MySQL版表现出色,提供了高性能、高可用性和灵活的资源管理,是个人开发者和企业用户的优秀选择。
|
3月前
|
关系型数据库 MySQL 分布式数据库
PolarDB 与传统数据库的性能对比分析
【8月更文第27天】随着云计算技术的发展,越来越多的企业开始将数据管理和存储迁移到云端。阿里云的 PolarDB 作为一款兼容 MySQL 和 PostgreSQL 的关系型数据库服务,提供了高性能、高可用和弹性伸缩的能力。本文将从不同角度对比 PolarDB 与本地部署的传统数据库(如 MySQL、PostgreSQL)在性能上的差异。
228 1
|
14天前
|
关系型数据库 分布式数据库 数据库
锦鲤附体 | PolarDB数据库创新设计赛,好礼不停!
锦鲤附体 | PolarDB数据库创新设计赛,好礼不停!
|
2月前
|
关系型数据库 分布式数据库 数据库
2024年全国大学生计算机系统能力大赛PolarDB数据库创新设计赛(天池杯)等你来战!
2024年全国大学生计算机系统能力大赛PolarDB数据库创新设计赛(天池杯)等你来战!
2024年全国大学生计算机系统能力大赛PolarDB数据库创新设计赛(天池杯)等你来战!

热门文章

最新文章

相关产品

  • 云原生数据库 PolarDB