Elasticsearch 查詢革新:探索 Wildcard 類型的高效模糊匹配策略
1、背景
在生產(chǎn)使用中,Elasticsearch 除了精確匹配的要求,也會有模糊查詢的場景。
2、解決方案探討
面對這種問題 ,傳統(tǒng)的解決方案有兩種:
2.1 方案一:ngram 分詞器
使用 ngram 分詞器對存入的數(shù)據(jù)進(jìn)行精細(xì)化的拆分,利用細(xì)顆粒度的 token 進(jìn)行快速的召回。
這是一個利用空間換時間的方案,細(xì)化查詢所需的詞根內(nèi)容,利用精確匹配結(jié)果大范圍的命中來達(dá)到模糊效果。
PUT test-005
{
  "settings": {
    "index.max_ngram_diff": 10,
    "analysis": {
      "analyzer": {
        "my_analyzer": {
          "tokenizer": "my_tokenizer"
        }
      },
      "tokenizer": {
        "my_tokenizer": {
          "type": "ngram",
          "min_gram": 3,
          "max_gram": 10,
          "token_chars": [
            "letter",
            "digit"
          ]
        }
      }
    }
  },
  "mappings": {
    "properties": {
      "title": {
        "type": "text",
        "analyzer": "my_analyzer",
        "fields": {
          "keyword": {
            "type": "keyword"
          }
        }
      }
    }
  }
}
 
POST test-005/_bulk
{"index":{"_id":1}}
{"title":"英文官網(wǎng)承認(rèn)劉強(qiáng)東一度被捕的原因是涉嫌性侵"}
{"index":{"_id":2}}
{"title":"別提了朋友哥哥劉強(qiáng)東窗事發(fā)了"}
{"index":{"_id":3}}
{"title":"劉強(qiáng)東施效顰,沒想到竟然收獲了流量"}
{"index":{"_id":4}}
{"title":"劉強(qiáng)東是誰?我不認(rèn)識"}
 
POST test-005/_search
{
  "query": {
    "match_phrase": {
      "title": "劉強(qiáng)東"
    }
  }
}- 優(yōu)點:召回快,性能消耗小;
 - 缺點:有不小的空間消耗,顆粒度越細(xì),消耗越大。同時,有一定的學(xué)習(xí)成本,需要對分詞器有成熟的了解,不適合新手。
 
這里有個明顯的使用案例,如下圖所示,使用 ngram 的 test2 索引比原來使用 keyword 的索引空間大小大了接近10倍。
圖片
2.2 方案二:wildcard 查詢
使用 wildcard 查詢,這是一項支持通配符的模糊檢索功能,有點類似 SQL 中的 like 匹配。
為了實現(xiàn)通配符和正則表達(dá)式的查詢,Ealsticsearch 依賴的 Lucene4.0 會將輸入的字符串模式構(gòu)建成一個DFA (Deterministic Finite Automaton),而帶有通配符的pattern構(gòu)造出來的DFA可能會很復(fù)雜,開銷很大。
具體分析:
https://elasticsearch.cn/article/171
https://elasticsearch.cn/article/186
- 優(yōu)點:使用簡單,也不需要額外的存儲資源。
 - 缺點:性能消耗巨大,濫用則可能會造成線上事故。
 
