Elasticsearch解決問題之道——請亮出你的DSL!

0、引言
在業務開發中,咱們每每會陷入開發的細枝末節之中,而忽略了事物的本源。
常常有同窗問到:
1, 業務代碼實現結果和kibana驗證不一致。
好比:個人python或者java程序檢索結果怎麼和kibana裏面不一致?
2, 個人某個關鍵詞明明匹配,但怎麼檢索不到?
好比:星球羣討論的「三星」ik_max_word + match_phrase匹配問題。
3, 熟悉sql,但轉dsl就不會寫了。
好比:作聚合搜索的話,select * from user where usrid >5 group by userid having count(userid) >3 這個功能能在一個dsl實現嗎 ?
4, 查詢慢,但不知道什麼緣由致使的。
好比:elasticsearch有8億數據查詢慢是怎麼回事,有什麼辦法優化。
等等等等…..
以上的看似複雜的問題,若是轉換成DSL,清楚的寫出來,梳理清楚問題的前因後果,問題就天然解決了一大半。
因此,請亮出你的dsl,不論什麼語言的檢索,轉換到es查詢都是sql查詢,在es中對應dsl語法,es再拆解好比:分詞match_phrase拆解成各term組合,最終傳給lucene處理。
亮出你的dsl,確保編程裏的實現和你的kibana或者head插件一致是很是重要、很容易被忽視的工做。
若是對dsl拆解不理解,那就再加上profile:true或者explain:true拆解結果一目瞭然。
一、啥是Elasticsearch DSL?
維基百科定義:領域特定語言(英語:domain-specific language、DSL)指的是專一於某個應用程序領域的計算機語言。又譯做領域專用語言。
Elasticsearch提供基於JSON的完整查詢DSL來定義查詢。 將Query DSL視爲查詢的AST(抽象語法樹),由兩種類型的子句組成:
一、葉子查詢子句
葉查詢子句查找特定字段中的特定值,例如匹配,術語或範圍查詢。 這些查詢能夠單獨使用。
二、複合查詢子句
複合查詢子句能夠組合其餘葉子或複合查詢,用於以邏輯方式組合多個查詢(例如bool或dis_max查詢),或更改其行爲(例如constant_score查詢)。
給個例子,一看就明白。
1GET "localhost:9200/_search
2{
3 "query": {
4 "bool": {
5 "must": [
6 { "match": { "title": "Search" }},
7 { "match": { "content": "Elasticsearch" }}
8 ],
9 "filter": [
10 { "term": { "status": "published" }},
11 { "range": { "publish_date": { "gte": "2015-01-01" }}}
12 ]
13 }
14 }
15}
16
看到這裏,可能會有人着急了:「我X,這不是官網定義嗎?再寫一遍有意思嗎?」
引用一句雞湯話,「再顯而易見的道理,在中國,至少有一億人不知道」。一樣的,再顯而易見的問題,在Elasticsearch技術社區也會有N多人提問。
基礎認知不怕重複,可怕的是對基礎的專研、打磨、夯實。
二、DSL的全局認知
Elasticsearch相關的核心操做,廣義上可作以下解讀,不必定涵蓋全,僅拋磚引玉,說明DSL的重要性。
從大到小。
2.1 維度1:集羣的管理。
集羣的管理,通常咱們會使用Kibana或者第三方工具Head插件、cerebro工具、elastic-hq工具。
基本上硬件的(磁盤、cpu、內存)使用率、集羣的健康狀態都能一目瞭然。
但基礎的DSL會更便捷,便於細粒度分析問題。
如:集羣狀態查詢:
1GET /_cluster/stats?human&pretty
如:節點熱點線程查看:
1GET /_nodes/hot_threads
如:集羣分片分配狀況查看:
1GET /_cluster/allocation/explain
2.2 維度2:索引的生命週期管理。
索引生命週期是一直強調的概念,主要指索引的「生、老、病、死」的全過程鏈條的管理。
2.2.一、生:建立索引。
建立索引咱們優先使用較單純index更靈活的template模板。
建立索引相似Mysql的建立表的操做,提早設計好表結構對應ES是提早設計好Mapping很是重要。
兩個維度:
一、血淋淋的教訓:業務上精煉再精煉,一個索引搞定的毫不多表關聯。
二、提早設計好字段及擴展字段,即使ES支持動態添加字段。
三、不使用動態映射,全部字段本身定義,節省存儲空間。
舉例:
1PUT _template/template_1
2{
3 "index_patterns": ["te", "bar"],
4 "settings": {
5 "number_of_shards": 1
6 },
7 "mappings": {
8 "_doc": {
9 "_source": {
10 "enabled": false
11 },
12 "properties": {
13 "host_name": {
14 "type": "keyword"
15 }
16 }
17 }
18 }
19}
20'
21
2.2.二、老:滾動索引、關閉索引或者凍結索引。
舉例:
1POST /my_index/_freeze
2POST /my_index/_unfreeze
2.2.三、病:索引出了問題。
如:索引清理緩存。
1POST /twitter/_cache/clear
如:某緣由致使分片從新分配,_recovery查看分片分配狀態。
1GET index1,index2/_recovery?human
2.2.四、死:刪除索引。
1DELETE my_index
高版本的索引生命週期管理推薦使用:ILM功能。
2.3 維度3:數據的增刪改查。
這個是你們再熟悉不過的了。
2.3.1 增
單條導入數據、批量bulk寫入數據、第三方同步數據(本質也是批量)。
舉例:
1POST _bulk
2{ "index" : { "_index" : "test", "_id" : "1" } }
3{ "field1" : "value1" }
2.3.2 刪
刪除數據包括:指定id刪除 delete和批量刪除delete_by_query(知足給定條件)。
2.3.3 改
更新操做。包括:指定id的update/upsert或者批量更新update_by_query。
2.3.4 查
這是ES的重頭戲。包含但不限於:
一、支持精確匹配查詢的:term、range、exists、wildcard、prefix、fuzzy等。
二、支持全文檢索的:match、match_phrase、query_string、multi_match等
2.4 維度4:數據統計分析
Elasticsearch解決問題之道——請亮出你的DSL!java

