【轉】Elasticsearch學習筆記

1、經常使用術語

索引(Index)、類型(Type)、文檔(Document)

  • 索引Index是含有相同屬性的文檔集合。索引在ES中是經過一個名字來識別的,且必須是英文字母小寫,且不含中劃線(-);可類比於 MySQL 中的 database ;在 7.0中,因爲類型(Type)的移除,咱們能夠理解爲,一個索引就是一張 table。
  • 一個索引中能夠定義一個或多個類型Type,文檔必須屬於一個類型;可類比於 MySQL 中的 table;
  • 文檔Document是能夠被索引的基本數據單位。文檔是Elasticsearch中最小的數據存儲單位。可類比於 MySQL 中 一個table 中的一行記錄

注意事項:
從ES6.0開始,官方便不建議一個索引中建立多個類型;在ES7.0中,更是移除了類型(Type)這個概念。爲何呢?
在Elasticsearch索引中,不一樣類型(Type)中具備相同名稱的字段在內部由相同的Lucene字段支持。一個index中多個Type在Lucene中會有許多問題。具體的能夠參考官方說明:Removal of mapping typeshtml

節點Node、集羣Cluster

  • 節點:一個ES運行實例,是集羣的的構成單元
  • 集羣:由1個(只有1個節點也是1個集羣)或多個節點組成,對外提供服務

分片Shard(集羣—提升吞吐與計算性能)、副本Replica(主從—提升可用性)

  • 在ES中,每一個索引都有多個分片,每一個分片都是一個Lucene索引。假設一個索引的數據量很大,就會形成硬盤壓力很大,同時,搜索速度也會出現瓶頸。咱們能夠將一個索引分爲多個分片,從而分攤壓力;分片同時還容許用戶進行水平地擴展和拆分,以及分佈式的操做,能夠提升搜索以及其餘操做的效率。
  • 拷貝一份分片,就完成了分片的備份。備份的好處是,當一個主分片出現問題時,備份的分片就能代替工做,從而提升了ES的可用性。同時,備份的分片還能夠執行搜索操做,以分攤搜索的壓力。ES禁止同一個分片的主分片和副本分片在同一個節點上。

RESTful API

Elasticsearch 集羣對外提供 RESTful APIjava

  • REST - REpresentational State Transfer
  • URI 指定資源,如Index、Document等
  • Http Method 指明資源操做類型,如GET、POST、PUT、DELETE等

倒排索引

  • 正排索引node


     
    正排索引
 
倒排索引
 
全文檢索查詢流程

倒排索引組成

倒排索引是搜索引擎的核心,主要包含兩部分:python

  • 單詞詞典(Term Dictionary)
    • 記錄全部文檔的單詞,通常都比較大
    • 記錄單詞到倒排列表的關聯信息
    • 單詞字典的實現通常是用B+Tree,能兼顧內存與磁盤性能,保障增刪改查高效
  • 倒排列表(Posting List)
    • 倒排列表(Posting List)記錄了單詞對應的文檔集合,由倒排索引項(Posting)組成
    • 倒排索引項(Posting)主要包含以下信息:
      • 文檔ld,用於獲取原始信息
      • 單詞頻率(TF,Term Frequency),記錄該單詞在該文檔中的出現次數,用於後續相關性算分
      • 位置(Position),記錄單詞在文檔中的分詞位置(多個),用於作詞語搜索(Phrase Query)
      • 偏移(Offset),記錄單詞在文檔的開始和結束位置,用於作高亮顯示
 
倒排列表例子

 

 
單詞字典指向倒排列表例子

ES中的倒排索引

es存儲的是一個json格式的文檔,其中包含多個字段,每一個字段會有本身的倒排索引。nginx

 

 
ES中倒排索引

相關性算分

相關性算分是指文檔與查詢語句間的相關度,英文爲 relevancegit

  • 經過倒排索引能夠獲取與查詢語句相匹配的文檔列表,那麼如何將最符合用戶查詢需求的文檔放到前列呢?
  • 本質是一個排序問題,排序的依據是相關性算分
 

相關性算分的幾個重要概念

  • Term Frequency(TF)詞頻,即單詞在該文檔中出現的次數。詞頻越高,相關度越高
  • Document Frequency(DF)文檔頻率,即單詞出現的文檔數
  • Inverse Document Frequency(IDF)逆向文檔頻率,與文檔頻率相反,簡單理解爲1/DF。即單詞出現的文檔數越少,相關度越高
  • Field-length Norm 文檔越短,相關性越高

使用 explain 參數查看具體的計算方法

  • es的算分是按照shard進行的,即shard的分數計算是相互獨立的,因此在使用explain的時候注意分片數
  • 能夠經過設置索引的分片數爲1來避免這個問題es6


     

ES中的相關性算分模型

  • TF/IDF 模型
  • BM25 模型5.x以後的默認模型

TF/IDF 模型

 

BM25 模型

 
 
BM25 模型對比TF/IDF 模型

2、Document API

1. 文檔是一個Json Object,由字段(Field)組成,常見數據類型以下:

  • 字符串:text,keyword(不分詞)
  • 數值型:long,integer,short byte,double,float,half float,scaled_float
  • 布爾:boolean
  • 日期:date
  • 二進制:binary
  • 範圍類型:integer_range,float_range,long_range,double_range,date_range

2. 文檔元數據,用於標註文檔的相關信息

  • _index:文檔所在的索引名
  • _type:文檔所在的類型名(7.0後默認_doc)
  • _id:文檔惟一id
  • _uid:組合id,由_type和_id組成(6.x_type再也不起做用,所以同_id值同樣),默認禁用
  • _source:文檔的原始Json數據,能夠從這裏獲取每一個字段的內容
  • _all:整合全部字段內容到該字段,默認禁用

3. 每一個文檔有惟一的_Id標識

  • 自行指定
  • es自動生成

4. 文檔API

  • es有專門的Document API,建立文檔,查詢文檔,更新文檔,刪除文檔

建立文檔(建立文檔時,若是索引不存在,es會自動建立對應的index和type)

  • 指定id建立文檔
PUT /test_index/_doc/1
{
  "username":"zhangsan", "age":1 } 
 
指定ID建立文檔
  • 不指定id建立文檔
POST /test_index/_doc
{
  "username":"lisi", "sex":2 } 
 
不指定ID建立文檔

查詢文檔

  • 指定要查詢的文檔id
GET /test_index/_doc/1
 
指定要查詢的文檔id
  • 搜索全部文檔,用到_search
GET /test_index/_search # GET /test_index/_doc/_search在高版本提示類型已過時,所以不用指定type了 
 
搜索全部文檔

批量增刪改查文檔

  • ES容許一次建立多個文檔,從而減小網絡傳輸開銷,提高寫入速率,endpoint爲_bulk
  • index 用於建立文檔,文檔已存在則更細文檔
  • create 一樣能夠建立文檔,文檔已存在則返回錯誤
  • delete 用於刪除文檔
  • update 用於更新文檔,文檔不存在則返回錯誤
  • 在es6.0以後的版本能夠省略_type,官方已捨棄_type這個概念
POST _bulk
{"index":{"_index":"test_index","_id":"3"}} {"username":"alfred","age":10} {"create":{"_index":"test_index","_id":"3"}} {"username":"alfred2","age":110} {"delete":{"_index":"test_index","_id":"1"}} {"update":{"_id":"2","_index":"test_index"}} {"doc":{"age":"20"}} 

返回:github

{ "took" : 18, "errors" : true, "items" : [ { "index" : { "_index" : "test_index", "_type" : "_doc", "_id" : "3", "_version" : 2, "result" : "updated", "_shards" : { "total" : 2, "successful" : 2, "failed" : 0 }, "_seq_no" : 4, "_primary_term" : 1, "status" : 200 } }, { "create" : { "_index" : "test_index", "_type" : "_doc", "_id" : "3", "status" : 409, "error" : { "type" : "version_conflict_engine_exception", "reason" : "[3]: version conflict, document already exists (current version [2])", "index_uuid" : "jjJIqT7QSeaYcOeWxxY-og", "shard" : "0", "index" : "test_index" } } }, { "delete" : { "_index" : "test_index", "_type" : "_doc", "_id" : "1", "_version" : 3, "result" : "not_found", "_shards" : { "total" : 2, "successful" : 2, "failed" : 0 }, "_seq_no" : 5, "_primary_term" : 1, "status" : 404 } }, { "update" : { "_index" : "test_index", "_type" : "_doc", "_id" : "2", "status" : 404, "error" : { "type" : "document_missing_exception", "reason" : "[_doc][2]: document missing", "index_uuid" : "jjJIqT7QSeaYcOeWxxY-og", "shard" : "0", "index" : "test_index" } } } ] } 

批量查詢文檔[跨索引]

  • es容許一次查詢多個索引的文檔,endpoint爲_mget
GET /_mget
{
  "docs": [ { "_index": "test_index", "_id": 1 }, { "_index": "test_index2", "_id": 1 } ] } 

返回算法

