笔记四十七:Elasticsearch 数据建模佳实践

建模建议(一):如何处理关联关系

  1. Object : 优先考虑 Denormailzation
  2. Nested : 当数据包含多数值对象(对个演员),同时有查询需求
  3. Child/Parent: 关联文档更新非常频繁时

Kibana

  • Kibana 目前暂不支持 nested 类型 和 parent / child 类型,在未来有可能会支持
  • 如果需要使用 Kibana 进行数据分析,在数据建模时仍需要对嵌套和父子关联类型作出取舍

建模建议(二):避免过多字段

  • 一个文档中,最好避免大量的字段
    • 过多的字段数不容易维护
    • Mapping 信息保存在 Cluster State 中, 数据量过大,对集群性能会有影响(Cluster State 信息需要和所有的节点同步)
    • 删除或者修改数据需要 reindex
  • 默认最大字段数是1000,可以设置 index.mapping.total_fields.limit 限制最大的字段数
  • 什么原因会导致文档中会有成百上千的字段?

Dynamic v.s Strict

  • 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
      }
    }
  }
}

建模建议(五):为索引的Mapping 加入 Meta 的信息

  • Mappings 设置非常重要,需要从两个维度进行考虑
    • 功能:索引,聚合,排序
    • 性能:存储的开销,内存的开销,搜索的性能
  • Mappings 设置是一个迭代的过程
    • 加入新的字段容易(必要时需要 update_by_query)
    • 更新删除字段不允许(需要Reindex 重建数据)
    • 最好能对 Mappings 加入 Meta 信息,更好的进行版本管理
    • 可以考虑 Mapping 文件上传 git 进行管理
PUT softwares/
{
  "mappings": {
    "_meta": {
      "software_version_mapping": "1.0"
    }
  }
}
本作品采用《CC 协议》,转载必须注明作者和本文链接
快乐就是解决一个又一个的问题!
CrazyZard
讨论数量: 0
(= ̄ω ̄=)··· 暂无内容!

讨论应以学习和精进为目的。请勿发布不友善或者负能量的内容,与人为善,比聪明更重要!