ES 32 - Elasticsearch 數據建模的探索與實踐

来源:https://www.cnblogs.com/shoufeng/archive/2019/10/07/11631897.html
-Advertisement-
Play Games

本篇博文通過對ES中不同類型的欄位的建模方案進行說明, 並結合實際案例, 演示了index、stored、dynamic等參數的使用, 並歸納了ES處理關聯關係、避免太多的欄位、避免正則查詢、避免空值引起聚合結果失真等最佳實踐. 如有疑問, 留言區見


目錄

1 什麼是數據建模?

數據建模(Data modeling), 是創建數據模型的過程.

數據模型是對真實世界進行抽象描述的一種工具和方法, 實現對現實世界的映射. 比如影視作品、演員、觀眾評論...

數據建模有三個過程: 概念模型 => 邏輯模型 => 數據模型(第三範式)

數據模型, 需要結合使用的資料庫類型, 在滿足業務讀寫性能等需求的前提下, 制定出最終的定義.

2 如何對 ES 中的數據進行建模

ES中的數據建模:

由數據存儲、檢索等功能需求提煉出實體屬性、實體之間的關係 =》形成邏輯模型;

由性能需求提煉製定索引模板、索引Mapping(包括欄位的配置、關係的處理) ==》形成物理模型.

ES 中存儲、檢索的基本單位是索引文檔(document), 文檔由欄位(field)組成, 所以ES的建模就是對欄位進行建模.

文檔類似於關係型資料庫中的一行數據, 欄位對應關係型資料庫中的某一列數據.

2.1 欄位類型的建模方案

(1) text 與 keyword 比較:

  • text: 用於全文本欄位, 文本會被 Analyzer 分詞; 預設不支持聚合分析及排序, 設置 "fielddata": true 即可支持;
  • keyword: 用於 id、枚舉及不需要分詞的文本, 比如身份證號碼、電話號碼,Email地址等; 適用於 Filter(精確匹配過濾)、Sorting(排序) 和 Aggregations(聚合).

  • 設置多欄位類型:

    預設會為文本類型設置成 text, 並設置一個 keyword 的子欄位;
    在處理人類自然語⾔時, 可以添加“英⽂”、“拼⾳”、“標準”等分詞器, 提高搜索結果的正確性.

(2) 結構化數據:

  • 數值類型: 儘量選擇貼近的類型, 例如可以用 byte, 就不要用 long;
  • 枚舉類型: 設置為 keyword, 即使是數字, 也應該設置成 keyword, 獲取更好的性能; 另外範圍檢索使用keyword, 速度更快;

  • 其他類型: 日期、二進位、布爾、地理信息等類型.

2.2 檢索、聚合及排序的建模方案

  • 如不需要檢索、排序和聚合分析, 則可設置 "enable": false ;
  • 如不需要檢索, 則可設置 "index": false ;

  • 如不需要排序、聚合分析功能, 則可設置 "doc_values": false / "fielddate": false ;

  • 更新頻繁、聚合查詢頻繁的 keyword 類型的欄位, 推薦設置 "eager_global_ordinals": true .

2.3 額外存儲的建模方案

  • 是否需要專門存儲當前欄位數據?

"store": true, 可以存儲該欄位的原始內容;

一般結合 "_source": { "enabled": false } 進行使用, 因為預設的 "_source": { "enabled": true }, 也就是添加索引時文檔的原始 JSON 結構都會存儲到 _source 中.

  • disable_source: 禁用 _source 元欄位, 能節約磁碟, 適用於指標型數據 —— 類似於標識欄位、時間欄位的數據, 不會更新、高亮查詢, 多用來進行過濾操作以快速篩選出更小的結果集, 用來支撐更快的聚合操作.

官方建議: 如果更多關註磁碟空間, 那麼建議優先考慮增加數據的壓縮⽐, 而不是禁用 _source;

無法看到 _source 欄位, 就不能做 reindexupdateupdate_by_query 操作;

目前為止, Kibana 中無法對禁用了 _source 欄位的索引進行 Discover 挖掘操作.

—— 謹慎禁用 _source 欄位, 參考: https://www.elastic.co/guide/en/elasticsearch/reference/current/mapping-source-field.html

