PostgrSQL有个模块叫pg_trgm,能够对字符串来进行比较类似度,并经过加GIST或者GIN索引来达到提速的效果。在通常的RDBMS中这种需求都会进行全表扫描的,可是PG若是加了这个模块,在必定场景下就可使用索引来提速了。
1、背景
咱们有一个需求根据人员的拼音码(或者药品的拼音码)进行搜索,由于拼音码不必定是全的,故一般给的方案是模糊搜索,在拼音码的首尾两端各加一个百分号,可是效率一般很慢,通常状况下也不建议这么作。
2、环境
OS:CentOS 6.5
DB:PostgreSQL 9.3
3、步骤
1.由于DB是经过源码编译的,因此建立很简单,只要添加一个扩展算法
his=# create extension pg_trgm; CREATE EXTENSION
2.添加索引 在添加索引前,先比较一下二者的查询消耗和速度数组
his=# select count(1) from tbl_user; count --------- 1008215 (1 row) his=# explain analyze select 1 from tbl_user where user_spell like '%CYL%'; QUERY PLAN -------------------------------------------------------------------------------------------- Seq Scan on tbl_user (cost=0.00..35156.69 rows=82 width=0) (actual time=0.357..693.233 rows=1021 loops=1) Filter: ((user_spell)::text ~~ '%CYL%'::text) Rows Removed by Filter: 1007194 Total runtime: 1193.699 ms (4 rows)
--加了索引后的查询,提升了近10倍函数
his=# create index idx_user_spell on tBL_user using gist (user gist_trgm_ops); CREATE INDEX his=# explain analyze select 1 from tbl_user where user_spell like '%CYL%'; QUERY PLAN --------------------------------------------------------------------------------------------- Bitmap Heap Scan on tbl_user (cost=4.92..319.11 rows=82 width=0) (actual time=117.652..120.849 rows=1021 loops=1) Recheck Cond: ((user_spell)::text ~~ '%CYL%'::text) Rows Removed by Index Recheck: 2 -> Bitmap Index Scan on idx_user_spell (cost=0.00..4.90 rows=82 width=0) (actual time=117.291..117.291 rows=1023 loops=1) Index Cond: ((user_spell)::text ~~ '%CYL%'::text) Total runtime: 121.098 ms (6 rows)
4、说明
能够看出来模糊搜索也走了索引,速度有了很大提高,COST也减少不少。这个模块在官网上能够看到有几个自带的函数,主要示例以下:
1.similarity(text,text)
这个函数是用来比较两个字符串的相近程度的,取值范围在0-1之间,彻底相同为1,彻底不一样则为0oop
his=# select similarity('123','789'); similarity ------------ 0 (1 row) his=# select similarity('123','123'); similarity ------------ 1 (1 row) his=# select similarity('123','12345'); similarity ------------ 0.428571 (1 row) --和类似度相反的是他的操做符<->,这个操做符表示的是两组字符串的一个距离,若是是同样的,则是重合的,距离为0,若是彻底不一样,则为1,算法实际就是1减去上面这个类似值,好比如下例子: his=# select '123'<->'123','123'<->'12345','123'<->'678'; ?column? | ?column? | ?column? ----------+----------+---------- 0 | 0.571429 | 1 (1 row)
2.show_trgm(text) 这个函数返回的一串字符数组,有点相似于全文检索的分词,能够用这个函数来作一些Debugspa
his=# select show_trgm('123'),show_trgm('1234'); show_trgm | show_trgm -------------------------+----------------------------- {" 1"," 12",123,"23 "} | {" 1"," 12",123,234,"34 "} (1 row) --上面的类似度就是用的这个分词分出来的,好比123和1234,相同的值有3个,总的不一样值有6个,因此类似度是3/6=0.5 --这个函数能够看出对字符数字能有些进行切割,可是对汉字暂时还无能为力,有必定的限制 his=# select show_trgm('中国人民'),show_trgm('中国人民12'); show_trgm | show_trgm -----------+--------------------- {} | {" 1"," 12","12 "} (1 row) his=# select similarity('中国人','日本'),similarity('中国人','中国人'); similarity | similarity ------------+------------ 0 | 0 (1 row)
5、优势与不足
1.使用这个模块能够对须要使用模糊检索字符串的数据进行加索引提速
2.对字母或数字的类似度比较较为满意,对汉字还不支持
3.若是模糊检索的数据结果集较大,运行速度可能比较慢,好比只搜索一个字母匹配的 %C%code