乐趣区

Elasticsearch-数据建模的一些建议

如何处理关联关系

  • Kibana 目前暂时不支持 nested 类型和 parent/child 类型,在未来有可能会支持,在 Kibana 界面上展示数据的时候不是很友好
  • 如果需要使用 Kibana 进行数据分析,在建模时需要对嵌套和父子类型进行取舍

避免过多字段

  1. 过多字段不好维护
  2. Mapping 信息保存在 Cluster State 中,数据量过大,对集群性能有影响(Cluster State 信息需要和所有节点同步)
  3. 删除或者修改数据需要 reindex.
  4. 默认最大字段数是 1000,可以设置 index.mapping.total_fields.limt 限定最大字段数
  5. 生产环境尽量不要打开 Dynamic Dynamic Mapping
  • 考虑下这样一个场景,
## 索引数据,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"
 }
}

我们的 cookie 字段使用了 Dynamic=true 默认值,所以随着写入的数据越来越多,如果不对 cookies 字段的子字段进行限制的话,字段数会越来越多,会影响性能,

  • 解决方案
# 使用 Nested 对象,增加 key/value
PUT cookie_service
{
  "mappings": {
    "dynamic": "strict",
    "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
          }
        }
      }
    }
  }
}

需要说明几点的是:

  • 设置 "dynamic": "strict" 阻止其他字段加入
  • 通过 name 和 dateValue, keywordValue, IntValue 这样的设计可以存储任意的 key-value

避免正则查询

正则查询的性能不够好,前缀查询属于 Term 查询

  • 案例如下:

文档中某个字段包含了 Elasticsearch 的版本信息,例如 version: “7.1.0”, 现在我们需要查询朱版本是 7,次要版本是 2 的文档,不要使用正则查询

  • 解决方案如下: 我们使用 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"}
        }
      }
    }
  }
}

然后我们再使用查询

POST softwares/_search
{
  "query": {
    "bool": {
      "filter": [
        {
          "match":{"version.marjor":7}
        },
        {
          "match":{"version.minor":2}
        }

      ]
    }
  }
}

避免空值引起的聚合不准

  • 首先我们先看一下示例
PUT ratings/_doc/1
{"rating":5}
PUT ratings/_doc/2
{"rating":null}

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

# 查询结果
{
  "took" : 1,
  "timed_out" : false,
  "_shards" : {
    "total" : 1,
    "successful" : 1,
    "skipped" : 0,
    "failed" : 0
  },
  "hits" : {
    "total" : {
      "value" : 2,
      "relation" : "eq"
    },
    "max_score" : null,
    "hits" : []},
  "aggregations" : {
    "avg" : {"value" : 5.0}
  }
}

很明显,我们查到了两条数据,但是平均值是 5,这个很难以理解,

  • 解决方案,我们给 null 值设置一个默认值
PUT ratings
{
  "mappings": {
    "properties": {
      "rating": {
        "type": "float",
        "null_value": 0
      }
    }
  }
}

再次插入上面的数据,我们得到下面的结果

{
  "took" : 5,
  "timed_out" : false,
  "_shards" : {
    "total" : 1,
    "successful" : 1,
    "skipped" : 0,
    "failed" : 0
  },
  "hits" : {
    "total" : {
      "value" : 2,
      "relation" : "eq"
    },
    "max_score" : null,
    "hits" : []},
  "aggregations" : {
    "avg" : {"value" : 2.5}
  }
}

这样就比较对了,当然 null_value 的值是可以自己根据业务需求自己设定的

退出移动版