ES 文件與索引介紹

來份鍋包肉發表於2022-04-15

在之前的文章中,介紹了 ES 整體的架構和內容,這篇主要針對 ES 最小的儲存單位 - 文件以及由文件組成的索引進行詳細介紹。

會涉及到如下的內容:

  1. 文件的 CURD 操作。
  2. Dynamic Mapping 和顯示 Mapping 的區別
  3. 常見 Mapping 型別與常見引數介紹
  4. Index Template 和 Dynamic Template

對文件進行操作

單個文件 CRUD

和常見的資料庫類似,ES 也支援 CURD 操作:

下面展示了對單個 ES 文件的操作:

操作名稱 URL 解釋
Index image-20220406215957677 建立或者更新索引中的文件。在指定 id 的情況下,如果 id 存在,則會更新。如果不指定,則會建立。
Get image-20220406220223805 查詢某個文件。
Delete DELETE //_doc/<_id> 刪除某個指定的文件。
Update POST //_update/<_id> 更新某個文件中的內容,可以理解成 Patch 的更新。如果想完全替換文件,請使用 index.

下面是實際操作文件的例子, 開啟 kibana 的開發者工具:

先來建立一個文件:

ES 在建立文件時,會有兩種方式 index 和 create。index 與 create 不同在於,在指定 id 的情況下,如果 id 存在,index 會覆蓋,同時版本號+1,而 create 會報錯不讓建立。

這裡手動指定 id 為10,使用 index 方法,建立了一個文件,注意版本號為 1。

image-20220406222248162

注意再次傳送同樣的情況,可以看到正常執行,版本號變成 2了。

image-20220406222310526

但是使用 create 方法:

image-20220406222351472

這裡報錯,顯示文件已經存在。

需要注意的 ES 這裡的更新並不是正常理解的更新,而是先把老文件刪掉,然後建立一個新文件出來。

接著對文件進行更新:

image-20220407073426264

可以看到這裡只更新 user 欄位,這種更新和之前 index 那種更新不同,屬於部分更新,將增加的內容 merge 進原始文件。

對文件進行讀取,這裡由於之前更新了三次,所以 version = 3:

image-20220407073714504

刪除文件就很好理解了,但有一點需要注意,刪除文件時並不會立馬釋放空間,而是將文件標記位 deleted 狀態,後臺程式會在合適的時候清理這些標記位已經刪除的文件。

批量文件操作

批量寫入

相較於當個文件的操作,大批量的操作對於 ES 來說,是更為常見的場景。ES 也提供了批量 API,該 API 支援在一次 API請求中包含 4 種型別, 並且 Response 中會針對每一條操作返回一個對應的結果。

POST _bulk
{ "index" : { "_index" : "test", "_id" : "1" } }
{ "field1" : "value1" }
{ "delete" : { "_index" : "test", "_id" : "2" } }
{ "create" : { "_index" : "test", "_id" : "3" } }
{ "field1" : "value3" }
{ "update" : {"_id" : "1", "_index" : "test"} }
{ "doc" : {"field2" : "value2"} }

批量讀取

可以同時傳入多個文件 id,進行讀取,多個文件可以屬於不同的索引。

GET /_mget
{
  "docs": [
    {
      "_index": "my-index-000001",
      "_id": "1"
    },
    {
      "_index": "my-index-000001",
      "_id": "2"
    }
  ]
}

索引 - Mapping

索引是多個文件的集合,體現了邏輯空間的概念。對於每個索引來說都可以設定 Mapping 和 Setting 兩部分。

其中 Mapping 定義了文件包含欄位的型別與名稱,以及如倒排索引,分詞的一些設定。Setting 定義瞭如何將資料分佈儲存在不同的節點上。

資料型別

ES 中的資料型別分為三種:

  • 簡單型別
  • 複雜型別
    • 物件型別
    • 巢狀型別
  • 特殊型別
    • 地理位置等

下圖中顯示了 ES 中常見的簡單資料型別以及和 SQL 對應的關係。

Elasticsearch type Elasticsearch SQL type SQL type SQL precision
Core types
null null NULL 0
boolean boolean BOOLEAN 1
byte byte TINYINT 3
short short SMALLINT 5
integer integer INTEGER 10
long long BIGINT 19
double double DOUBLE 15
float float REAL 7
half_float half_float FLOAT 3
scaled_float scaled_float DOUBLE 15
keyword type family keyword VARCHAR 32,766
text text VARCHAR 2,147,483,647
binary binary VARBINARY 2,147,483,647
date datetime TIMESTAMP 29
ip ip VARCHAR 39

Dynamic Mapping

我們知道,Mapping 類似於資料庫 Scheme 的定義,但回想之前對文件 CURD 的操作時,我們並未手動設定 Mapping,但可以自動建立文件,原因就在於利用了 Dynamic Mapping 的特性。就是即使索引不存在時,也可以手動建立索引,並根據文件資訊自動推算出對應的 Mapping 關係。

比如之前建立的文件,如下就是生成的 Mapping 關係,ES 自動將 company 和 user 推斷為 text 欄位。

