如何設計一個高性能 Elasticsearch mapping


前言

在關系型數據庫設計當中,表的設計尤其重要,然而關系型數據庫更關注的表與表之間的關系,以及表的划分是否合理,而 Elasticsearch 中卻更加關注字段類型的設計,一個好的字段類型設計可以更好的利用 Elasticsearch 的搜索分析特性。

mapping

如果說我們想要用好 Elasticsearch,那么就必須要先了解 mapping 什么是 mapping。一句話:mapping是定義如何存儲和索引文檔及其包含的字段的過程。

mapping 能做什么

前面我們提到,在 Elasticsearch 中,mapping 類似於傳統關系型數據庫的表結構定義,主要做以下幾件事:

  • 定義字段名稱和字段類型。
  • 定義倒排索引相關的配置,比如是否被索引,是否可以被分詞等。

mapping 可以分為兩種:Dynamic mappingExplicit mapping

Dynamic mapping

Dynamic mapping 即:動態映射。動態映射顧名思義就是 mapping 會被動態創建,也就是說我們不需要定義 mapping 就可以往一個索引插入數據,插入索引數據之后,Elasticsearch 會根據插入的數據自動推測數據類型,進而動創建 mapping

比如下面就是往一個不存在的索引 index_001 插入一條數據:

PUT index_001/_doc/1
{
  "name":"lonely wolf",
  "age": 18,
  "create_date":"2021-05-19 20:45:11",
  "update_date":"2021-05-23"
}

插入數據之后,執行 GET index_001 來查詢一下索引信息:

可以發現,這時候索引已經被自動創建了,而且 age 字段被 Elasticsearch 定義為 long 類型,update_date 被定義為 data 類型,其他兩個字段則被推測為 text 類型。

Elasticsearch 中自動映射類型規則可以通過 dynamic 參數進行配置,dynamic 類型有 4 種:

dynamic=true

默認值。當設置為 true 時,一旦有新字段插入文檔,則 mapping 會被同步更新。

我們在上面的文檔中再插入一個新文檔,新文檔新增一個 address 字段:

PUT index_001/_doc/2
{
  "name":"lonely wolf2",
  "age": 20,
  "create_date":"2021-05-23 11:37:11",
  "update_date":"2021-05-23",
  "address":"廣東深圳"
}

然后再查看一下 mapping,可以看到 mapping 已經新增了一個 address 字段,mapping 字段被更新意味着該字段會加入索引:

dynamic=runtime

這個類型和 true 類型非常相似,但是有一個非常大的區別就是,雖然加入新字段也會更新 mapping,但是新加入的字段不會被索引,也就是不會使得索引變大,不過雖然不被索引,但是新加入的字段依然可以被查詢,只是查詢的代價會更大。所以這種類型一般不建議用在經常查詢的條件字段上,而更適合用在一些不確定數據結構的日志類索引中。

修改 dynamic 類型:

PUT index_001/_mapping
{
  "dynamic":  "runtime"
}

新增一個文檔,並加入一個新字段:

PUT index_001/_doc/3
{
  "email":"123@qq.com"
}

最后詢一下 mapping,可以看到字段屬性是 runtime,而且類型是 keyword

下表就是自動創建 mapping 時,Elasticsearch 的映射關系:

插入數據類型 dynamic=true dynamic=runtime
null 不會添加任何字段 不會添加任何字段
true 或 false boolean boolean
double float double
integer long long
object object object
string(通過 date 校驗) date date
string(通過 numreic 校驗) float 或 long double 或 long
string(沒有通過 date 或 numreic 校驗) text ,並且同時會創建一個 keyword 子域 keyword
array 取決於數組中第一個非 null 值 取決於數組中第一個非 null 值

PS:keyword 表示 不參與分詞。

dynamic=false

當設置為 false 時,新加入的字段不會被更新到 mapping,也就是說新字段不會被索引,故以這個字段為條件進行搜索時,無法被搜索到(這一點要注意和 runtime 類型進行區分),不過雖然無法被索引,但是該字段會出現在 _source 中。也就是說該字段不能作為查詢條件,但是能被查詢出來

接下來我們將 dynamic 修改為 false,並新增一個字段來驗證,可以發現新增的字段會出現在 _source 中,但是無法作為條件被查詢出來:

dynamic=strict

這種類型最為嚴格,表示不允許新增一個不在 mapping 中的字段,一旦新增的字段不在 mapping 定義中,則直接報錯:

是否可以修改 mapping 中的數據類型

Elasticsearch 中,一旦一個字段被定義在了 mapping 中,是無法被修改的,因為一旦字段被修改了,就會無法被索引(新增字段除外),所以一般我們需要修改索引的話,都會重建索引,並采用 reindex 操作來遷移數據。