一、Bucketing分桶聚合
舉例:最經常使用的terms就相似Mysql group by功能。
二、Metric計算聚合
舉例:類比Mysql中的: MIN(), MAX(), SUM() 操做。
三、Pipeline針對聚合結果聚合
舉例:bucket_script實現相似Mysql的group by 後having的操做。
2.5 更多其餘維度
留給你們結合業務場景思考添加。
三、實踐乾貨
講了那麼多,實踐中遇到問題咋辦?
這裏把開頭提到的幾個問題逐一解答一下。
3.1,業務代碼實現結果和kibana驗證不一致。
實際Mysql業務中,咱們通常是先驗證sql沒有問題,再寫業務代碼。
實際ES業務中,也同樣,先DSL確認沒有問題,再寫業務代碼。
寫完java或者python後,打印DSL,覈對是否徹底一致。
不一致的地方基本就是結果和預期不一致的緣由所在。
3.2,個人某個關鍵詞明明匹配,但怎麼檢索不到?
第一步:藉助analyzer API分析查詢語句和待查詢document分詞結果。
1GET my_index/_analyze
2{
3 "field": "text",
4 "text": "華爲鴻蒙系統發佈-可隨時替代安卓",
5 "analyzer":"ik_smart"
6}
這個API的重要性,再怎麼強調都不爲過。
第二步:能夠藉助profile:true查看細節。第三步:覈對match_phrase詞序的原理。
3.3,熟悉sql,但轉dsl就不會寫了。
6.3版本後已經支持sql,若是不會寫,能夠藉助translate 以下API翻譯一下。
Elasticsearch解決問題之道——請亮出你的DSL!node

不夠精確,但足夠參考用了,須要根據業務細節微調。
固然,仍是建議,從業務出發,本身寫DSL。
3.4,查詢慢,但不知道什麼緣由致使的。
從大往小,逐步細化排解
思路1:索引層面。
8億條分散到多個索引、多個副本當中,仍是一個索引?
思路2:Mapping映射設計層面。
舉例,設計高效檢索Number類型建議改爲keyword。
詳細參考攜程架構師的文章:number?keyword?傻傻分不清楚
思路3:檢索DSL優化層面
注意:能使用filter過濾檢索的就不要使用query,原理參考我以前梳理的文章:
吃透 | Elasticsearch filter和query的不一樣
思路4:返回字段層面
有沒有檢索的使用_source:"" 限定返回的字段,
若是沒有,會全字段返回,數據量大的話,也會慢。
思路5:DSL 調試
調試方法:DSL執行語句中加上profile:true .
或者藉助:xpack可視化插件排查。
這樣,會打印出對應查詢的細節花費時間,讓你明明白白知道那裏慢了。
思路6:日誌查詢
查詢的時候,查詢ES日誌,看看有沒有大量的gc。
看看有沒有錯誤日誌,錯誤日誌的處理就是優化的方向。
思路7:藉助cerebro或者xpack mointer監視集羣狀態
看一看,集羣堆內存、cpu、負載的使用狀況。
思路8:外部思惟
想想,查詢的時候,有沒有並行的寫入操做?
那麼查詢的時候慢,是否是寫入壓力大隊集羣形成的影響。
思路9:排除網絡慢的緣由
內網查詢仍是外網映射查詢,返回時間也不同。
思路10:其餘問題
結合業務場景進行分析,本身的業務代碼邏輯的問題。
必定要轉成DSL進行最小化定位。
四、小結
實際業務中的問題遠比上面複雜。但開發的過程當中,不少時候,走的過久忘記了出發的目的是什麼。
本文僅起到拋磚引玉做用,更多複雜DSL、腳本、自定義評分等DSL沒有涉及,不過原理一致。
因此,遇到問題,切莫亂求醫。亮出你的DSL,追根溯源、條分縷析逐步細化,問題會迎刃而解。
一塊兒加油,共勉!python

相關文章
相關標籤/搜索