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