{ "docs" : [ { "_index" : "test_index", "_type" : "_doc", "_id" : "1", "found" : false }, { "_index" : "test_index2", "_type" : null, "_id" : "1", "error" : { "root_cause" : [ { "type" : "index_not_found_exception", "reason" : "no such index [test_index2]", "resource.type" : "index_expression", "resource.id" : "test_index2", "index_uuid" : "_na_", "index" : "test_index2" } ], "type" : "index_not_found_exception", "reason" : "no such index [test_index2]", "resource.type" : "index_expression", "resource.id" : "test_index2", "index_uuid" : "_na_", "index" : "test_index2" } } ] } 

3、Indices APIs

1. 索引中通常存儲具備相同結構的文檔(Document)

  • 每一個索引都有本身的mapping定義,用於定義字段名和類型
  • 建立索引不定義mapping,es將自動根據插入的數據定義mapping,可是一般不建議這樣作,mapping至關於數據庫建表時的表結構定義
  • 1個索引中能夠存儲不一樣結構的文檔,但在6.0後type的捨棄,官方建議1個index存儲1中結構的文檔

2. 一個集羣(只有1個節點也是1個集羣)能夠有多個索引,好比:nginx 日誌存儲的時候能夠按照日期天天生成一個索引來存儲

  • nginx-log-2017-01-01
  • nginx-log-2017-01-02
  • nginx-log-2017-01-03

3. 索引API

  • es有專門的IndexAPI,用於查詢、建立、更新、刪除索引配置等

建立索引

PUT /test_index
 
建立索引成功

查看全部索引

GET /_cat/indices
 
所有索引

刪除索引

DELETE /test_index
 
刪除成功

4. 索引模板

索引模板,英文爲Index Template,主要用於在新建索引時自動應用預先設定的配置,簡化索引建立的操做步驟sql

  • 能夠設定索引的配置和mapping
  • 能夠有多個模板,當建立的索引匹配到多個模板時,根據order設置,order大的覆蓋小的配置
 
建立索引模板(語法可能過時)
  • 查看全部索引模板
    • GET /_template
  • 查看指定名稱的索引模板
    • GET /_template/test_template
  • 刪除指定名稱的索引模板
    • DELETE /_template/test_template

4、Analysis

分詞是指將文本轉換成一系列單詞(term or token)的過程,也能夠叫作文本分析,在es 裏面稱爲Analysis,以下圖所示:

 

 
分詞

在es中,分詞會在以下兩個時機使用:

  • 建立或更新文檔時,也稱索引時(Index Time),會對相應的文檔進行分詞處理


     
    索引時分詞
  • 查詢時(Search Time),會對查詢語句進行分詞


     
    查詢時分詞

通常不須要特別指定查詢時分詞器,直接使用索引時分詞器便可(此時查詢也會默認使用索引時分詞器)

分詞的使用建議:

  • 明確字段是否須要分詞,不須要分詞的字段就將type設置爲keyword,能夠節省空間和提升寫性能
  • 善用_analyze API,查看文檔的具體分詞結果
  • 動手測試

1. 分詞器組成

分詞器是es中專門處理分詞的組件,英文爲Analyzer,它的組成以下:

  • Character Filters
    • 針對原始文本進行處理,好比去除html特殊標記符
  • Tokenizer
    • 將原始文本按照必定規則切分爲單詞
  • Token Filters
    • 針對tokenizer處理的單詞就行再加工,好比轉小寫、刪除或新增等處理
 
分詞器中的調用順序

2. es內置的分詞器

es 自帶以下的分詞器:

  • Standard
  • Simple
  • Whitespace
  • Stop
  • Keyword
  • Pattern
  • Language

Standard Analyzer

 
POST _analyze
{
  "analyzer":"standard", "text":"The 2 QUICK Brown-Foxes jumped over the lazy dog's bone." } 

分詞結果:

{ "tokens" : [ { "token" : "the", "start_offset" : 0, "end_offset" : 3, "type" : "<ALPHANUM>", "position" : 0 }, { "token" : "2", "start_offset" : 4, "end_offset" : 5, "type" : "<NUM>", "position" : 1 }, { "token" : "quick", "start_offset" : 6, "end_offset" : 11, "type" : "<ALPHANUM>", "position" : 2 }, { "token" : "brown", "start_offset" : 12, "end_offset" : 17, "type" : "<ALPHANUM>", "position" : 3 }, { "token" : "foxes", "start_offset" : 18, "end_offset" : 23, "type" : "<ALPHANUM>", "position" : 4 }, { "token" : "jumped", "start_offset" : 24, "end_offset" : 30, "type" : "<ALPHANUM>", "position" : 5 }, { "token" : "over", "start_offset" : 31, "end_offset" : 35, "type" : "<ALPHANUM>", "position" : 6 }, { "token" : "the", "start_offset" : 36, "end_offset" : 39, "type" : "<ALPHANUM>", "position" : 7 }, { "token" : "lazy", "start_offset" : 40, "end_offset" : 44, "type" : "<ALPHANUM>", "position" : 8 }, { "token" : "dog's", "start_offset" : 45, "end_offset" : 50, "type" : "<ALPHANUM>", "position" : 9 }, { "token" : "bone", "start_offset" : 51, "end_offset" : 55, "type" : "<ALPHANUM>", "position" : 10 } ] } 

Simple Analyzer

 
POST _analyze
{
  "analyzer":"simple", "text":"The 2 QUICK Brown-Foxes jumped over the lazy dog's bone." } 

分詞結果:

{ "tokens" : [ { "token" : "the", "start_offset" : 0, "end_offset" : 3, "type" : "word", "position" : 0 }, { "token" : "quick", "start_offset" : 6, "end_offset" : 11, "type" : "word", "position" : 1 }, { "token" : "brown", "start_offset" : 12, "end_offset" : 17, "type" : "word", "position" : 2 }, { "token" : "foxes", "start_offset" : 18, "end_offset" : 23, "type" : "word", "position" : 3 }, { "token" : "jumped", "start_offset" : 24, "end_offset" : 30, "type" : "word", "position" : 4 }, { "token" : "over", "start_offset" : 31, "end_offset" : 35, "type" : "word", "position" : 5 }, { "token" : "the", "start_offset" : 36, "end_offset" : 39, "type" : "word", "position" : 6 }, { "token" : "lazy", "start_offset" : 40, "end_offset" : 44, "type" : "word", "position" : 7 }, { "token" : "dog", "start_offset" : 45, "end_offset" : 48, "type" : "word", "position" : 8 }, { "token" : "s", "start_offset" : 49, "end_offset" : 50, "type" : "word", "position" : 9 }, { "token" : "bone", "start_offset" : 51, "end_offset" : 55, "type" : "word", "position" : 10 } ] } 

Whitespace Analyzer

 
POST _analyze
{
  "analyzer":"whitespace", "text":"The 2 QUICK Brown-Foxes jumped over the lazy dog's bone." } 

分詞結果:

{ "tokens" : [ { "token" : "The", "start_offset" : 0, "end_offset" : 3, "type" : "word", "position" : 0 }, { "token" : "2", "start_offset" : 4, "end_offset" : 5, "type" : "word", "position" : 1 }, { "token" : "QUICK", "start_offset" : 6, "end_offset" : 11, "type" : "word", "position" : 2 }, { "token" : "Brown-Foxes", "start_offset" : 12, "end_offset" : 23, "type" : "word", "position" : 3 }, { "token" : "jumped", "start_offset" : 24, "end_offset" : 30, "type" : "word", "position" : 4 }, { "token" : "over", "start_offset" : 31, "end_offset" : 35, "type" : "word", "position" : 5 }, { "token" : "the", "start_offset" : 36, "end_offset" : 39, "type" : "word", "position" : 6 }, { "token" : "lazy", "start_offset" : 40, "end_offset" : 44, "type" : "word", "position" : 7 }, { "token" : "dog's", "start_offset" : 45, "end_offset" : 50, "type" : "word", "position" : 8 }, { "token" : "bone.", "start_offset" : 51, "end_offset" : 56, "type" : "word", "position" : 9 } ] } 

Stop Analyzer

 
POST _analyze
{
  "analyzer":"stop", "text":"The 2 QUICK Brown-Foxes jumped over the lazy dog's bone." } 

分詞結果:

{ "tokens" : [ { "token" : "quick", "start_offset" : 6, "end_offset" : 11, "type" : "word", "position" : 1 }, { "token" : "brown", "start_offset" : 12, "end_offset" : 17, "type" : "word", "position" : 2 }, { "token" : "foxes", "start_offset" : 18, "end_offset" : 23, "type" : "word", "position" : 3 }, { "token" : "jumped", "start_offset" : 24, "end_offset" : 30, "type" : "word", "position" : 4 }, { "token" : "over", "start_offset" : 31, "end_offset" : 35, "type" : "word", "position" : 5 }, { "token" : "lazy", "start_offset" : 40, "end_offset" : 44, "type" : "word", "position" : 7 }, { "token" : "dog", "start_offset" : 45, "end_offset" : 48, "type" : "word", "position" : 8 }, { "token" : "s", "start_offset" : 49, "end_offset" : 50, "type" : "word", "position" : 9 }, { "token" : "bone", "start_offset" : 51, "end_offset" : 55, "type" : "word", "position" : 10 } ] } 