3 ES 數據建模實例演示

3.1 動態創建映射關係

# 直接寫入一本圖書信息:
POST books/_doc
{
  "title": "Thinking in Elasticsearch 7.2.0",
  "author": "Heal Chow",
  "publish_date": "2019-10-01",
  "description": "Master the searching, indexing, and aggregation features in Elasticsearch.",
  "cover_url": "https://healchow.com/images/29dMkliO2a1f.jpg"
}

# 查看自動創建的mapping關係:
GET books/_mapping
# 內容如下:
{
  "books" : {
    "mappings" : {
      "properties" : {
        "author" : {
          "type" : "text",
          "fields" : {
            "keyword" : {
              "type" : "keyword",
              "ignore_above" : 256
            }
          }
        },
        "cover_url" : {
          "type" : "text",
          "fields" : {
            "keyword" : {
              "type" : "keyword",
              "ignore_above" : 256
            }
          }
        },
        "description" : {
          "type" : "text",
          "fields" : {
            "keyword" : {
              "type" : "keyword",
              "ignore_above" : 256
            }
          }
        },
        "publish_date" : {
          "type" : "date"
        },
        "title" : {
          "type" : "text",
          "fields" : {
            "keyword" : {
              "type" : "keyword",
              "ignore_above" : 256
            }
          }
        }
      }
    }
  }
}

3.2 手動創建映射關係

# 刪除自動創建的圖書索引:
DELETE books

# 手動優化欄位的mapping:
PUT books
{
  "mappings": {
    "_source": { "enabled": true },
    "properties": {
      "title": {
        "type": "text",
        "fields": {
          "keyword": {
            "type": "keyword",
            "ignore_above": 100
          }
        }
      },
      "author": { "type": "keyword" },
      "publish_date": {
        "type": "date",
        "format": "yyyy-MM-dd HH:mm:ss||yyyyMMddHHmmss||yyyy-MM-dd||epoch_millis"
      },
      "description": { "type": "text" },
      "cover_url": {          # index 設置成 false, 不支持搜索, 但支持 Terms 聚合
        "type": "keyword",
        "index": false
      }
    }
  }
}

說明: _source 元欄位預設是開啟的, 若禁用後, 就無法對搜索的結果進行展示, 也無法進行 reindexupdateupdate_by_query 操作.

3.3 新增需求 - 添加大欄位

  • 需求描述: 添加圖書內容欄位, 要求支持全文搜索, 並且能夠高亮顯示.

  • 需求分析: 新需求會導致 _source 的內容過⼤, 雖然我們可以通過source filtering對要搜索結果中的欄位進行過濾:

    "_source": {
        "includes": ["title"]  # 或 "excludes": ["xxx"] 排除某些欄位, includes 優先順序更高
    }

    但這種方式只是 ES 服務端傳輸給客戶端時的過濾, 內部 Fetch 數據時, ES 各數據節點還是會傳輸 _source 中的所有數據到協調節點 —— 網路 IO 沒有得到本質上的降低.

3.4 解決大欄位帶來的性能問題

(1) 在創建 mapping 時手動關閉 _source 元欄位: "_source": { "enabled": false} ;

(2) 然後為每個欄位設置 "store": true .

# 關閉_source元欄位, 設置store=true:
PUT books
{
  "mappings": {
    "_source": { "enabled": false },
    "properties": {
      "title": {
        "type": "text",
        "store": true,
        "fields": {
          "keyword": {
            "type": "keyword",
            "ignore_above": 100
          }
        }
      },
      "author": { "type": "keyword", "store": true },
      "publish_date": {
        "type": "date",
        "store": true,
        "format": "yyyy-MM-dd HH:mm:ss||yyyyMMddHHmmss||yyyy-MM-dd||epoch_millis"
      },
      "description": { "type": "text", "store": true },
      "cover_url": {
        "type": "keyword",
        "index": false,
        "store": true
      },
      "content": { "type": "text", "store": true }
    }
  }
}

(3) 加數據, 併進行高亮查詢:

