1. 程式人生 > >elasticsearch學習筆記-倒排索引

elasticsearch學習筆記-倒排索引

一,倒排索引(Inverted Index)

ElasticSearch引擎把文件資料寫入到倒排索引(Inverted Index)的資料結構中,倒排索引建立的是分詞(Term)和文件(Document)之間的對映關係,在倒排索引中,資料是面向詞(Term)而不是面向文件的。

舉個例子,文件和詞條之間的關係如下圖:

欄位值被分析之後,儲存在倒排索引中,倒排索引儲存的是分詞(Term)和文件(Doc)之間的關係,簡化版的倒排索引如下圖:

從圖中可以看出,倒排索引有一個詞條的列表,每個分詞在列表中是唯一的,記錄著詞條出現的次數,以及包含詞條的文件。實際上,ElasticSearch引擎建立的倒排索引比這個複雜得多。

1,段是倒排索引的組成部分

倒排索引是由段(Segment)組成的,段儲存在硬碟(Disk)檔案中。索引段不是實時更新的,這意味著,段在寫入硬碟之後,就不再被更新。在刪除文件時,ElasticSearch引擎把已刪除的文件的資訊儲存在一個單獨的檔案中,在搜尋資料時,ElasticSearch引擎首先從段中執行查詢,再從查詢結果中過濾被刪除的文件,這意味著,段中儲存著被刪除的文件,這使得段中含有”正常文件“的密度降低。多個段可以通過段合併(Segment Merge)操作把“已刪除”的文件將從段中物理刪除,把未刪除的文件合併到一個新段中,新段中沒有”已刪除文件“,因此,段合併操作能夠提高索引的查詢速度,但是,段合併是IO密集型操作,需要消耗大量的硬碟IO。

在ElasticSearch中,大多數查詢都需要從硬碟檔案(索引的段資料儲存在硬碟檔案中)中獲取資料,因此,在全域性配置檔案elasticsearch.yml 中,把結點的路徑(Path)配置為效能較高的硬碟,能夠提高查詢效能。預設情況下,ElasticSearch使用基於安裝目錄的相對路徑來配置結點的路徑,安裝目錄由屬性path.home顯示,在home path下,ElasticSearch自動建立config,data,logs和plugins目錄,一般情況下不需要對結點路徑單獨配置。結點的檔案路徑配置項:

  • path.data:設定ElasticSearch結點的索引資料儲存的目錄,多個數據檔案使用逗號隔開,例如,path.data: /path/to/data1,/path/to/data2;
  • path.work:設定ElasticSearch的臨時檔案儲存的目錄;

2,分詞和原始文字的儲存

對映引數index決定ElasticSearch引擎是否對文字欄位執行分析操作,也就是說分析操作把文字分割成一個一個的分詞,也就是標記流(Token Stream),把分詞編入索引,使分詞能夠被搜尋到:

  • 當index為analyzed時,該欄位是分析欄位,ElasticSearch引擎對該欄位執行分析操作,把文字分割成分詞流,儲存在倒排索引中,使其支援全文搜尋;
  • 當index為not_analyzed時,該欄位不會被分析,ElasticSearch引擎把原始文字作為單個分詞儲存在倒排索引中,不支援全文搜尋,但是支援詞條級別的搜尋;也就是說,欄位的原始文字不經過分析而儲存在倒排索引中,把原始文字編入索引,在搜尋的過程中,查詢條件必須全部匹配整個原始文字;
  • 當index為no時,該欄位不會被儲存到倒排索引中,不會被搜尋到;

欄位的原始值是否被儲存到倒排索引,是由對映引數store決定的,預設值是false,也就是,原始值不儲存到倒排索引中。

對映引數index和store的區別在於:

  • store用於獲取(Retrieve)欄位的原始值,不支援查詢,可以使用投影引數fields,對stroe屬性為true的欄位進行過濾,只獲取(Retrieve)特定的欄位,減少網路負載;
  • index用於查詢(Search)欄位,當index為analyzed時,對欄位的分詞執行全文查詢;當index為not_analyzed時,欄位的原始值作為一個分詞,只能對欄位的原始文字執行詞條查詢;

3,單個分詞的最大長度

如果設定欄位的index屬性為not_analyzed,原始文字將作為單個分詞,其最大長度跟UTF8 編碼有關,預設的最大長度是32766Bytes,如果欄位的文字超過該限制,那麼ElasticSearch將跳過(Skip)該文件,並在Response中丟擲異常訊息:

operation[607]: index returned 400 _index: ebrite _type: events _id: 76860 _version: 0 error: Type: illegal_argument_exception Reason: "Document contains at least one immense term in field="event_raw" (whose UTF8 encoding is longer than the max length 32766), all of which were skipped. Please correct the analyzer to not produce such terms. The prefix of the first immense term is: '[112, 114,... 115]...', original message: bytes can be at most 32766 in length; got 35100" CausedBy:Type: max_bytes_length_exceeded_exception Reason: "bytes can be at most 32766 in length; got 35100"

可以在欄位中設定ignore_above屬性,該屬性值指的是字元數量,而不是位元組數量;由於一個UTF8字元最多佔用3個位元組,因此,可以設定

“ignore_above”:10000

這樣,超過30000位元組之後的字元將會被分析器忽略,單個分詞(Term)的最大長度是30000Bytes。

The value for ignore_above is the character count, but Lucene counts bytes. If you use UTF-8 text with many non-ASCII characters, you may want to set the limit to 32766 / 3 = 10922 since UTF-8 characters may occupy at most 3 bytes.