image-20220407085317352

當 Dynamic Mapping 也有自己的缺點:就是推算不準確,比如上面的例子,company 和 user 的欄位為 keyword 型別更為合適,以至於搜尋時出現一些問題。

dynamic Mapping 可以通過 dynamic 欄位進行控制, 其值為 true,false,strict 三種型別。

對於已經建立的索引,在修改 Mapping 分為兩種情況:

  • 增加新的欄位:
    • dynamic 為 true,新欄位寫入後,Mapping 也會被更新
    • dynamic 為 false,欄位可以寫入到 _source, 但 Mapping 不會被更新,自然也不會被索引
    • dynamic 為 strict,不允許寫入
  • 修改已經存在欄位的型別:
    • 不允許修改,因為 Lucene 生成的倒排索引,不允許被修改。
    • 除非重新生成索引。

顯示指定 Mapping

與 Dynamic Mapping 不同,顯示指定 Mapping 可以允許我們手動指定 Mapping 結構。

編寫 Mapping 有兩種方式:

  • 可以參考 doc
  • 利用 dynamic 自動建立功能,查詢後,自己再編輯成想要的結構。

看一個簡單的例子:

PUT user
 {
  "user" : {
    "mappings" : {
      "properties" : {
        "company" : {
          "type" : "keyword"
          "null_value": "NULL"
          }
        },
        "name" : {
          "type" : "keyword",
          "index_options": "offsets"
        },
        "id_card" : {
          "type" : "keyword",
          "index": false # 表示該欄位不需要被索引,不用被搜尋到
        }
      }
  }
}

"null_value":表示對 NULL 值可以進行搜尋。

"index": false 表示該欄位不需要被索引,不用被搜尋到

"index_options": "offsets" 表示對倒排索引的結構進行設定:

  • docs :表示記錄 doc id
  • freqs :表示記錄 doc id 和 term frequencies
  • position :表示記錄 doc id 和 term frequencies 和 term position(Text 型別預設記錄為 position)
  • offsets: 表示記錄 doc id 和 term frequencies 和 term position 以及 character offset.

關於倒排可以檢視之前寫的這篇文章

Index Template 和 Dynamic Template

Index Template

考慮到資料不斷增長的情況的,就需要按照一定的規則,將資料分散在不同的 Index 中。但每次都需要為每個 Index 設定 Mapping 和 Setting 關係。

這時 Index Template 就可以很好滿足這個需求。

在 Index Template 中,可以通過設定一個通配名稱,當建立的索引的名稱,滿足該條件時,就會使用模板的規則。

Note:

  • 模板只會在建立新索引時生效,修改模板不會影響已經建立的索引。
  • 可以設定多個模板,通過 "order" 引數,控制那個模板的規則生效。

下面這個例子就是為告警建立的一個 template,當建立的名字以 alarm 開頭時,就會使用該索引。

image-20220415092031928

Dynamic Template

在上面 Dynamic Mapping 的介紹中知道,ES 對於沒有設定 Mapping 欄位的內容,會自己推算一個型別,但這就可能造成推算型別不準確的情況。

這時就可以用 Dynamic Template 來解決,通過規範插入的欄位的名稱,來指定他的型別:

  • 比如可以 is 開頭的欄位,都設定成 boolean
  • long_ 開頭的欄位,設定成 long
  • 所有字串型別,設定成 keyword

Dynamic Template 直接作用在索引上, 看下面這個例子。

PUT my-index-000001
{
  "mappings": {
    "dynamic_templates": [
      {
        "longs_as_strings": {
          "match_mapping_type": "string",
          "match":   "long_*",
          "unmatch": "*_text",
          "mapping": {
            "type": "long"
          }
        }
      }
    ]
  }
}

PUT my-index-000001/_doc/1
{
  "long_num": "5", 
  "long_text": "foo" 
}

當匹配到以 long 開頭的字串時並且不包含以 _text 結尾,會將其設定成 long 類。

總結

本篇文章中,主要是對 ES 文件和索引的設定進行了說明。

ES 文件支援 CURD 操作,但需要知道 Index 和 create 的區別在於,對於指定 id 情況下的處理方式不同。同時為了適應大資料量的讀取和寫入,可以用 bulk api.

對於 ES 索引來說,在建立時,支援兩種方式來指定 Setting 和 Mapping 的關係。一種 Dynamic Mapping,這種方式不需要手動設定 Index 格式,會根據文件自動的建立,但缺點在於推斷的型別不不準確。而顯示 Mapping,可以手動規定 index 的格式。

考慮到資料不斷增長的情況,需要將資料拆分到不同的 index 上,可以通過 IndexTemplate 實現。

對於 Dynamic Mapping,推斷不準確的情況,可以通過 Dynamic Template 手動規定建立的型別。

參考

https://www.elastic.co/guide/en/elasticsearch/reference/7.16/docs.html

https://www.elastic.co/guide/en/elasticsearch/reference/7.1/mapping-params.html

https://www.elastic.co/guide/en/elasticsearch/reference/current/dynamic-templates.html

相關文章