# 添加包含新欄位的文檔:
POST books/_doc
{
  "title": "Thinking in Elasticsearch 7.2.0",
  "author": "Heal Chow",
  "publish_date": "2019-10-01",
  "description": "Master the searching, indexing, and aggregation features in Elasticsearch.",
  "cover_url": "https://healchow.com/images/29dMkliO2a1f.jpg",
  "content": "1. Revisiting Elasticsearch and the Changes. 2. The Improved Query DSL. 3. Beyond Full Text Search. 4. Data Modeling and Analytics. 5. Improving the User Search Experience. 6. The Index Distribution Architecture.  .........."
}

# 通過 stored_fields 指定要查詢的欄位:
GET books/_search
{
  "stored_fields": ["title", "author", "publish_date"],
  "query": {
    "match": { "content": "data modeling" }
  },
  "highlight": {
    "fields": { "content": {} }
  }
}

查詢結果如下:

{
  "took" : 1,
  "timed_out" : false,
  "_shards" : {
    "total" : 1,
    "successful" : 1,
    "skipped" : 0,
    "failed" : 0
  },
  "hits" : {
    "total" : {
      "value" : 1,
      "relation" : "eq"
    },
    "max_score" : 0.5753642,
    "hits" : [
      {
        "_index" : "books",
        "_type" : "_doc",
        "_id" : "dukLoG0BdfGBNhbF13CJ",
        "_score" : 0.5753642,
        "highlight" : {
          "content" : [
            "<em>Data</em> <em>Modeling</em> and Analytics. 5. Improving the User Search Experience. 6."
          ]
        }
      }
    ]
  }
}

(4) 結果說明:

  • 返回結果中不包含 _source 欄位;
  • 對需要顯示的信息, 要在查詢中指定 "stored_fields": ["xxx", "yyy"] ;
  • 禁⽌ _source 欄位後, 仍然支持使用 Highlights API 的使用.

3.5 mapping中欄位的常用參數

參考: https://www.elastic.co/guide/en/elasticsearch/reference/current/mapping-params.html

  • enabled – 設置成 false, 當前欄位就只存儲, 不支持搜索和聚合分析 (數據保存在 _source 中);
  • index – 是否構建倒排索引, 設置成 false, 就無法被搜索, 但還是支持聚合操作, 並會出現在 _source 中;
  • norms – 只⽤來過濾和聚合分析(指標數據)、不關心評分的欄位, 建議關閉, 節約存儲空間;
  • doc_values – 是否啟用 doc_values, 用於排序和聚合分析;
  • field_data – 如果要對 text 類型啟用排序和聚合分析, fielddata 需要設置成true;
  • coerce – 是否開啟數據類型的自動轉換 (如: 字元串轉數字), 預設開啟;
  • multifields - 是否開啟多欄位特性;
  • dynamic – 控制 mapping 的動態更新策略, 有 true / false / strict 三種.

doc_values 與 fielddata 比較:

doc_values: 聚合和排序的欄位需要開啟 —— 預設 為所有非text類型的欄位 開啟 —— 記憶體不夠時, 會寫入磁碟文件中;

fielddata: 是否為text類型開啟, 以實現排序和聚合分析 —— 預設關閉 —— 全部載入進記憶體中.

3.6 mapping 設置小結

(1) 支持加入新的欄位 (包括子欄位)、更換分詞器等操作:

可以通過 update_by_query 令舊數據得到清洗.

(2) Index Template: 根據索引的名稱匹配不同的 mappings 和 settings;

(3) Dynamic Template: 在一個 mapping 上動態設定欄位類型;

(4) Reindex: 如果要修改、刪除已經存在的欄位, 或者修改分片個數等參數, 就要重建索引.

必須停機, 數據量大時耗時會比較久.

可藉助 Index Alias (索引別名) 來實現零停機維護.

4 ES 數據建模最佳實踐

4.1 如何處理關聯關係

(1) 範式化設計:

我們知道, 在關係型資料庫中有“範式化設計”的概念, 有 1NF、2NF、3NF、BCNF 等等, 主要目標是減少不必要的更新, 雖然節省了存儲空間, 但缺點是數據讀取操作可能會更慢, 尤其是跨表操作, 需要 join 的表會很多.

反範式化設計: 數據扁平, 不使用關聯關係, 而是在文檔中通過 _source 欄位來保存冗餘的數據拷貝.