Keyword Analyzer

 
POST _analyze
{
  "analyzer":"keyword", "text":"The 2 QUICK Brown-Foxes jumped over the lazy dog's bone." } 

分詞結果:

{ "tokens" : [ { "token" : "The 2 QUICK Brown-Foxes jumped over the lazy dog's bone.", "start_offset" : 0, "end_offset" : 56, "type" : "word", "position" : 0 } ] } 

Pattern Analyzer

 
POST _analyze
{
  "analyzer":"pattern", "text":"The 2 QUICK Brown-Foxes jumped over the lazy dog's bone." } 

分詞結果:

{ "tokens" : [ { "token" : "the", "start_offset" : 0, "end_offset" : 3, "type" : "word", "position" : 0 }, { "token" : "2", "start_offset" : 4, "end_offset" : 5, "type" : "word", "position" : 1 }, { "token" : "quick", "start_offset" : 6, "end_offset" : 11, "type" : "word", "position" : 2 }, { "token" : "brown", "start_offset" : 12, "end_offset" : 17, "type" : "word", "position" : 3 }, { "token" : "foxes", "start_offset" : 18, "end_offset" : 23, "type" : "word", "position" : 4 }, { "token" : "jumped", "start_offset" : 24, "end_offset" : 30, "type" : "word", "position" : 5 }, { "token" : "over", "start_offset" : 31, "end_offset" : 35, "type" : "word", "position" : 6 }, { "token" : "the", "start_offset" : 36, "end_offset" : 39, "type" : "word", "position" : 7 }, { "token" : "lazy", "start_offset" : 40, "end_offset" : 44, "type" : "word", "position" : 8 }, { "token" : "dog", "start_offset" : 45, "end_offset" : 48, "type" : "word", "position" : 9 }, { "token" : "s", "start_offset" : 49, "end_offset" : 50, "type" : "word", "position" : 10 }, { "token" : "bone", "start_offset" : 51, "end_offset" : 55, "type" : "word", "position" : 11 } ] } 

Language Analyzer

 

3. 中文分詞

 
中文分詞難點
 
經常使用的中文分詞器
 
基於天然語言處理的分詞系統

4. Analyzer API

es提供了一個測試分詞的api接口,方便驗證分詞效果,endpoint是_analyze:

  • 能夠直接指定 analyzer 進行測試
  • 能夠直接指定索引中的字段進行測試
  • 能夠自定義分詞器進行測試

直接指定analyzer進行測試

