ElasticSearch入門 第八篇:存儲

這是ElasticSearch 2.4 版本系列的第八篇:html

 

在ElasticSearch 2.4版本中,文檔存儲的介質分爲內存和硬盤:內存速度快,可是容量有限;硬盤速度較慢,可是容量很大。同時,ElasticSearch進程自身的運行也須要內存空間,必須保證ElasticSearch進程有充足的運行時內存。爲了使ElasticSearch引擎達到最佳性能,必須合理分配有限的內存和硬盤資源。正則表達式

一,倒排索引(Inverted Index)bootstrap

ElasticSearch引擎把文檔數據寫入到倒排索引(Inverted Index)的數據結構中,倒排索引創建的是分詞(Term)和文檔(Document)之間的映射關係,在倒排索引中,數據是面向詞(Term)而不是面向文檔的。數組

舉個例子,文檔和詞條之間的關係以下圖:網絡

字段值被分析以後,存儲在倒排索引中,倒排索引存儲的是分詞(Term)和文檔(Doc)之間的關係,簡化版的倒排索引以下圖:session

從圖中能夠看出,倒排索引有一個詞條的列表,每一個分詞在列表中是惟一的,記錄着詞條出現的次數,以及包含詞條的文檔。實際上,ElasticSearch引擎建立的倒排索引比這個複雜得多。數據結構

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

倒排索引是由段(Segment)組成的,段存儲在硬盤(Disk)文件中。索引段不是實時更新的,這意味着,段在寫入硬盤以後,就再也不被更新。在刪除文檔時,ElasticSearch引擎把已刪除的文檔的信息存儲在一個單獨的文件中,在搜索數據時,ElasticSearch引擎首先從段中執行查詢,再從查詢結果中過濾被刪除的文檔,這意味着,段中存儲着被刪除的文檔,這使得段中含有」正常文檔「的密度下降。多個段能夠經過段合併(Segment Merge)操做把「已刪除」的文檔將從段中物理刪除,把未刪除的文檔合併到一個新段中,新段中沒有」已刪除文檔「,所以,段合併操做可以提升索引的查找速度,可是,段合併是IO密集型操做,須要消耗大量的硬盤IO。elasticsearch

在ElasticSearch中,大多數查詢都須要從硬盤文件(索引的段數據存儲在硬盤文件中)中獲取數據,所以,在全局配置文件elasticsearch.yml 中,把結點的路徑(Path)配置爲性能較高的硬盤,可以提升查詢性能。默認狀況下,ElasticSearch使用基於安裝目錄的相對路徑來配置結點的路徑,安裝目錄由屬性path.home顯示,在home path下,ElasticSearch自動建立config,data,logs和plugins目錄,通常狀況下不須要對結點路徑單獨配置。結點的文件路徑配置項:ide

  • 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":       "string",
            "index":      "not_analyzed"
        },
        "session_id": { 
            "type":       "string",
            "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": "string",
          "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)。

 

參考文檔:

Elasticsearch Reference [2.4] » Mapping » Mapping parameters

相關文章
相關標籤/搜索