優點: 無需處理 join 操作, 數據讀取性能好;

缺點: 不適合數據頻繁修改的場景.

==》ES 不擅長處理關聯關係, 一般可以通過對象類型(object)、嵌套類型(nested)、父子關聯關係(child/parent)解決.

具體使用所占篇幅較大, 這裡省略.

4.2 避免太多的欄位

(1) 一個⽂檔中, 最好不要有⼤量的欄位:

  • 過多的欄位導致數據不容易維護;
  • mapping 信息保存在 Cluster State 中, 數據量過⼤, 對集群性能會有影響 (Cluster State 信息需要和所有的節點同步);
  • 刪除或修改欄位時, 需要 reindex;

(2) ES中單個索引最大欄位數預設是 1000, 可以通過參數 index.mapping.total_fields.limt 修改最⼤欄位數.

思考: 什麼原因會導致文檔中有成百上千的欄位?

ES 是無模式 (schemaless) 的, 預設情況下, 每添加一個欄位, ES 都會根據該欄位可能的類型自動添加映射關係.

如果業務處理不嚴謹, 會出現欄位爆炸的現象. 為了避免這種現象的發生, 需要制定 dynamic 策略:

  • true - 未知欄位會被自動加入, 是預設設置;
  • false - 新欄位不會被索引, 但是會保存到 _source 中;
  • strict - 新增欄位不會被索引, ⽂檔寫入失敗, 拋出異常.

—— 生產環境中, 儘量不要使用預設的 "dynamic": true .

4.3 避免正則查詢

正則、首碼、通配符查詢, 都屬於 Term 查詢, 但是性能很不好(掃描所有文檔, 並逐一比對), 特別是將通配符放在開頭, 會導致性能災難.

(1) 案例:

  • 文檔中某個欄位包含了 Elasticsearch 的版本信息, 例如 version: "7.2.0" ;
  • 搜索某系列的 bug_fix 版本(末位非0的版本號)? 每個主要版本號所關聯的文檔?

(2) 通配符查詢示例:

# 插入2條數據:
PUT softwares/_doc/1
{
  "version": "7.2.0",
  "doc_url": "https://www.elastic.co/guide/en/elasticsearch/.../.html"
}

PUT softwares/_doc/2
{
  "version": "7.3.0",
  "doc_url": "https://www.elastic.co/guide/en/elasticsearch/.../.html"
}

# 通配符查詢:
GET softwares/_search
{
  "query": {
    "wildcard": {
      "version": "7*"
    }
  }
}

(3) 解決方案 - 將字元串類型轉換為對象類型:

# 創建對象類型的映射:
PUT softwares
{
  "mappings": {
    "properties": {
      "version": {      # 版本號設置為對象類型
        "properties": {
          "display_name": { "type": "keyword" },
          "major": { "type": "byte" },
          "minor": { "type": "byte" },
          "bug_fix": { "type": "byte" }
        }
      },
      "doc_url": { "type": "text" }
    }
  }
}

# 添加數據:
PUT softwares/_doc/1
{
  "version": {
    "display_name": "7.2.0",
    "major": 7,
    "minor": 2,
    "bug_fix": 0
  },
  "doc_url": "https://www.elastic.co/guide/en/elasticsearch/.../.html"
}

PUT softwares/_doc/2
{
  "version": {
    "display_name": "7.3.0",
    "major": 7,
    "minor": 3,
    "bug_fix": 0
  },
  "doc_url": "https://www.elastic.co/guide/en/elasticsearch/.../.html"
}

# 通過filter過濾, 避免正則查詢, 大大提升性能:
GET softwares/_search
{
  "query": {
    "bool": {
      "filter": [
        {
          "match": { "version.major": 7 }
        },
        {
          "match": { "version.minor": 2 }
        }
      ]
    }
  }
}

4.4 避免空值引起的聚合不准

(1) 示例:

# 添加數據, 包含1條 null 值的數據:
PUT ratings/_doc/1
{
  "rating": 5
}
PUT ratings/_doc/2
{
  "rating": null
}

# 對含有 null 值的欄位進行聚合:
GET ratings/_search
{
  "size": 0,
  "aggs": {
    "avg_rating": {
      "avg": { "field": "rating"}
    }
  }
}