關閉 dynamic mapping

可以通過以下兩個配置來關閉 dynamic mapping,以下兩個屬性默認值均為 true,如果需要關閉,則需要修改為 false

action.auto_create_index: true
index.mapper.dynamic: true

Explicit mapping

Explicit mapping 即:顯式映射。也就是說這時候我們需要顯示的定義字段類型。

Elasticsearch 中支持的字段類型很多,在這里就舉一些比較常用的字段類型:

text 類型

這是最常用的一種類型,存儲字符串,用於全文索引。當字段被定義為 text 類型時,默認不能用於聚合,排序等操作:

可以看到,用 text 類型字段排序匯報湊,如果想要允許這些操作,可以通過設置 fielddata=true,如下

PUT my-index-011/_mapping
{
  "properties": {
    "my_field": { 
      "type":     "text",
      "fielddata": true
    }
  }
}

field 字段存儲在堆內存中,因為其涉及到的計算比較消耗性能,所以一般不建議設置 fielddata=true,而是通過建立一個 keyword 子域來實現(默認方式):

PUT index_111
{
  "mappings": {
    "properties": {
      "my_field": { 
        "type": "text",
        "fields": {
          "keyword": { 
            "type": "keyword"
          }
        }
      }
    }
  }
}

這種定義方式我們可以將一個字段同時作為 textkeyword 類型使用,如果要用於聚合或者排序等操作則可以使用 字段名.keyword 來作為字段名來進行操作:

keyword 類型

這種類型也非常常用,該字段存儲的數據表示一個整體,不可被分詞,所以一般不會用來定義大本文的全文檢索字段,而是用來存儲一些結構化的字符串,比如:id,郵箱,標簽等。

keyword 類型一般用於聚合,排序等操作。除此之外,該字段還有兩種衍生類型:constant_keywordwildcard

  • constant_keyword:一般用於定義常量類型,比如一個索引中某一個字段全部為同一個值,可以定義為這種類型。
  • wildcard:一般用於模糊匹配查詢或者正則匹配查詢。

如下就是一個模糊匹配查詢的示例(可以配合通配符使用,類似於關系型數據庫的 like 操作):

GET index_112/_search
{
  "query": {
    "wildcard": {
      "my_wildcard": {
        "value": "*quite*lengthy"
      }
    }
  }
}

date 類型

用於定義日期類型,定義日期類型的同時,可以通過 format 來指定日期的格式:

PUT index_113
{
  "mappings": {
    "properties": {
      "date": {
        "type":   "date",
        "format": "yyyy-MM-dd HH:mm:ss||yyyy-MM-dd||epoch_millis"
      }
    }
  }
}

numeric 類型

Elasticsearch 中提供了比較多的格式用來表示不同長度的數字類型:

數字類型 長度
long 64 位有符號整數。范圍:-2 的 63 次方到 2 的 63 次方 -1
integer 32 位有符號整數。范圍:-2 的 31 次方到 2 的 31 次方 -1
short 16 位有符號整數。范圍:-32768 到 32767
byte 8 位有符號整數。范圍:-128 到 127
double 64 位雙精度小數
float 32 位單精度小數
half_float 16 位單精度小數
scaled_float 帶有縮放因子的浮點數,一般適用於存放金額之類的數據。比如 18.88 元,縮放因子是 100,那么在索引時會被索引為 1888(即:原值 * 縮放因子)
unsigned_long 64 位無符號整數。范圍:0 到 2 的 64 次方減 1

定義方式如下所示:

PUT index_002
{
  "mappings": {
    "properties": {
      "number_of_bytes": {
        "type": "integer"
      },
      "time_in_seconds": {
        "type": "float"
      },
      "price": {
        "type": "scaled_float",
        "scaling_factor": 100
      }
    }
  }
}

boolean 類型

布爾類型比較簡單,只有 truefalse 兩種:

PUT index_001
{
  "mappings": {
    "properties": {
      "is_published": {
        "type": "boolean"
      }
    }
  }
}

其他類型

除了上面介紹的一些比較常用的數據類型,Elasticsearch 中還有一些高級數據類型:如 Nested(嵌套類型),地理數據類型,ip 類型等。

總結

Elasticsearch 中支持動態 mapping 和顯示 mapping 兩種,在使用中有時候可以先插入一條數據到臨時索引,等自動生成 mapping 之后,在對現有 mapping 進行修改調整,在字段上尤其要考慮好 text 類型和 keyword 類型的設置,如果需要支持全文搜索和分詞搜索,則需要使用 text 類型,需要支持關鍵字模糊搜索或者聚合排序等操作可以考慮使用 keyword 字段。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM