ElasticSearch(提高篇)

前言

Elasticsearch的簡單入門請參考之前寫的一篇文章Elasticsearch簡單入門篇,這篇簡單介紹啦Elasticsearch的基本安裝、Docker安裝方法、基本的概念,以及如何使用Java代碼實現對ElasticsearchCRUD操作等入門知識。

內容摘要

Elastic Search分享

1.1.Elastic Stack應用場景

  • 網站搜索、代碼搜索等(例如生產環境的日志收集 ——格式化分析——全文檢索——系統預警)
  • 日志管理與分析、應用系統性能分析、安全指標監控等

1.2.Elastic Stack技術架構

Elastic static家族產品

image-20190802210856808

高級架構

Elastic的技術架構可以簡單,也可以高級,它是很具有擴展性的,最簡單的技術架構就是使用Beats進行數據的收集,Beats是一種抽象的稱呼,具體的可以是使用FileBeat收集數據源為文件的數據或者使用TopBeat來收集系統中的監控信息,可以說類似Linux系統中的TOP命令,當然還有很多的Beats的具體實現,再使用logstash進行數據的轉換和導入到Elasticsearch中,最后使用Kibana進行數據的操作以及數據的可視化等操作。

當然,在生產環境中,我們的數據可能在不同的地方,例如關系型數據庫Postgre,或者MQ,再或者Redis中,我們可以統一使用Logstash進行數據的轉換,同時,也可以根據數據的熱度不同將ES集群架構為一種冷溫熱架構,利用ES的多節點,將一天以內的數據稱謂熱數據,讀寫頻繁,就存放在ES熱節點中,七天以內的數據稱之為溫數據,就是偶爾使用的數據存放在溫節點中,將極少數會用到的數據存放在冷節點中

image-20190802212002520

1.3.ES基本概念回顧

文檔(Document)

Elasticsearch面向文檔性,文檔就是所有可搜索數據的最小單位。比如,一篇PDF中的內容,一部電影的內容,一首歌等,文檔會被序列化成JSON格式,保存在Elasticsearch中,必不可少的是每個文檔都會有自己的唯一標識,可以自己指定,也可以由Elasticsearch幫你生成。類似數據庫的一行數據。

元數據(標注文檔信息)

"_index" : "user",
"_type" : "_doc",
"_id" : "l0D6UmwBn8Enzbv1XLz0",
"_score" : 1.6943597,
"_source" : {
    "user" : "mj",
    "sex" : "男",
    "age" : "18"
}
  • _index:文檔所屬的索引名稱。
  • _type:文檔所屬的類型名。
  • _id:文檔的唯一標識。
  • _version:文檔的版本信息。
  • _score:文檔的相關性打分。
  • _source:文檔的原始JSON內容。

索引(index)

索引是文檔的容器,是一類文檔的集合,類似關系數據庫中的表,索引體現的是一種邏輯空間的概念,每個索引都應該有自己的Mapping定義,用于定義包含文檔的字段名和字段類型。其中Shard(分片)體現的是物理空間的一種概念,就是索引中的數據存放在Shard上,因為有啦集群,要保證高空用,當其中一個機器崩潰中,保存在它上的分片數據也能被正常訪問,因此,存在啦分片副本

索引中有兩個重要的概念,MappingSettingMapping定義的是文檔字段和字段類型,Setting定義的是數據的不同分布。

類型(Type)

  • 在7.0之前,一個index可以創建多個Type。之后就只能一個index對應一個Type

節點(Node)

一個節點就是一個Elaseticsearch實例,本質就是一個JAVA進程。每一個節點啟動后,默認就是一個master eligible節點。就是具備成為master資格的節點,你也可以狠心的指定它沒有這個資格(node.master:false),

第一個節點啟動后,他就選自己成為Master節點類,每一個節點上都保存了集群狀態,但是,只有Master才能修改集群狀態信息。集群狀態信息就比如:

  • 所有的節點信息。
  • 所有的索引信息,索引對應的mapping信息和setting信息。
  • 分片的路由信息。

分片(shard)

  • 主分片:用于解決數據的水平擴展問題,通過主分片就數據分布在集群內的不同節點上,主分片在創建索引的時候就指定了,后面就不允許修改,除非重新定義Index
  • 副本:用于解決高可用的問題,分片是主分片的拷貝。副本分片數可以動態的調整,增加副本數量可以在一定的程度上提高服務的可用性。關于主分片的理解可以如下圖,看是怎樣實現高可用的,
image-20190802231314640
"settings" : {
    "index" : {
        // 設置主分片數
        "number_of_shards" : "1",
        "auto_expand_replicas" : "0-1",
        "provided_name" : "kibana_sample_data_logs",
        "creation_date" : "1564753951554",
        // 設置副本分片數
        "number_of_replicas" : "1",
        "uuid" : "VVMLRyw6TZeSfUvvLNYXEw",
        "version" : {
            "created" : "7010099"
        }
    }
}

1.4.倒排索引

正排索引:就是文檔ID到文檔內容的索引,簡單講,就是根據ID找文檔。

倒排索引:就是根據文檔內容找文檔