POST /_analyze
{
  "analyzer": "standard", # 分詞器 "text":"Hello World!" # 測試文本 } 

分詞結果:

{
  "tokens" : [ { "token" : "hello", # 分詞結果 "start_offset" : 0, # 開始偏移 "end_offset" : 5, # 結束偏移 "type" : "<ALPHANUM>", "position" : 0 # 分詞位置 }, { "token" : "world", "start_offset" : 6, "end_offset" : 11, "type" : "<ALPHANUM>", "position" : 1 } ] } 

直接指定索引中的字段進行測試

POST /test_index/_analyze
{
  "field": "username", "text":"Hello World HA!" } 

分詞結果:

{ "tokens" : [ { "token" : "hello", "start_offset" : 0, "end_offset" : 5, "type" : "<ALPHANUM>", "position" : 0 }, { "token" : "world", "start_offset" : 6, "end_offset" : 11, "type" : "<ALPHANUM>", "position" : 1 }, { "token" : "ha", "start_offset" : 12, "end_offset" : 14, "type" : "<ALPHANUM>", "position" : 2 } ] } 

自定義分詞器進行測試

自定義分詞器的三個組成部分:
Character Filters——char_filter
Tokenizer——tokenizer
Token Filters——filter

POST /_analyze
{
  "tokenizer": "standard", "filter": ["lowercase"], "text": ["Hello ElasticSearch!"] } 

分詞結果:

{ "tokens" : [ { "token" : "hello", "start_offset" : 0, "end_offset" : 5, "type" : "<ALPHANUM>", "position" : 0 }, { "token" : "elasticsearch", "start_offset" : 6, "end_offset" : 19, "type" : "<ALPHANUM>", "position" : 1 } ] } 

5. 自定義分詞

當自帶的分詞沒法知足需求時,能夠自定義分詞

  • 經過自定義 Character Filters、Tokenizer和Token Filter實現

Character Filters

  • 在Tokenizer以前對原始文本進行處理,好比增長、刪除或替換字符等
  • 自帶的以下:
    • HTML Strip去除html 標籤和轉換html實體
    • Mapping 進行字符替換操做
    • Pattern Replace 進行正則匹配替
  • 會影響後續tokenizer 解析的postion和offset信息

HTML Strip

POST _analyze
{
  "tokenizer": "keyword", "char_filter": ["html_strip"], "text": ["<p>i am groot</p>"] } 

分詞結果:

{ "tokens" : [ { "token" : """ i am groot """, "start_offset" : 0, "end_offset" : 17, "type" : "word", "position" : 0 } ] } 

Tokenizer

  • 將原始文本按照必定規則切分爲單詞(term or token)
  • 自帶的以下:
    • standard 按照單詞進行分割
    • letter 按照非字符類進行分割
    • whitespace 按照空格進行分割
    • UAX URL Email 按照standard分割,但不會分割郵箱和url
    • NGram和Edge NGram連詞分割
    • Path Hierarchy 按照文件路徑進行切割

Token Filter

  • 對於tokenizer輸出的單詞(term)進行增長、刪除、修改等操做
  • 自帶的以下:
    • lowercase 將全部term轉換爲小寫
    • stop刪除stop words
    • NGram和Edge NGram連詞分割
    • Synonym 添加近義詞的term

6. 自定義分詞API

自定義分詞須要在索引的配置中設定

 

 
 
自定義分詞器1
 
自定義分詞器1驗證
 
自定義分詞器2
 
自定義分詞器2驗證

5、Mapping

相似數據庫中的表結構定義,主要做用以下:

  • 定義Index下的字段名(Field Name)
  • 定義字段的類型,好比數值型、字符串型、布爾型等
  • 定義倒排索引相關的配置,好比是否索引、記錄 position等
 
查看索引mapping

1. mapping參數配置

  • Mapping 中的字段類型一旦設定後,禁止直接修改,緣由以下:
    • Lucene 實現的倒排索引生成後不容許修改
  • 若是要修改就須要從新創建新的索引,而後作 reindex操做把之間索引的數據導入到新的索引中
  • 容許新增字段
  • 經過dynamic參數來控制字段的新增
    • true(默認)容許自動新增字段
    • false 不容許自動新增字段,可是文檔能夠正常寫入,但沒法對字段進行查詢等操做
    • strict 文檔不能寫入mapping未定義的字段,插入文檔會報錯
  • copy_to字段複製
    • 將該字段的值複製到目標字段,實現相似_all的做用
    • 不會出如今_source中,只用來搜索,通常也是用於搜索使用
  • index控制當前字段是否索引
    • 默認爲true,記錄索引,便可搜索
    • false則不記錄索引,即不可搜索該字段,省去了爲該字段創建倒排索引的時間與空間
  • index_options用於控制倒排索引記錄的內容
    • 有以下4種配置
      • docs 只記錄 doc id
      • freqs 記錄 doc id和term frequencies
      • positions 記錄 doc id、term frequencies和term position
      • offsets 記錄 doc id、term frequencies、term position和character offsets
    • text 類型默認配置爲positions,其餘默認爲docs
    • 記錄內容越多,佔用空間越大
  • null_value 當字段遇到null值的處理策略
    • 默認爲null,即空值,此時es會忽略該值。
  • fields 多字段multi-fields
    • 多字段能夠以不一樣方式索引相同字段。例如,一個字符串字段既能夠映射爲全文搜索的text字段,也能夠映射爲排序或聚合的keyword字段。

dynamic控制字段的新增

 
ES7.0中自定義mapping,dynamic字段設置爲false
 
查詢索引mapping
 
插入文檔,desc未在mapping中定義
 
因爲dynamic字段設置爲false,沒法根據未定義的字段搜索
 
ES7.0中自定義mapping,dynamic字段設置爲strict
 
因爲dynamic字段設置爲strict,插入文檔報錯

copy_to將字段複製到目標字段

 
自定義mapping,使用copy_to
 
插入並查看文檔,查詢結果沒有full_name字段
 
對full_name字段搜索

index控制當前字段是否索引

 
搜索index爲false的字段報錯

index_options控制倒排索引記錄的內容

 
配置index_options

2. 數據類型

  • 核心數據類型
    • 字符串類型 text(分詞)、keyword(不分詞)
    • 數值型long、integer、short、byte、double、float、half_float、scaled_float
    • 日期類型 date
    • 日期納秒類型 date_nanos
    • 布爾類型 boolean
    • 二進制類型 binary
    • 範圍類型 integer_range、float_range、long_range、double_range、date_range
  • 複雜類型
    • 數組類型 array
    • 對象類型 object
    • 嵌套類型 nested object
  • 地理位置類型
    • 地理位置點 Geo-point
    • 地理位置形狀 Geo-shape
  • 專用類型
    • 記錄ip地址 ip
    • 實現自動補全 completion
    • 記錄分詞數 token_count
    • 記錄字符串hash值 murmur3
    • annotated-text
    • percolator
    • join
    • alias
    • rank_feature
    • rank_features
    • dense_vector
    • sparse_vector
  • 多字段特性 multi-fields
    • 容許對同一個字段採用不一樣的配置,好比分詞,常見例子如對人名實現拼音搜索,只須要在人名中新增一個子字段爲pinyin便可,分詞器須要支持子字段分詞才能夠索引


       

3. Dynamic Mapping

Dynamic field mapping

  • es能夠自動識別文檔字段類型,從而下降用戶使用成本


     
  • es是依靠JSON文檔的字段類型來實現自動識別字段類型,支持的類型以下


     
  • 日期的自動識別能夠自行配置日期格式,以知足各類需求
    • 默認匹配格式是["strict_date_optional_time","yyyy/MM/dd HH:mm:ss Zllyyyy/MM/dd Z]
    • strict_date_optional_time是ISO datetime的格式,完整格式相似下面:
      • YYYY-MM-DDThh:mm:ssTZD(eg 1997-07-16T19:20:30+01:00)
    • dynamic_date_formats能夠自定義日期類型
    • date_detection 能夠關閉日期自動識別的機制


       
  • 字符串是數字時,默認不會自動識別爲整型,由於字符串中出現數字是徹底合理的
    • numeric_detection能夠開啓字符串中數字的自動識別,以下所示


       

Dynamic templates

  • 容許根據es自動識別的數據類型、字段名等來動態設定字段類型,能夠實現以下效果:
    • 全部字符串類型都設定爲keyword類型,即默認不分詞
    • 全部以message開頭的字段都設定爲text類型,即分詞
    • 全部以long_開頭的字段都設定爲long類型
    • 全部自動匹配爲double類型的都設定爲float類型,以節省空間
  • 匹配規則參數
    • match_mapping_type 匹配es自動識別的字段類型,如boolean,long,string等
    • match,unmatch 匹配字段名
    • path_match,path_unmatch 匹配路徑
 
例:字符串默認使用keyword類型
 
例:以message開頭的字段都設置爲text類型
 
例:double 類型設定爲float,節省空間

4. 自定義Mapping的建議

自定義Mapping的操做步驟以下:

  1. 寫入一條文檔到es的臨時索引中,獲取es自動生成的mapping
  2. 修改步驟1獲得的mapping,自定義相關配置
  3. 使用步驟2的mapping 建立實際所需索引

6、Search APIs

實現對es中存儲的數據進行查詢分析,endpoint爲_search,以下所示:

 

查詢主要有兩種形式:

  • URI Search
    • 操做簡便,方便經過命令行測試
    • 僅包含部分查詢語法
  • Request Body Search
    • es提供的完備查詢語法Query DSL(Domain Specific Language)
 
查詢兩種形式

1. URI Search

經過url query參數來實現搜索,經常使用參數以下:

  • q 指定查詢的語句,語法爲Query String Syntax
  • df 若q中不指定字段時默認查詢的字段,若是不指定,es會查詢全部字段
  • sort 排序
  • timeout 指定超時時間,默認不超時
  • from,size 用於分頁

Query String Syntax

term(單詞)與 phrase(詞語)

  • alfred way 單詞查詢,等效於 alfred OR way
  • "alfred way" 詞語查詢,查詢時會按照單詞前後順序檢索

泛查詢

  • alfred 等效於在全部字段去匹配該term

指定字段

  • name:alfred 查詢name字段包含alfred的文檔

Group分組設定,使用括號指定匹配的規則

  • (quick OR brown) AND fox
  • status:(active OR pending) title:(full text search)

布爾操做符

  • AND(&&) OR(||) NOT(!)
    • name:(tom NOT lee) 查詢name字段不包含lee或者name字段包含tom的文檔
    • name:(tom AND NOT lee) 查詢name字段不包含lee而且name字段包含tom的文檔
    • name:(tom OR lee) 等價於name:(tom lee) 查詢name字段包含lee或者name字段包含tom的文檔
    • 注意AND OR NOT必定是大寫的,小寫的就變成term了
  • +-分別對應must和must_not
    • name:(tom +lee -alfred) 查詢name字段必定包含lee必定不包含alfred能夠包含tom的文檔
      • 等價於 name:(lee AND NOT alfred) OR (tom AND lee AND NOT alfred))
    • +在url中會被解析爲空格,要使用urlencode後的結果才能夠,+爲%2B

範圍查詢,支持數值和日期

  • 區間寫法,閉區間[],開區間用{}
    • age:[1 TO 10]意爲1<=age<=10
    • age:[1 TO 10}意爲1<=age<10
    • age:[1 TO ]意爲age>=1
    • age:[* TO 10]意爲age<=10
  • 算數符號寫法
    • age:>=1
    • age:(>=1 && <=10)或者age:(+>=1 + <=10)

通配符查詢

  • ? 表明1個字符,* 表明0或多個字符
    • name:t?m
    • name:tom*
    • name:t*m
  • 通配符匹配執行效率低,且佔用較多內存,不建議使用
  • 如無特殊需求,不要將?/*放在最前面,放在最前面會檢索所有文檔,效率最低,內存易oom

模糊匹配 fuzzy query

  • name:roam~1 匹配與roam查1個character的詞,好比foam roams等
  • 以 character 字符爲單位進行差別比較

近似度查詢 proximity search

  • "fox quick"~5 匹配5個單位差別的文檔
  • 以 term 爲單位進行差別比較,好比"quick fox" "quick brown fox"都會被匹配
 
url query的形式
 
Query String Syntax
 
Query String Syntax

2. Request Body Search 【推薦使用!!!功能比URI Search更強大!!!】

將查詢語句經過http request body發送到es,主要包含以下參數:

  • query符合Query DSL語法的查詢語句
  • from,size 用於分頁查詢
  • timeout 指定超時時間,默認不超時
  • sort 排序

Source filtering

過濾返回結果中source中的字段,主要有以下幾種方式:

 

 

Query DSL

基於JSON定義的查詢語言,主要包含以下兩種類型:

  • 字段類查詢
    • 如term,match,range等,只針對某一個字段進行查詢
  • 複合查詢
    • 如bool查詢等,包含一個或多個字段類查詢或者複合查詢語句

字段類查詢

字段類查詢主要包括如下兩類:

  • 全文匹配
    • 針對text 類型的字段進行全文檢索,會對查詢語句先進行分詞處理,如match,match_phrase等query類型
  • 單詞匹配
    • 不會對查詢語句作分詞處理,直接去匹配字段的倒排索引,如term,terms,range等query類型
Match Query
  • 對字段做全文檢索,最基本和經常使用的查詢類型,API示例以下:


     
  • Match Query執行流程:


     
  • 經過operator參數能夠控制單詞間的匹配關係,可選項爲or和and


     
  • 經過minimum_should_match參數能夠控制須要匹配的單詞數


     
Match Phrase Query
  • 對字段做檢索,對單詞有順序要求,API示例以下:


     
    匹配job字段包含java engineer單詞,且java在engineer前面的文檔
  • 經過slop參數能夠控制單詞間的間隔


     
    slop以term爲單位
Query String Query
  • 相似於URI Search中的q參數查詢
 
Simple Query String Query
  • 相似Query String,可是會忽略錯誤的查詢語法,而且僅支持部分查詢語法
  • 其經常使用的邏輯符號以下,不能使用AND、OR、NOT 等關鍵詞:
    • + 代指AND
    • | 代指OR
    • - 代指NOT
 
Term Query

將查詢語句做爲整個單詞進行查詢,即不對查詢語句作分詞處理,經常使用於查詢keyword類型字段,以下所示:

 
Terms Query

一次傳入多個單詞進行查詢,以下所示:

 
Range Query

範圍查詢主要針對數值和日期類型,以下所示:

 
 
查詢日期以及DateMath表達式
  • Date Math 針對日期提供的一種更友好地計算方式,格式以下:


     
    Date Math
 
Date Math單位
 
案例

複合查詢

複合查詢是指包含字段類查詢或複合查詢(複合查詢裏面能夠包含複合查詢)的類型,主要包括如下幾類:

  • constant_score query
  • bool query
  • dis_max query
  • function_score query
  • boosting query
constant_score query

該查詢將其內部的查詢結果文檔得分都設定爲1或者boost的值

  • 多用於結合bool 查詢實現自定義得分
 
bool query
  • 布爾查詢由一個或多個布爾子句組成,主要包含以下4個:
    • filter 只過濾符合條件的文檔,不計算相關性得分
    • must 文檔必須符合must中的全部條件,會影響相關性得分
    • must_not 文檔必須不符合must_not中的全部條件
    • should 文檔能夠符合should中的條件,會影響相關性得分
 
Bool 查詢的API
filter
  • Filter 查詢只過濾符合條件的文檔,不會進行相關性算分
  • es針對filter會有智能緩存,所以其執行效率很高
  • 作簡單匹配查詢且不考慮算分時,推薦使用 filter 替代 query 等
 
must
  • 文檔必須符合must中的全部條件,會影響相關性得分
 
must_not
  • 文檔必須不符合must_not中的全部條件
 
should
  • 文檔能夠符合should中的條件,會影響相關性得分
  • Should 使用分兩種狀況:
    • bool 查詢中只包含should,不包含 must 查詢
      • 只包含should時,文檔必須知足至少一個條件
      • minimum_should_match 能夠控制知足條件的個數或者百分比


         
    • bool 查詢中同時包含 should 和 must 查詢
      • 同時包含should和must時,文檔沒必要知足should中的條件,可是若是知足條件,會增長相關性得分


         
Query Context VS Filter Context

當一個查詢語句位於Query或者Filter上下文時,es執行的結果會不一樣,對好比下:

 

 

 

3. Count API

獲取符合條件的文檔數,endpoint 爲 _count

 

7、分佈式

1. 分佈式特性

  • es支持集羣模式,是一個分佈式系統,其好處主要有兩個:
    • 增大系統容量,如內存、磁盤,使得es集羣能夠支持PB級的數據
    • 提升系統可用性,即便部分節點中止服務,整個集羣依然能夠正常服務
  • es集羣由多個es實例組成
    • 不一樣集羣經過集羣名字來區分,可經過cluster.name 進行修改,默認爲elasticsearch
    • 每一個es實例本質上是一個JVM進程,且有本身的名字,經過node.name 進行修改

2. 構建集羣

啓動單節點

運行以下命令能夠啓動一個es節點實例:
bin/elasticsearch -E cluster.name=my_cluster -E node.name=node1

 

再啓動一個新的es節點,構建一個由node1node2 2個節點組成的集羣my_cluster
bin/elasticsearch -E cluster.name=my_cluster -E node.name=node2

 

集羣狀態 Cluster State

es 集羣相關的數據稱爲cluster state,主要記錄以下信息:

  • 節點信息,好比節點名稱、鏈接地址等
  • 索引信息,好比索引名稱、配置等
 

Master Node 主節點

  • 能夠修改cluster state的節點稱爲master節點,一個集羣只能有一個
  • cluster state 存儲在每一個節點上,master 維護最新版本並同步給其餘節點
  • master 節點是經過集羣中全部節點選舉產生的,能夠被選舉的節點稱爲master- eligible 節點,相關配置以下:
    • node.master:true
 

建立索引後,cluster state 的版本將更新

 

Coordinating Node 協調節點

處理請求的節點即爲coordinating 節點,該節點爲全部節點的默認角色,不能取消

  • 路由請求到正確的節點處理,好比建立索引的請求轉發(Redis是重定向)到master節點
 

Data Node 數據存儲節點

存儲數據的節點即爲data節點,默認節點都是data類型,相關配置以下:

  • node.data:true
 

3. 副本與分片

提升系統可用性

  • 服務可用性
    • 2個節點的狀況下,容許其中1個節點中止服務
  • 數據可用性
    • 引入副本(Replication)解決
    • 每一個節點上都有完備的數據

副本

ES中的副本不是對面向節點的副本,而是面向分片的副本,副本分片的數據由主分片同步,能夠有多個,從而提升讀取的吞吐量,且能夠隨時修改副本數量。

 

增大系統容量

  • 如何將數據分佈於全部節點上?

    • 引入分片(Shard)解決問題
  • 分片是es支持PB級數據的基石

    • 分片存儲了部分數據,能夠分佈於任意節點上
    • 分片數在索引建立時指定且後續不容許再更改,默認爲5個
    • 分片有主分片和副本分片之分,以實現數據的高可用
    • 副本分片的數據由主分片同步,能夠有多個,從而提升讀取的吞吐量,且能夠隨時修改副本數量

分片

分片數的設定很重要,須要提早規劃好

  • 太小會致使後續沒法經過增長節點實現水平擴容
  • 過大會致使一個節點上分佈過多分片,形成資源浪費,同時會影響查詢性能
 

此時增長副本數是否能提升test_index的讀取吞吐量?
不能。由於新增的副本也是分佈在這3個節點上,仍是利用了一樣的資源。若是要增長吞吐量,還須要新增節點。

此時增長節點是否能提升test_index的數據容量?
不能。由於建立索引時指定了3個分片,已經分佈在3臺節點上,新增的節點沒法利用。

4. 集羣運行狀態

Cluster Health

經過以下api能夠查看集羣健康情況,包括如下三種:

  • green健康狀態,指全部主副分片都正常分配
  • yellow指全部主分片都正常分配,可是有副本分片未正常分配
  • red有主分片未分配
 

5. 故障轉移

  • 集羣由3個節點組成,以下所示,此時集羣狀態是green
 
  • node1 所在機器宕機致使服務終止,此時集羣會如何處理?

    1. node2和node3發現node1沒法響應一段時間後會發起master選舉,好比這裏選擇node2爲master節點。此時因爲主分片P0下線,集羣狀態變爲Red。


       
    2. node2發現主分片P0未分配,將R0提高爲主分片。此時因爲全部主分片都正常分配,集羣狀態變爲Yellow。


       
    3. node2爲P0和P1生成新的副本,集羣狀態變爲綠色


       

6. 文檔分佈式存儲

  • 文檔最終會存儲在分片上,以下圖所示:

    • Document1最終存儲在分片P1上


       
  • Document1是如何存儲到分片P1的?選擇P1的依據是什麼?

    • 須要文檔到分片的映射算法
  • 目的

    • 使得文檔均勻分佈在全部分片上,以充分利用資源
  • 算法

    • 隨機選擇或者round-robin輪詢算法?
      • 不可取,由於須要維護文檔到分片的映射關係,成本巨大
    • 根據文檔值實時計算對應的分片!

文檔到分片的映射算法

es經過以下的公式計算文檔對應的分片:

  • shard=hash(routing) % number_of_primary_shards
  • hash 算法保證能夠將數據均勻地分散在分片中
  • routing是一個關鍵參數,默認是文檔id,也能夠自行指定
  • number_of_primary_shards是主分片數

該算法與主分片數相關,這也是分片數一旦肯定後便不能更改的緣由

文檔建立的流程

 

文檔讀取的流程

 

文檔批量建立的流程

 

文檔批量讀取的流程

 

7. 腦裂問題

腦裂問題,英文爲 split-brain,是分佈式系統中的經典網絡問題

  1. 3個節點組成的集羣,忽然 node1的網絡和其餘兩個節點中斷


     
  2. node2與node3會從新選舉 master,好比node2成爲了新 master,此時會更新cluster state

  3. node1本身組成集羣后,也會更新 cluster state

同一個集羣有兩個master,並且維護不一樣的cluster state,網絡恢復後沒法選擇正確的master

 

解決方案

解決方案爲僅在可選舉master-eligible節點數大於等於quorum時才能夠進行 master
選舉

  • quorum = master-eligible節點數/2+1,例如3個master-eligible節點時,quorum爲2。
  • 配置 discovery.zen.minimum_master_nodes: quorum 便可避免腦裂
 

8. shard詳解

倒排索引的不可變動

  1. 倒排索引一旦生成,不能更改
  2. 其好處以下:
  • 不用考慮併發寫文件的問題,杜絕了鎖機制帶來的性能問題
  • 因爲文件再也不更改,能夠充分利用文件系統緩存,只需載入一次,只要內存足夠,對該文件的讀取都會從內存讀取,性能高
  • 利於生成緩存數據
  • 利於對文件進行壓縮存儲,節省磁盤和內存存儲空間
  1. 壞處爲須要寫入新文檔時,必須從新構建倒排索引文件,而後替換老文件後,新文檔才能被檢索,致使文檔實時性差


     

文檔搜索實時性

  1. 解決方案是新文檔直接生成新的倒排索引文件,查詢的時候同時查詢全部的倒排文件,而後作結果的彙總計算便可


     
  2. Lucene 即是採用了這種方案,它構建的單個倒排索引稱爲segment,合在一塊兒稱爲Index,與ES中的Index概念不一樣。
  3. Lucene 會有一個專門的文件來記錄全部的segment信息,稱爲commit point
     

文檔搜索實時性 - refresh

  1. segment寫入磁盤的過程依然很耗時,能夠藉助文件系統緩存的特性,先將 segment在緩存中建立並開放查詢來進一步提高實時性,該過程在 es 中被稱爲refresh
  2. refresh以前文檔會先存儲在一個buffer中,refresh時將 buffer中的全部文檔清空並生成segment
     
    refresh發生前

     
    refresh發生後
  3. es默認每1秒執行一次refresh,所以文檔的實時性被提升到1秒,這也是es被稱爲近實時(Near Real Time)的緣由

refresh 發生的時機主要有以下幾種狀況:

  1. 間隔時間達到時,經過index.settings.refresh_interval來設定,默認是1秒
  2. index.bufer 佔滿時,其大小經過indices.memory.index_buffer_size設置,默認爲jvm heap的10%,全部shard共享
  3. flush發生時也會發生refresh

文檔搜索實時性 - translog

  1. 若是在內存中的 segment 尚未寫入磁盤前發生了宕機,那麼其中的文欄檔就沒法恢復了,如何解決這個問題?
  2. es引入translog機制。寫入文檔到buffer時,同時將該操做寫入translog
     
  3. translog 文件會即時寫入磁盤(fsync),6.x默認每一個請求都會落盤,能夠修改成每5秒寫一次,這樣風險即是丟失5秒內的數據,相關配置爲index.translog.*
  4. es啓動時會檢查translog 文件,並從中恢復數據

文檔搜索實時性 - flush

flush負責將內存中的segment寫入磁盤,主要作以下的工做:

  1. 將 translog 寫入磁盤、
  2. 將 index buffer 清空,其中的文檔生成一個新的 segment,至關於一個refresh操做
  3. 更新commit point 並寫入磁盤
  4. 執行fsync操做,將內存中的segment寫入磁盤
  5. 刪除舊的translog文件


     

flush發生的時機主要有以下幾種狀況:

  1. 間隔時間達到時,默認是30分鐘,5.x以前能夠經過index.translog.flush threshold period 修改,以後沒法修改
  2. translog 佔滿時,其大小能夠經過index.translog.flush threshold size控制,默認是512mb,每一個 index 有本身的 translog

文檔搜索實時性 - 刪除與更新文檔

  • segment一旦生成就不能更改,那麼若是你要刪除文檔該如何操做?
    • Lucene專門維護一個.del的文件,記錄全部已經刪除的文檔,注意.del上記錄的是文檔在Lucene內部的id
    • 在查詢結果返回前會過濾掉.del中的全部文檔
  • 更新文檔如何進行呢?
    • 首先刪除文檔,而後再建立新文檔

Segment Merging

  • 隨着segment的增多(es默認每秒refresh一次,每次refresh後都會生成新的segement),因爲一次查詢的segment數(查詢全部的segement作彙總)增多,查詢速度會變慢
  • es 會定時在後臺進行 segment merge的操做,減小segment的數量
  • 經過force_merge api能夠手動強制作 segment merge的操做

ES Index與Lucene Index的對照總體視角

 
總體視角

8、深刻了解Search的運行機制

1. Query-Then-Fetch

Search執行的時候實際分兩個步驟運做的

  • Query階段
  • Fetch階段

Query階段

 

Fetch階段

 

2. 相關性算分問題

相關性算分在shard與shard間是相互獨立的,也就意味着同一個Term的IDF等值在不一樣shard上是不一樣的。

文檔的相關性算分和它所處的shard相關
在文檔數量很少時,會致使相關性算分嚴重不許的狀況發生

解決方案

一是設置分片數爲1個,從根本上排除問題,在文檔數量很少的時候能夠考慮該方案,好比百萬到幹萬級別的文檔數量

二是使用DFS Query-then-Fetch 查詢方式

DFS Query-then-Fetch是在拿到全部文檔後再從新完整的計算一次相關性算分,耗費更多的cpu和內存,執行性能也比較低下,通常不建議使用。使用方式以下:

 

3. 排序

es默認會採用相關性算分排序,用戶能夠經過設定sorting參數來自行設定排序規則

單字段排序

 
單個字段排序

多字段排序

 
多個字段排序

字符串類型排序

按照字符串排序比較特殊,由於es有text和keyword兩種類型

針對text類型排序

 

針對keyword類型排序

 

排序原理

排序的過程實質是對字段原始內容排序的過程,這個過程當中倒排索引沒法發揮做用,須要用到正排索引,也就是經過文檔ld和字段快速獲得字段原始內容,而後對字段原始內容排序。

經過文檔ld和字段快速獲得字段原始內容,ES對此提供了2種實現方式:

  • fielddata 默認禁用
  • doc values 默認啓用,除了text類型
 
Fielddata vs DocValues

Fielddata

Fielddata 默認是關閉的,能夠經過以下api開啓:

  • 此時字符串是按照分詞後的term排序,每每結果很難符合預期
  • 通常是在對分詞作聚合分析的時候開啓
 

DocValues

Doc Values默認是啓用的,能夠在建立索引的時候關閉:

  • 若是後面要再開啓 doc values,須要作reindex操做
 

docvalue_fields

能夠經過該字段獲取 fielddata或者doc values中存儲的內容

 

4. 分頁與遍歷

es 提供了3種方式來解決分頁與遍歷的問題:

  • from/size
  • scroll
  • search_after

from/size

最經常使用的分頁方案

  • from 指明開始位置
  • size 指明獲取總數
 

深度分頁問題

深度分頁是一個經典的問題:在數據分片存儲的狀況下如何獲取前1000個文檔?

  • 獲取從990~1000的文檔時,會在每一個分片上都先獲取1000個文檔,而後再由Coordinating Node聚合全部分片的結果後再排序選取前1000個文檔
  • 頁數越深,處理文檔越多,佔用內存越多,耗時越長。儘可能避免深度分頁,es經過index.max_result_window 限定最多到10000條數據
  • 各大搜索引擎也都有此問題,google最多展現100頁搜索結果,百度最多76頁搜索結果
 

scroll

遍歷文檔集的api,以快照的方式來避免深度分頁的問題

  • 不能用來作實時搜索,由於數據不是實時的
  • 儘可能不要使用複雜的 sort 條件,使用 _doc 最高效
  • 使用稍嫌複雜

使用方法

  1. 第一步須要發起1個scroll search,以下所示:

es在收到該請求後會根據查詢條件建立文檔Id合集的快照

 

 
  1. 第二步調用scroll search的api,獲取文檔集合,以下所示:

不斷迭代調用直到返回hits.hits數組爲空時中止

 

 
  1. 過多的scroll 調用會佔用大量內存,能夠經過clear api刪除過多的scroll快照:
 

Search_After

避免深度分頁的性能問題,提供實時的下一頁文檔獲取功能

  • 缺點是不能使用from參數,即不能指定頁數
  • 只能下一頁,不能上一頁
  • 使用簡單

使用方法

  1. 第一步爲正常的搜索,但要指定 sort值,並保證值惟一
  2. 第二步爲使用上一步最後一個文檔的 sort 值進行查詢
 

如何避免深度分頁問題?

經過惟一排序值定位將每次要處理的文檔數都控制在size內

 

應用場景

 

9、聚合分析 Aggregations

1. 什麼是聚合分析

搜索引擎用來回答以下問題:

  • 請告訴我地址爲上海的全部訂單?
  • 請告訴我最近1天內建立但沒有付款的全部訂單?

聚合分析能夠回答以下問題:

  • 請告訴我最近1周天天的訂單成交量有多少?
  • 請告訴我最近1個月天天的平均訂單金額是多少?
  • 請告訴我最近半年賣的最火的前5個商品是哪些?

聚合分析,英文爲 Aggregation,是es除搜索功能外提供的針對 es 數據作統計分析的功能

  • 功能豐富,提供 Bucket、Metric、Pipeline 等多種分析方式,能夠知足大部分的分析需求
  • 實時性高,全部的計算結果都是即時返回的,而 hadoop 等大數據系統通常都是T+1級別的

2. 聚合分析api

 

3. 聚合分析分類

爲了便於理解,es將聚合分析主要分爲以下4類:

  • Bucket,分桶類型,相似SQL中的GROUP BY語法
  • Metric,指標分析類型,如計算最大值、最小值、平均值等等
  • Pipeline,管道分析類型,基於上一級的聚合分析結果進行再分析
  • Matrix,矩陣分析類型

Metric 聚合分析

主要分以下兩類:

  • 單值分析,只輸出一個分析結果
    • min 最小,max 最大,avg 平均,sum 求和
    • cardinality 數目
  • 多值分析,輸出多個分析結果
    • stats,extended stats
    • percentile,percentile rank
    • top hits
 

Metric 聚合分析 - Min

 

Metric 聚合分析 - Max

 

Metric 聚合分析 - Avg

 

Metric 聚合分析 - Sum

 

Metric 聚合分析 - Cardinality

 

Metric 聚合分析 - Stats

 

Metric 聚合分析 - Extended Stats

 

Metric 聚合分析 - Percentile

 
 
指定百分位數

Metric 聚合分析 - Percentile Rank

 

Metric 聚合分析 - Top Hits

 

Bucket 聚合分析

Bucket,意爲桶,即按照必定的規則將文檔分配到不一樣的桶中,達到分類分析的目的

 

按照Bucket的分桶策略,常見的Bucket聚合分析以下:

  • Terms
  • Range
  • Date Range
  • Histogram
  • Date Histogram

聚合分析 - Terms

 

size參數表示從每一個分片上返回_count值最大的前size個分桶,最終在Coordinating Node節點彙總全部分片返回的分桶結果,所以沒法保證返回的前size個分桶數據必定是_count值最大的分桶,更多介紹:Terms 聚合的執行流程

Bucket 聚合分析 - Range

 

Bucket 聚合分析 - Date Range

 

Bucket 聚合分析 - Historgram

 

Bucket 聚合分析 - Date Historgram

 

Bucket + Metric 聚合分析

Bucket 聚合分析容許經過添加子分析來進一步進行分析,該子分析能夠是 Bucket 也能夠是 Metric。這也使得 es 的聚合分析能力變得異常強大。

分桶再分桶

 
 

分桶後進行指標分析

 

Pipeline 聚合分析

針對聚合分析的結果再次進行聚合分析,並且支持鏈式調用,能夠回答以下問題:

 
訂單月平均銷售額是多少?

Pipeline的分析結果會輸出到原結果中,根據輸出位置的不一樣,分爲如下兩類:

  • Parent結果內嵌到現有的聚合分析結果中
    • Derivative
    • Moving Average
    • Cumulative Sum
  • Sibling 結果與現有聚合分析結果同級
    • Max/Min/Avg/Sum Bucket
    • Stats/Extended Stats Bucket
    • Percentiles Bucket

Pipeline 聚合分析 Sibling - Min Bucket

 

Pipeline 聚合分析 Sibling - Max Bucket

 

Pipeline 聚合分析 Sibling - Percentiles Bucket

 

Pipeline 聚合分析 Parent - Derivative

 

Pipeline 聚合分析 Parent - Moving Average

 

Pipeline 聚合分析 Parent - Cumulative Sum

 

4. 做用範圍

es 聚合分析默認做用範圍是query的結果集,能夠經過以下的方式改變其做用範圍:

  • filter
  • post_filter
  • global
 

做用範圍 - filter

 

做用範圍 - post-filter

 

做用範圍 - global

 

5. 排序

可使用自帶的關鍵數據進行排序,好比:

  • _count 文檔數
  • key 按照 key 值排序
 
 
嵌套排序

6. 計算精準度問題

Min 聚合的執行流程

 

Terms 聚合的執行流程

 

Terms 並不老是準確

 

Terms 不許確的緣由

數據分散在多Shard上,Coordinating Node 沒法得悉數據全貌

Terms 不許確的解決方法

  • 設置Shard數爲1,消除數據分散的問題,但沒法承載大數據量

  • 合理設置 Shard_Size大小,即每次從Shard上額外多獲取數據,以提高準確度


     

Shard_Size 大小的設定方法

  • Shard_Size 默認大小以下:
    • shard_size = (size * 1.5) + 10
  • 經過調整 Shard_Size 的大小下降 doc_count_error_upper_bound 來提高準確度
    • 增大了總體的計算量,從而下降了響應時間

terms 聚合返回結果中有以下兩個統計值:

  • doc_count_error_upper_bound 被遺漏的 term 可能的最大值
  • sum_other_doc_count 返回結果 bucket 的 term 外其餘 term 的文檔總數
 
 
  • 設定 show_term_doc_count_error 能夠查看每一個 bucket 誤算的最大值
 

近似統計算法

 

在ES的聚合分析中,Cardinality 和 Percentile 分析使用的是近似統計算法

  • 結果是近似準確的,但不必定精準
  • 能夠經過參數的調整使其結果精準,但同時也意味着更多的計算時間和更大的性能消耗

10、數據建模

1. 什麼是數據建模

英文爲Data Modeling,爲建立數據模型的過程
數據模型(Data Model)

  • 對現實世界進行抽象描述的一種工具和方法
  • 經過抽象的實體及實體之間聯繫的形式去描述業務規則,從而實現對現實世界的映射

2. 數據建模的過程

  • 概念模型
    • 肯定系統的核心需求和範圍邊界,設計實體和實體間的關係
  • 邏輯模型
    • 進一步梳理業務需求,肯定每一個實體的屬性、關係和約束等
  • 物理模型
    • 結合具體的數據庫產品,在知足業務讀寫性能等需求的前提下肯定最終的定義
    • Mysql、MongoDB、elasticsearch 等
    • 第三範式

3. 數據建模的意義

 

重視數據建模

  • 牽一髮而動全身

4. ES中的數據建模

ES是基於Lucene以倒排索引爲基礎實現的存儲體系,不遵循關係型數據庫中的範式約定

 

Mapping 字段的相關設置

  • enbaled
    • true | false
    • 僅存儲,不作搜索或聚合分析
  • index
    • true | false
    • 是否構建倒排索引
  • index options
    • docs I freqs I positions l offsets
    • 存儲倒排索引的哪些信息
  • norms
    • true | false
    • 是否存儲歸一化相關參數,若是字段僅用於過濾和聚合分析,可關閉
  • doc_values
    • true | false
    • 是否啓用doc_values,用於排序和聚合分析
  • field data
    • false l true
    • 是否爲text類型啓用fielddata,實現排序和聚合分析
  • store
    • false l true
    • 是否單獨存儲該字段值,默認false
  • coerce
    • true | false
    • 是否開啓自動數據類型轉換功能,好比字符串轉爲數字、浮點轉爲整型等
  • multifields 多字段
    • 靈活使用多字段特性來解決多樣的業務需求
  • dynamic
    • true I false | strict
    • 控制 mapping 自動更新
  • date_detection
    • true I false
    • 是否自動識別日期類型

Mapping 字段屬性的設定流程

 

是何種類型?

  • 字符串類型
    • 須要分詞則設定爲text類型,不然設置爲keyword類型
  • 枚舉類型
    • 基於性能考慮將其設定爲keyword類型,即使該數據爲整型
  • 數值類型
    • 儘可能選擇貼近的類型,好比byte便可表示全部數值時,即選用byte,不要用long
  • 其餘類型
    • 好比布爾類型、日期、地理位置數據等

是否須要檢索?

  • 徹底不須要檢索、排序、聚合分析的字段
    • enabled 設置爲false
  • 不須要檢索的字段
    • index 設置爲false
  • 須要檢索的字段,能夠經過以下配置設定須要的存儲粒度
    • index_options 結合須要設定
    • norms 不須要歸一化數據時關閉便可

是否須要排序和聚合分析?

不須要排序或者聚合分析功能

  • doc_values設定爲false
  • fielddata 設定爲false

是否須要另行存儲?

是否須要專門存儲當前字段的數據?

  • store 設定爲 true,便可存儲該字段的原始內容(與 _source 中的不相關)
  • 通常結合_source的 enabled 設定爲 false 時使用

實例

博客文章 blog_index

  • 標題 title
  • 發佈日期 publish_date
  • 做者 author
  • 摘要 abstract
  • 內容 content 內容很是大
  • 網絡地址 url

blog_index的mapping設置以下:

PUT blog_index
{
  "mappings": { "_source": { "enabled": false }, "properties": { "title": { "type": "text", "fields": { "kw": { "type": "keyword" } }, "store": true }, "publish_date": { "type": "date", "store": true }, "author": { "type": "keyword", "store": true }, "abstract": { "type": "text", "store": true }, "content": { "type": "text", "store": true }, "url": { "type": "keyword", "norms": false, "ignore_above": 100, "store": true, "doc_values": false } } } } 

如上設置後,_source中不會存儲原始值,查詢時指定要查詢的字段,每一個分片查詢時就不會返回content字段(字段內容較大,佔用內存大),提升了查詢效率

GET /blog_index/_search
{
  "stored_fields": [ "title", "publish_date", "author", "abstract", "url" ], "query": { "match": { "content": "good" } }, "highlight": { "fields": { "content": {} } } } 

ES中關聯關係處理

ES不擅長處理關係型數據庫中的關聯關係(底層存儲的倒排索引,倒排索引並不適合處理關聯關係),好比文章表 blog 與評論表 comment 之間經過 blog_id 關聯,在ES中能夠經過以下兩種手段變相解決:

  • Nested Object
  • Parent / Child

例如:

評論 Comment

  • 文章Id blogid
  • 評論人username
  • 評論日期 date
  • 評論內容 content
 
關係型數據庫中

關聯關係處理之 Nested Object

1.直接將comment整合到blog中
 
2.查詢結果不符合要求
 
3.錯誤緣由:Comments默認是Object Array,存儲結構相似下面的形式
 
4.Nested Object 能夠解決這個問題
 
5.Nested查詢語法
 
6. Nested Object Array的存儲結構
 

關聯關係處理之 Parent/Child

ES還提供了相似關係數據庫中 join 的實現方式,使用join數據類型實現

1.建立索引時mapping配置
 
2.建立父/子文檔
 
3. 常見query 語法
  • parent_id 返回某父文檔的子文檔
  • has_child 返回包含某子文檔的父文檔
  • has_parent 返回包含某父文檔的子文檔
parent_id 查詢:返回某父文檔的子文檔
 
has_child 查詢:返回包含某子文檔的父文檔
 
has_parent 查詢:返回包含某父文檔的子文檔
 

Nested Object vs Parent/Child

 

Reindex

指重建全部數據的過程,通常發生在以下狀況:

  • mapping 變動,好比字段類型變化、分詞器字典更新等
  • setting 變動,好比分片數更改等
  • 遷移數據

ES提供了現成的API用於完成該工做

  • _update_by_query 在現有索引上重建
  • _reindex 在其餘索引上重建

Reindex - Update By Query API

 

Reindex - Reindex API

 
 

Reindex - Task Management API

  • 數據重建的時間受源索引文檔規模的影響,當規模越大時,所需時間越多,此時須要經過設定url參數wait_for_completionfalse來異步執行,ES以 task 來描述此類執行任務
  • ES提供了 Task API 來查看任務的執行進度和相關數據
 

5. ES中數據模型的一些建議

數據模型版本管理

  • 對Mapping進行版本管理
    • 包含在代碼或者以專門的文件進行管理,添加好註釋,並加入Git等版本管理倉庫中,方便回顧
    • 爲每一個增長一個metadata字段,在其中維護一些文檔相關的元數據,方便對數據進行管理


       

防止字段過多

  • 字段過多主要有以下的壞處:
    • 難於維護,當字段成百上干時,基本很難有人能明確知道每一個字段的含義
    • mapping 的信息存儲在 cluster state 裏面,過多的字段會致使 mapping 過大,最終致使更新變慢
  • 經過設置 index.mapping.total_fields.limit 能夠限定索引中最大字段數,默認是1000
  • 能夠經過 key/value 的方式解決字段過多的問題,但並不完美
  • 通常字段過多的緣由是因爲沒有高質量的數據建模致使的,好比 dynamic 設置爲true
  • 考慮拆分多個索引來解決問題

key/value方式詳解

 
mapping的配置
 
添加文檔的語法
 
查詢的語法

key/value方式的弊端

  • 雖然經過這種方式能夠極大地減小Field數目,但也有一些明顯的壞處
    • query語句複雜度飆升,且有一些可能沒法實現,好比聚合分析相關的
    • 不利於在 Kibana 中作可視化分析

11、集羣調優建議

1. 生產環境集羣搭建建議 Set up Elasticsearch

  • 系統設置要到位 Important System Configuration

  • ES設置儘可能簡潔

    • elasticsearch.yml 中儘可能只寫必備的參數,其餘能夠經過api動態設置的參數都經過api來設定 Important Elasticsearch configuration
    • 隨着ES的版本升級,不少網絡流傳的配置參數已經再也不支持,所以不要隨便複製別人的集羣配置參數
  • elasticsearch.yml 中建議設定的基本參數

cluster.name
node.name
node.master/node.data/node.ingest
network.host 建議顯示指定爲內網ip,不要偷懶直接設爲0.0.0.0 discovery.zen.ping.unicast.hosts 設定集羣其餘節點地址 discovery.zen.minimum_master_nodes 通常設定爲2 path.data/path.log 除上述參數外再根據須要增長其餘的靜態配置參數 
  • 動態設定的參數有transient和persistent兩種設置,前者在集羣重啓後會丟失,後者不會,但兩種設定都會覆蓋 elasticsearch.yml中的配置
PUT /_cluster/settings
{
  "persistent":{ "discovery.zen.minimum_master_nodes":2 }, "transient":{ "indices.store.throttle.max_bytes_per_sec":"50mb" } } 
  • 關於JVM內存設定
    • 不要超過31GB,預留一半內存給操做系統,用來作文件緩存
    • 具體大小根據該node要存儲的數據量來估算,爲了保證性能,在內存和數據量間有一個建議的比例
      • 搜索類項目的比例建議在 1:16 之內
      • 日誌類項目的比例建議在 1:48 ~ 1:96
    • 假設總數據量大小爲1TB,3個 node,1個副本,那麼每一個 node 要存儲的數據量爲 2TB/
      3=666GB,即700GB左右,作20%的預留空間,每一個node 要存儲大約850GB的數據
      • 若是是搜索類項目,每一個node內存大小爲850GB/16=53GB,大於31GB。31*16=496,即每一個node 最多存儲496GB數據,因此須要至少5個node
      • 若是是日誌類型項目,每一個node內存大小爲850GB/48=18GB,所以3個節點足夠

2. 寫性能優化

ES 寫數據過程

  • refresh
  • translog
  • flush

ES 寫數據 - refresh

  • segment 寫入磁盤的過程依然很耗時,能夠藉助文件系統緩存的特性,先將 segment 在緩存中建立並開放查詢來進一步提高實時性,該過程在 es 中被稱爲 refresh。


     
  • 在 refresh 以前文檔會先存儲在一個 buffer 中,refresh 時將 buffer 中的全部文檔清空並生成 segment


     
  • es默認每1秒執行一次 refresh,所以文檔的實時性被提升到1秒,這也是 es 被稱爲近實時(Near Real Time)的緣由

ES寫數據 - translog

  • 若是在內存中的segment尚未寫入磁盤前發生了宕機,那麼其中的文檔就沒法恢復了,如何解決這個問題?
    • es 引入 translog 機制。寫入文檔到 buffer 時,同時將該操做寫入 translog。
    • translog 文件會即時寫入磁盤(fsync),6.x默認每一個請求都會落盤,能夠修改成每5秒寫一次,這樣風險即是丟失5秒內的數據,相關配置爲index.translog.*
    • es 啓動時會檢查 translog 文件,並從中恢復數據


       

ES 寫數據 - flush

  • flush 負責將內存中的 segment 寫入磁盤,主要作以下的工做:
    • 將 translog 寫入磁盤
    • 將 index buffer 清空,其中的文檔生成一個新的 segment,至關於一個 refresh 操做
    • 更新 commit point 並寫入磁盤
    • 執行 fsync 操做,將內存中的 segment 寫入磁盤
    • 刪除舊的 translog 文件
 

寫性能優化

  • 目標是增大寫吞吐量 - EPS(Events Per Second)越高越好
  • 優化方案
    • 客戶端:多線程寫,批量寫
    • ES:在高質量數據建模的前提下,主要是在 refresh、translog 和 flush 之間作文章

寫性能優化 - refresh

  • 目標爲下降refresh的頻率
    • 增大refresh_interval,下降實時性,以增大一次 refresh 處理的文檔數,默認是1s,設置爲-1直接禁止自動refresh
    • 增大index buffer size,參數爲indices.memory.index_buffer_size(靜態參數,須要設定在elasticsearch.yml中),默認爲10%

寫性能優化 - translog

  • 目標是下降 translog 寫磁盤的頻率,從而提升寫效率,但會下降容災能力
    • index.translog.durability 設置爲 async,index.translog.sync_interval 設置須要的大小,好比120s,那麼 translog 會改成每120s寫一次磁盤
    • index.translog.flush_threshold_size 默認爲512mb,即 translog 超過該大小時會觸發一次 flush,那麼調大該大小能夠避免 flush 的發生

寫性能優化 - flush

  • 目標爲下降flush的次數,在6.x可優化的點很少,多爲es自動完成

寫性能優化 - 其餘

  • 副本設置爲0,寫入完畢再增長
  • 合理地設計shard數,並保證 shard 均勻地分配在全部 node 上,充分利用全部 node 的資源
    • index.routing.allocation.total_shards_per_node 限定每一個索引在每一個node上可分配的總主副分片數
    • 5個 node,某索引有10個主分片,1個副本,上述值應該設置爲多少?
      • (10+10)/5=4
      • 實際要設置爲5個,防止在某個node下線時,分片遷移失敗的問題

案例 - 日誌場景寫性能優化

  • 主要爲index級別的設置優化,以日誌場景舉例,通常會有以下的索引設定:
 

3. 讀性能優化

讀性能主要受如下幾方面影響:

  • 數據模型是否符合業務模型?
  • 數據規模是否過大?
  • 索引配置是否優化?
  • 查詢語句是否優化?

讀性能優化 - 數據建模

  • 高質量的數據建模是優化的基礎
    • 將須要經過script腳本動態計算的值提早算好做爲字段存到文檔中
    • 儘可能使得數據模型貼近業務模型

讀性能優化 - 數據規模

  • 根據不一樣的數據規模設定不一樣的SLA
    • 上萬條數據與上千萬條數據性能確定存在差別

讀性能優化 - 索引配置調優

  • 索引配置優化主要包括以下:
    • 根據數據規模設置合理的主分片數,能夠經過測試獲得最適合的分片數
    • 設置合理的副本數目,不是越多越好

讀性能優化 - 查詢語句調優

  • 查詢語句調優主要有如下幾種常見手段:
    • 儘可能使用Filter上下文,減小算分的場景,因爲Filter有緩存機制,能夠極大提高查詢性能
    • 儘可能不使用Script進行字段計算或者算分排序等
    • 結合profile、explain API分析慢查詢語句的癥結所在,而後再去優化數據模型

4. 其餘優化

如何設定Shard數?

  • ES的性能基本是線性擴展的,所以咱們只要測出1個Shard的性能指標,而後根據實際性能需求就能算出須要的Shard數。好比單Shard寫入eps是10000,而線上eps需求是50000,那麼你須要5個shard。(實際還要考慮副本的狀況)
  • 測試1個Shard的流程以下:
    • 搭建與生產環境相同配置的單節點集羣
    • 設定一個單分片零副本的索引
    • 寫入實際生產數據進行測試,獲取寫性能指標
    • 針對數據進行查詢請求,獲取讀性能指標
  • 壓測工具能夠採用 esrally
  • 壓測的流程仍是比較複雜,能夠根據經驗來設定。若是是搜索引擎場景,單Shard大小不要超過15GB,若是是日誌場景,單Shard大小不要超過50GB(Shard越大,查詢性能越低)
  • 此時只要估算出你索引的總數據大小,而後再除以上面的單Shard大小也能夠獲得分片

5. 監控

X-Pack Monitoring

    • 官方推出的免費集羣監控功能
    • kibana7.0能夠自動安裝x-pack

【轉】 https://www.jianshu.com/p/9b062f80c0cf

相關文章
相關標籤/搜索