笔记四十七:Elasticsearch 数据建模佳实践
建模建议(一):如何处理关联关系
- Object : 优先考虑 Denormailzation
- Nested : 当数据包含多数值对象(对个演员),同时有查询需求
- 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
- 同时采用扁平化的设计,必然导致字段数量的膨胀
##索引数据,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 协议》,转载必须注明作者和本文链接