# 結果如下:
{
  "took" : 3,
  "timed_out" : false,
  "_shards" : {
    "total" : 1,
    "successful" : 1,
    "skipped" : 0,
    "failed" : 0
  },
  "hits" : {
    "total" : {
      "value" : 2,              # 2條數據, avg_rating 結果不正確
      "relation" : "eq"
    },
    "max_score" : null,
    "hits" : [ ]
  },
  "aggregations" : {
    "avg_rating" : {
      "value" : 5.0
    }
  }
}

(2) 使用 null_value 解決空值的問題:

# 創建 mapping 時, 設置 null_value:
PUT ratings
{
  "mappings": {
    "properties": {
      "rating": {
        "type": "float",
        "null_value": "1.0"
      }
    }
  }
}

# 添加相同的數據, 再次聚合, 結果正確:
{
  "took" : 0,
  "timed_out" : false,
  "_shards" : {
    "total" : 1,
    "successful" : 1,
    "skipped" : 0,
    "failed" : 0
  },
  "hits" : {
    "total" : {
      "value" : 2,
      "relation" : "eq"
    },
    "max_score" : null,
    "hits" : [ ]
  },
  "aggregations" : {
    "avg_rating" : {
      "value" : 3.0
    }
  }
}

參考資料

《極客時間》視頻課之《Elasticsearch核心技術與實戰》

版權聲明

作者: 馬瘦風(https://healchow.com)

出處: 博客園 馬瘦風的博客(https://www.cnblogs.com/shoufeng)

感謝閱讀, 如果文章有幫助或啟發到你, 點個[

您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 想使用 mongodb 官網提供的YUM安裝方法快速安裝,但官方暫時未提供centos8的安裝包,只需稍作修改則能順利安裝! ...
  • 原理:兩台web伺服器,通過心跳線進行通信,當主節點出現服務異常,備用節點通過探測判斷主節點是否存活,若是不存活,就把服務接管過來。 Web1和Web2中間有一根心跳線,檢查對方的存活狀態。流動IP:也叫vip是對外提供服務的ip,正常情況下是配置在Web1上的,當Web1宕機後,Web2會自動配置 ...
  • Linux 三劍客是(grep,sed,awk)三者的簡稱,熟練使用這三個工具可以提升運維效率。Linux 三劍客以正則表達式作為基礎,而在Linux系統中,支持兩種正則表達式,分別為“標準正則表達式”和“擴展正則表達式”。在掌握好正則表達式後,將具體講解三劍客的用法。 一、正則表達式 可以看到標準 ...
  • 大家好,我是痞子衡,是正經搞技術的痞子。今天痞子衡給大家介紹的是i.MX RT學習資源。 ...
  • 前面已經分析了linux記憶體管理演算法(伙伴管理演算法)的準備工作。 具體的演算法初始化則回到start_kernel()函數接著往下走,下一個函數是mm_init(): 乍看僅僅是幾個函數的調用,實際上這裡的事情遠遠沒這麼簡單。其中page_cgroup_init_flatmem()與cgroup相關, ...
  • 前面分析了memblock演算法、內核頁表的建立、記憶體管理框架的構建,這些都是x86處理的setup_arch()函數裡面初始化的,因地制宜,具有明顯處理器的特征。而start_kernel()接下來的初始化則是linux通用的記憶體管理演算法框架了。 build_all_zonelists()用來初始化 ...
  • 存儲類 存儲類(storage class)是kubernetes資源類型,它是由管理員為管理PV之便而按需創建的類別 存儲類好處是支持 PV 的動態創建,系統按PVC的需求標準動態創建適配的PV會為存儲管理帶來極大的靈活性。 PV的動態供給,其重點是在存儲類的定義,其分類大概是對存儲的性能進行分類 ...
  • 關係型資料庫-關係操作集合 1、 基本的關係操作 關係模型中常用的關係操作包括查詢(Query)操作和插入(Insert)、刪除 (Delete)、修改(Update)操作兩大部分。 查詢操作分為:選擇、投影、連接、除、並、差、交、笛卡爾積等; 五種基本操作:選擇、投影、並、差、笛卡爾積; 關係操作 ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...