倒排索引包含如下信息:

  • 單詞詞典:用于記錄所有文檔的單詞,以及單詞到倒排列表的關聯關系。
  • 倒排列表:記錄的是單詞對應的文檔集合,由倒排索引項組成,其中包含
    • 文檔ID
    • 單詞出現的次數,用于相關性的評分
    • 單詞出現的位置
    • 偏移量,用于記錄單詞的開始位置和結束位置,用于單詞的高亮顯示

舉例說明什么是正排索引倒排索引,其中正排索引如下:

文檔ID 文檔內容
1101 Elasticsearch Study
1102 Elasticsearch Server
1103 master Elasticsearch

講上例Elasticsearch單詞修改為倒排索引,如下:

文檔ID(Doc ID) 出現次數(TF) 位置(Position) 偏移量(Offset)
1101 1 0 <0,13>
1102 1 0 <0,13>
1103 1 1 <7,20>

Elasticsearch中的每一個字段都有自己的倒排索引,也可以指定某些字段不做索引,可以節省存儲空間,缺點就是不能被搜索到。

1.5.Analyzer分詞

Analysis:文本分析,就是將文本轉換為單詞(term或者token)的過程,其中Analyzer就是通過Analysis實現的,Elasticsearch給我們內置例很多分詞器。

  • Standard Analyzer:默認的分詞器,按照詞切分,并作大寫轉小寫處理
  • Simple Analyzer:按照非字母切分(符號被過濾),并作大寫轉小寫處理
  • Stop Anayzer:停用詞(theis)切分,并作大寫轉小寫處理
  • Whitespace Anayzer:空格切分,不做大寫轉小寫處理
  • IK:中文分詞器,需要插件安裝
  • ICU:國際化的分詞器,需要插件安裝
  • jieba:時下流行的一個中文分詞器。安裝方法見附錄

PS:Elasticsearch安裝插件,[root@34d02ff9d16c elasticsearch]# bin/elasticsearch-plugin install analysis-icu

查看已經安裝的插件:bin/elasticsearch-plugin list

1.6.Search API

ES中,我們可以使用URL SearchRequest Body Search進行相關的查詢操作。

URL 查詢

使用基本的查詢

GET /user/_search?q=2012&df=title&sort=year:desc&from=0&size=10
{
    ??profile??: true
}
  • 使用q指定查詢的字符串
  • 使用df指定查詢的字段
  • 使用sort進行排序,使用fromsize指定分頁
  • 使用profile可以查詢查詢是如何進行查詢的

指定所有字段的泛查詢

GET /user/_search?q=2012
{
    "profile":"true"
}

指定字段的查詢


GET /user/_search?q=title:2012&sort=year:desc&from=0&size=10&timeout=1s
{
    "profile":"true"
}

Term查詢

GET /user/_search?q=title:Beautiful Mind
{
    "profile":"true"
}
  • 上例中的BeautifulMind就是兩個TermTerm是查詢中最小的單位。
  • Term查詢是OR的關系,在上例中就是title字段包含Beautiful或者包含Mind都會被檢索到。

Phrase查詢

GET /user/_search?q=title:"Beautiful Mind"
{
    "profile":"true"
}
  • 使用引號表示Phrase查詢
  • Phrase查詢表示的不僅是And的關系,即Title字段中不僅要包含Beautiful Mind,而且。順序還要一致。

分組查詢

GET /user/_search?q=title:(Beautiful Mind)
{
    "profile":"true"
}
  • 使用中括號表示分組查詢,一般使用Term查詢的時候都會帶上分組查詢。

布爾查詢

  • 使用 ANDORNOT或者||&&!
  • 還可以使用+(表示must),使用-(表示must_not
  • 需要注意的是必須大寫
GET /user/_search?q=title:(Beautiful NOT Mind)
{
    "profile":"true"
}
GET /user/_search?q=title:(Beautiful %2BMind)
{
    "profile":"true"
}

PS:%2B表示的就是+,上例子表示的就是title字段中既要包含Beautiful,也要包含Mind字段

范圍查詢

GET /user/_search?q=title:beautiful AND age:[2002 TO 2018%7D
{
    "profile":"true"
}
  • 使用[ ]表示閉區間,使用{ }表示開區間,例如age :[* TO 56]
  • 使用算術符表示范圍,例如year :>=2019 && <=1970

PS:URL Search還有很多查詢方式。例如通配符查詢,正則插敘,模糊匹配,相似查詢,其中通配符查詢不建議使用。

Request Body 查詢

將查詢的條件參數放在Request Body中,調用查詢接口,就是Request Body查詢,

基本 的查詢

POST /movies,404_idx/_search?ignore_unavailable=true
{
  "profile": true,
    "query": {
        "match_all": {}
    }
}
  • 使用gnore_unavailable=true可以避免索引404_idx不存在導致的報錯
  • profileURL Search查詢一樣,可以看到查詢的執行方式

分頁查詢

POST /movies/_search
{
  "from":10,
  "size":20,
  "query":{
    "match_all": {}
  }
}

排序查詢

POST /movies/_search
{
  "sort":[{"order_date":"desc"}],
  "query":{
    "match_all": {}
  }
}

過濾要查詢的字段

POST /movies/_search
{
  "_source":["order_date"],
  "query":{
    "match_all": {}
  }
}
  • 如果一個文檔中的字段太多,我們不需全部字段顯示,就可以使用_source指定字段。可以使用通配符。

使用腳本查詢

  • ES中的文檔字段進行一定的處理后,再根據這個新的字段進行排序,
GET /movies/_search
{
  "script_fields": {
    "new_field": {
      "script": {
        "lang": "painless",
        "source": "doc['name'].value+'是大佬'"
      }
    }
  },
  "query": {
    "match_all": {}
  }
}

Term查詢

POST /movies/_search
{
  "query": {
    "match": {
      "title": "last christmas"
    }
  }
}

POST movies/_search
{
  "query": {
    "match": {
      "title": {
        "query": "last christmas",
        "operator": "and"
      }
    }
  }
}
  • 使用match,表示的就是OR的關系
  • 使用operator,表示查詢方式

Math_phrase查詢

POST movies/_search
{
  "query": {
    "match_phrase": {
      "title":{
        "query": "one love",
         "slop": 4
      }
    }
  }
}
  • slop指定查詢的字符中允許出現的字符

1.7.Dynamic Mapping

Mapping可以簡單的理解為數據庫中的Schema定義,用于定義索引中的字段的名稱定義字段的類型字段的倒排索引指定字段使用何種分詞器等。Dynamic Mapping意思就是在我們創建文檔的時候,如果索引不存在,就會自動的創建索引,同時自動的創建MappingElasticSearch會自動的幫我們推算出字段的類型,當然,也會存在推算不準確的時候,就需要我們手動的設置。常用的字段類型如下:

  • 簡單類型:TextDateIntegerBoolean
  • 復雜類型:對象類型和嵌套類型。

我們可以使用GET /shgx/_mapping查詢索引的Mapping的設置,需要注意的是以下幾點:

  • 當我們對索引中的文檔新增字段時候,希望可以更新索引的Mapping就可以可以設置Dynamic:true
  • 對于已經有數據的字段,就不再允許修改其Mapping,因為Lucene生成的倒排索引后就不允許修改。

Dynamic Mapping可以設置三個值,分別是:

  • true:文檔可被索引,新增字段也可被索引,Mapping也會被更新。
  • false:文檔可被索引,新增字段不能被索引,Mapping不會被更新。
  • strict:新增字段寫入,直接報錯。

如何寫Mapping

第一種方式是參考官方API,純手工寫,也可以先創建一個臨時的IndexElasticSearch自動當我們推斷出基本的Mapping,然后自己在改吧改吧,最后把臨時索引刪掉就是啦。下面列舉一些常用的Mapping設置屬性:

  • index:可以設置改字段是否需要被索引到。設置為false就不會生成倒排索引,節省啦磁盤開銷
  • null_value:可以控制NULL是否可以被索引
  • cope_to:將字段值放在一個新的字段中,可以使用新的字段search,但這個字段不會出現在_source中。
  • anaylzer:指定字段的分詞器
  • search_anaylzer:指定索引使用的分詞器
  • index_options:控制倒排索引的生成結構,有四種情況
    • docs:倒排索引只記錄文檔ID
    • freqs:記錄文檔IDTerm
    • positions:記錄文檔IDTermTerm Position
    • offsets:記錄文檔IDTermTerm Positionoffsets

PS:Text類型的字段默認的是Position,其它類型默認的是docs,記錄的越多,占用的存儲空間就越大。

1.8.Aggregation聚合分析

ElasticSearch不僅僅是搜索強大,他的統計功能也是相當的強大的,聚合分析就是統計整個數據的一個分類數量等,例如武侯區有多少新樓盤。天府新區有多少新樓盤,通過聚合分析我們只需要寫一條語句就可以得到。在加上Kibana的可視化分析,簡直就是清晰,高效。常用的集合有以下幾種:

  • Bucket Aggregation:滿足特定條件的一些集合,使用關鍵字terms
  • Metric Aggregation:簡單的數學運算,對字段進行統計分析,使用關鍵字minmaxsumavg等,使用關鍵字aggs
  • Pipeline Aggregation:二次聚合
  • Matrix Aggregation:對多個字段進行操作,提供一個結果矩陣

Bucket分析示例

GET kibana_sample_data_flights/_search
{
    "size": 0,
    "aggs":{
        "flight_dest":{
            "terms":{
                "field":"DestCountry"
            }
        }
    }
}

Metric分析示例

GET kibana_sample_data_flights/_search
{
    "size": 0,
    "aggs":{
        "flight_dest":{
            "terms":{
                "field":"DestCountry"
            },
            "aggs":{
                "avg_price":{
                    "avg":{
                        "field":"AvgTicketPrice"
                    }
                },
                "max_price":{
                    "max":{
                        "field":"AvgTicketPrice"
                    }
                },
                "min_price":{
                    "min":{
                        "field":"AvgTicketPrice"
                    }
                }
            }
        }
    }
}

附錄一

相關閱讀

更多文章,更好的閱讀體驗,請前往個人網站查看 碼醬博客

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容