ES 筆記四十七:Elasticsearch 資料建模佳實踐

CrazyZard發表於2020-01-28
  1. Object : 優先考慮 Denormailzation
  2. Nested : 當資料包含多數值物件(對個演員),同時有查詢需求
  3. Child/Parent: 關聯文件更新非常頻繁時
  • Kibana 目前暫不支援 nested 型別 和 parent / child 型別,在未來有可能會支援
  • 如果需要使用 Kibana 進行資料分析,在資料建模時仍需要對巢狀和父子關聯型別作出取捨
  • 一個文件中,最好避免大量的欄位
    • 過多的欄位數不容易維護
    • Mapping 資訊儲存在 Cluster State 中, 資料量過大,對叢集效能會有影響(Cluster State 資訊需要和所有的節點同步)
    • 刪除或者修改資料需要 reindex
  • 預設最大欄位數是1000,可以設定 index.mapping.total_fields.limit 限制最大的欄位數
  • 什麼原因會導致文件中會有成百上千的欄位?
  • Dynamic (生產環境中,儘量不要開啟 Dynamic)
    • true - 未知欄位會被自動加入
    • false - 新欄位不會被索引,但是會儲存在 _source
    • strict - 新增欄位不會被索引,文件寫入失敗
  • Strict
    • 可以控制到欄位級別

一個例子: Cookie Service 的資料

  • 來自 Cookie Service 的資料
    • Cookie 的鍵值對很多
    • 當 Dynamic 設定為 True
    • 同時採用扁平化的設計,必然導致欄位數量的膨脹

ES筆記四十七:Elasticsearch 資料建模佳實踐

##索引資料,dynamic mapping 會不斷加入新增欄位
PUT cookie_service/_doc/1
{
 "url":"www.google.com",
 "cookies":{
   "username":"tom",
   "age":32
 }
}

PUT cookie_service/_doc/2
{
 "url":"www.amazon.com",
 "cookies":{
   "login":"2019-01-01",
   "email":"xyz@abc.com"
 }
}

GET cookie_service/_mapping

解決方案: Nested Object & Key Value

PUT cookie_service
{
  "mappings": {
    "properties": {
      "cookies": {
        "type": "nested",
        "properties": {
          "name": {
            "type": "keyword"
          },
          "dateValue": {
            "type": "date"
          },
          "keywordValue": {
            "type": "keyword"
          },
          "IntValue": {
            "type": "integer"
          }
        }
      },
      "url": {
        "type": "text",
        "fields": {
          "keyword": {
            "type": "keyword",
            "ignore_above": 256
          }
        }
      }
    }
  }
}

寫入 & 查詢

PUT cookie_service/_doc/1
{
 "url":"www.google.com",
 "cookies":[
    {
      "name":"username",
      "keywordValue":"tom"
    },
    {
       "name":"age",
      "intValue":32

    }

   ]
 }

PUT cookie_service/_doc/2
{
 "url":"www.amazon.com",
 "cookies":[
    {
      "name":"login",
      "dateValue":"2019-01-01"
    },
    {
       "name":"email",
      "IntValue":32
    }
  ]
}

# Nested 查詢,通過bool查詢進行過濾
POST cookie_service/_search
{
  "query": {
    "nested": {
      "path": "cookies",
      "query": {
        "bool": {
          "filter": [
            {
            "term": {
              "cookies.name": "age"
            }},
            {
              "range":{
                "cookies.intValue":{
                  "gte":30
                }
              }
            }
          ]
        }
      }
    }
  }
}

通過 Nested 物件儲存 Key / Value 的一些不足

  • 可以減少欄位數量,解決 Cluster State 中 儲存過多 Meta 資訊的問題,但是
    • 導致查詢語句複雜度增加
    • Nested 物件 ,不利於在 Kibana 彙總實現視覺化分析
  • 問題:
    • 正則,萬用字元查詢,字首查詢屬於 Term 查詢,但是效能不夠好
    • 特別是將萬用字元放在開頭,會導致效能的災難
  • 案例:
    • 文件中某個欄位包含了 ES 的版本資訊,例如 version:“7.1.0”
    • 搜尋所有是 bug fix 的版本?每個主要版本號所關聯的文件?
GET softwares/_mapping
PUT softwares/_doc/1
{
  "software_version":"7.1.0"
}

解決方案:將字串轉換為物件

# 優化,使用inner object
PUT softwares/
{
  "mappings": {
    "_meta": {
      "software_version_mapping": "1.1"
    },
    "properties": {
      "version": {
        "properties": {
          "display_name": {
            "type": "keyword"
          },
          "hot_fix": {
            "type": "byte"
          },
          "marjor": {
            "type": "byte"
          },
          "minor": {
            "type": "byte"
          }
        }
      }
    }
  }
}

#通過 Inner Object 寫入多個文件
PUT softwares/_doc/1
{
  "version": {
    "display_name": "7.1.0",
    "marjor": 7,
    "minor": 1,
    "hot_fix": 0
  }
}

PUT softwares/_doc/2
{
  "version": {
    "display_name": "7.2.0",
    "marjor": 7,
    "minor": 2,
    "hot_fix": 0
  }
}

PUT softwares/_doc/3
{
  "version": {
    "display_name": "7.2.1",
    "marjor": 7,
    "minor": 2,
    "hot_fix": 1
  }
}

搜尋過濾

# 通過 bool 查詢,
POST softwares/_search
{
  "query": {
    "bool": {
      "filter": [
        {
          "match": {
            "version.marjor": 7
          }
        },
        {
          "match": {
            "version.minor": 2
          }
        }
      ]
    }
  }
}
PUT ratings
{
  "mappings": {
      "properties": {
        "rating": {
          "type": "float",
          "null_value": 1.0
        }
      }
    }
}

PUT ratings/_doc/1
{
  "rating": 5
}
PUT ratings/_doc/2
{
  "rating": null
}

使用 Null_Value 解決空值的問題


POST ratings/_search
{
  "size": 0,
  "aggs": {
    "avg": {
      "avg": {
        "field": "rating"
      }
    }
  }
}

POST ratings/_search
{
  "query": {
    "term": {
      "rating": {
        "value": 1
      }
    }
  }
}
  • Mappings 設定非常重要,需要從兩個維度進行考慮
    • 功能:索引,聚合,排序
    • 效能:儲存的開銷,記憶體的開銷,搜尋的效能
  • Mappings 設定是一個迭代的過程
    • 加入新的欄位容易(必要時需要 update_by_query)
    • 更新刪除欄位不允許(需要Reindex 重建資料)
    • 最好能對 Mappings 加入 Meta 資訊,更好的進行版本管理
    • 可以考慮 Mapping 檔案上傳 git 進行管理
PUT softwares/
{
  "mappings": {
    "_meta": {
      "software_version_mapping": "1.0"
    }
  }
}
本作品採用《CC 協議》,轉載必須註明作者和本文連結

快樂就是解決一個又一個的問題!

相關文章