面對兩個各有所長,甚至有點“臥龍鳳雛”的方案,ES 在 7.9 版本推出了 wildcard 字段類型來解決模糊匹配的場景需求。
3、wildcard 類型使用詳解
Elasticsearch 的 wildcard 字段類型最早在 7.9 版本中引入。這個版本加入了對 wildcard 類型的支持,旨在改善模糊匹配的查詢效率和性能,特別是在處理大量文本數(shù)據(jù)時。這一新特性主要針對了之前版本中 wildcard 查詢的性能問題,提供了更高效的方式來處理通配符和正則表達(dá)式的搜索需求。
圖片
https://www.elastic.co/guide/en/elasticsearch/reference/7.9/release-highlights.html
我們先來看下 wildcard 類型怎么使用:
先定義一個 wildcard 類型的字段
PUT my-index-000001
{
  "mappings": {
    "properties": {
      "my_wildcard": {
        "type": "wildcard"
      }
    }
  }
}為其寫入一個文檔
PUT my-index-000001/_doc/1
{
  "my_wildcard" : "This string can be quite lengthy"
}然后使用 wildcard 查詢?nèi)缦滤荆?/p>
GET my-index-000001/_search
{
  "query": {
    "wildcard": {
      "my_wildcard": "*quite*lengthy"
    }
  }
}結(jié)果為
{
  "took" : 6,
  "timed_out" : false,
  "_shards" : {
    "total" : 1,
    "successful" : 1,
    "skipped" : 0,
    "failed" : 0
  },
  "hits" : {
    "total" : {
      "value" : 1,
      "relation" : "eq"
    },
    "max_score" : 3.8610575,
    "hits" : [
      {
        "_index" : "my-index-000001",
        "_type" : "_doc",
        "_id" : "1",
        "_score" : 3.8610575,
        "_source" : {
          "my_wildcard" : "This string can be quite lengthy"
        }
      }
    ]
  }
}有時候我們需要忽略大小寫,可以在 wildcard 查詢使用 case_insensitive 參數(shù)。
GET my-index-000001/_search
{
  "query": {
    "wildcard": {
      "my_wildcard": {
        "value": "*Quite*lengthy",
        "case_insensitive": true
      }
    }
  }
}4、wildcard 原理
關(guān)于 wildcard 字段的實現(xiàn),官方在推出該字段的時候發(fā)布了相關(guān)的說明:
新的 wildcard 字段使用以下兩種數(shù)據(jù)結(jié)構(gòu)以這種方式自動加速通配符和正則表達(dá)式搜索:
- 字符串中所有3個字符序列的 n-gram 索引。
 - 完整原始文檔值的 “二進(jìn)制 doc value” 存儲。第一點,底層還是 ngram 的分詞去實現(xiàn)模糊查詢的場景,但是這里的 ngram 顆粒度是 3,從功能上滿足了模糊查詢的需求和保證了 wildcard 查詢的高性能。
 
第二點,使用了 ES 中常見的正排+列存數(shù)據(jù)存儲格式 doc value,在這里一個主要的效果就是在自動查詢驗證由 n-gram 語法匹配產(chǎn)生匹配候選的同時利用了doc value格式相對較高的壓縮比。
5、測試
現(xiàn)在來看下 wildcard 實際的表現(xiàn)。
5.1 空間大小
如下圖所示,可以看到使用 wildcard 字段的索引與原索引相差不大。
圖片
5.2 查詢效率
查詢dsl  | keyword類型  | wildcard類型  | 
wildcard:”紅豆”  | 715ms  | 71ms  | 
wildcard:”006-612014”  | 633ms  | 22ms  | 
wildcard:”55”  | 584ms  | 188ms  | 
wildcard:”11”  | 1359ms  | 357ms  | 
注:這里省卻了索引詳細(xì)信息,只需知道是同一個索引的比對測試。
綜上所述,在模糊搜索字段區(qū)分度很低的情況下 如:模糊查詢單個數(shù)字,此時優(yōu)化效率rt大概是之前的1/3左右,區(qū)分度高的場景rt大概是之前的1/15左右,有明顯效果。
6、小結(jié)
1.可以說 wildcard 字段類型滿足了模糊查詢的主要需求,同時也提供了相對較高的查詢性能;
2.wildcard 針對于 ngram 分詞器有著不小的空間優(yōu)勢。
3.wildcard 雖然有著不小的優(yōu)勢,但是查詢效率與數(shù)據(jù)的區(qū)分度有著很強(qiáng)的關(guān)聯(lián),在一些區(qū)分度較低的場景下效率與性能消耗依舊很嚴(yán)重。
4.相比 ES 在精確查詢場景優(yōu)秀的性能表現(xiàn)(即 term keyword 的高效,平穩(wěn)在毫秒級的返回),wildcard 字段在模糊查詢場景下的使用還是需要研發(fā)人員根據(jù)實際場景測試選擇。
7、作者介紹
金多安,Elastic 認(rèn)證專家,Elastic資深運維工程師,死磕Elasticsearch知識星球嘉賓,星球Top活躍技術(shù)專家,搜索客社區(qū)日報責(zé)任編輯
銘毅天下審稿并做了部分微調(diào)。















 
 
 













 
 
 
 