二,列式儲存(doc_values)

預設情況下,大多數字段被索引之後,能夠被搜尋到。倒排索引是由一個有序的詞條列表構成的,每一個詞條在列表中都是唯一存在的,通過這種資料儲存模式,你可以很快查詢到包含某一個詞條的文件列表。但是,排序和聚合操作採用相反的資料訪問模式,這兩種操作不是查詢詞條以發現文件,而是查詢文件,以發現欄位中包含的詞條。ElasticSearch使用列式儲存實現排序和聚合查詢。

文件值(doc_values)是儲存在硬碟上的資料結構,在索引時(index time)根據文件的原始值建立,文件值是一個列式儲存風格的資料結構,非常適合執行儲存和聚合操作,除了字元型別的分析欄位之外,其他欄位型別都支援文件值儲存。預設情況下,欄位的文件值儲存是啟用的,除了字元型別的分析欄位之外。如果不需要對欄位執行排序或聚合操作,可以禁用欄位的文件值,以節省硬碟空間。

"mappings": {
    "my_type": {
        "properties": {
        "status_code": { 
            "type":       "text",
            "index":      "not_analyzed"
        },
        "session_id": { 
            "type":       "text",
            "index":      "not_analyzed",
            "doc_values": false
        }
        }
    }
}

三,順排索引(fielddata)

字元型別的分析欄位,不支援文件值(doc_values),但是,支援fielddata資料結構,fielddata資料結構儲存在JVM的堆記憶體中。相比文件值(資料儲存在硬碟上),fielddata欄位(資料儲存在記憶體中)的查詢效能更高。預設情況下,ElasticSearch引擎在第一次對欄位執行聚合或排序查詢時((query-time)),建立fielddata資料結構;在後續的查詢請求中,ElasticSearch引擎使用fielddata資料結構以提高聚合和排序的查詢效能。

在ElasticSearch中,倒排索引的各個段(segment)的資料儲存在硬碟檔案上,從整個倒排索引的段中讀取欄位資料之後,ElasticSearch引擎首先反轉詞條和文件之間的關係,建立文件和詞條之間的關係,即建立順排索引,然後把順排索引儲存在JVM的堆記憶體中。把倒排索引載入到fielddata結構是一個非常消耗硬碟IO資源的過程,因此,資料一旦被載入到記憶體,最好保持在記憶體中,直到索引段(segment)的生命週期結束。預設情況下,倒排索引的每個段(segment),都會建立相應的fielddata結構,以儲存字元型別的分析欄位值,但是,需要注意的是,分配的JVM堆記憶體是有限的,Fileddata把資料儲存在記憶體中,會佔用過多的JVM堆記憶體,甚至耗盡JVM賴以正常執行的記憶體空間,反而會降低ElasticSearch引擎的查詢效能。

1,format屬性

fielddata會消耗大量的JVM記憶體,因此,儘量為JVM設定大的記憶體,不要為不必要的欄位啟用fielddata儲存。通過format引數控制是否啟用欄位的fielddata特性,字元型別的分析欄位,fielddata的預設值是paged_bytes,這就意味著,預設情況下,字元型別的分析欄位啟用fielddata儲存。一旦禁用fielddata儲存,那麼字元型別的分析欄位將不再支援排序和聚合查詢。

"mappings": {
    "my_type": {
      "properties": {
        "text": {
          "type": "text",
          "fielddata": {
            "format": "disabled" 
          }
        }
      }
    }
  }

2,載入屬性(loading)

loading屬性控制fielddata載入到記憶體的時機,可能的值是lazy,eager和eager_global_ordinals,預設值是lazy。

  • lazy:fielddata只在需要時載入到記憶體,預設情況下,在第一次搜尋時,fielddata被載入到記憶體中;但是,如果查詢一個非常大的索引段(Segment),lazy載入方式會產生較大的時間延遲。
  • eager:在倒排索引的段可用之前,其資料就被載入到記憶體,eager載入方式能夠減少查詢的時間延遲,但是,有些資料可能非常冷,以至於沒有請求來查詢這些資料,但是冷資料依然被載入到記憶體中,佔用緊缺的記憶體資源。
  • eager_global_ordinals:按照global ordinals積極把fielddata載入到記憶體。

四,JVM程序使用的記憶體和堆記憶體

1,配置ElasticSearch使用的記憶體

ElasticSearch使用JAVA_OPTS環境變數(Environment Variable)啟動JVM程序,在JAVA_OPTS中,最重要的配置是:-Xmx引數控制分配給JVM程序的最大記憶體,-Xms引數控制分配給JVM程序的最小記憶體。通常情況下,使用預設的配置就能滿足工程需要。

ES_HEAP_SIZE 環境變數控制分配給JVM程序的堆記憶體(Heap Memory)大小,順排索引(fielddata)的資料儲存在堆記憶體(Heap Memory)中。

2,記憶體鎖定

大多數應用程式嘗試使用盡可能多的記憶體,並儘可能把未使用的記憶體換出,但是,記憶體換出會影響ElasticSearch引擎的查詢效能,推薦啟用記憶體鎖定,禁用ElasticSearch記憶體的換進換出。

在全域性配置文件 elasticsearch.yml中,設定 bootstrap.memory_lock為ture,這將鎖定ElasticSearch程序的記憶體地址空間,阻止ElasticSearch記憶體被OS換出(Swap out)。

作者悅光陰 本文版權歸作者和部落格園所有,歡迎轉載,但未經作者同意,必須保留此段宣告,且在文章頁面醒目位置顯示原文連線,否則保留追究法律責任的權利。