Elasticsearch 学习笔记Day 23

123 阅读7分钟

hi,我是蛋挞,一个初出茅庐的后端开发,希望可以和大家共同努力、共同进步!


开启掘金成长之旅!这是我参与「掘金日新计划 · 4 月更文挑战」的第 7 天,点击查看活动详情

  • 起始标记->数据建模(7讲):「54 | Elasticsearch数据建模最佳实践」
  • 结尾标记->数据建模(7讲):「55 | 第二部分总结回顾」

Elasticsearch数据建模最佳实践

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

image.png

Kibana......

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

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

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

Dynamic v.s Strict

  • Dynamic (生产环境中,尽量不要打开 Dynamic )
    • true - 未知字段会被自动加入
    • false - 新字段不会被索引。但是会保存在_source
    • strict - 新增字段不会被索引,文档写入失败
  • Strict
    • 可以控制到字段级别

Cookie Service 的数据

  • 来自 Cookie Service 的数据
    • Cookie 的键值对很多
    • 当 Dynamic 设置为 True
    • 同时采用扁平化的设计,必然导致字段数量的膨胀

解决方案: Nested Obiect & Key Value

使用 Nested 对象

通过 Nested 对象保存 Key/Value 的一些不足

  • 可以减少字段数量,解决 Cluster State 中保存过多 Meta 信息的问题,但是
    • 导致查询语句复杂度增加
    • Nested 对象,不利于在 Kibana 中实现可视化分析

建模建议(三):避免正则查询

  • 问题:
    • 正则,通配符查询,前缀查询属于 Term 查询,但是性能不够好
    • 特别是将通配符放在开头,会导致性能的灾难
  • 案例:
    • 文档中某个字段包含了 Elasticsearch 的版本信息,例如 version:“7.1.0”
    • 搜索所有是 bug fix 的版本? 每个主要版本号所关联的文档?

解决方案:将字符串转换为对象

建模建议(四):避免空值引起的聚合不准image.png

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

  • Mappings 设置非常重要,需要从两个维度进行考虑
    • 功能:搜索,聚合,排序
    • 性能:存储的开销;内存的开销;搜索的性能
  • Mappings 设置是一个迭代的过程
    • 加入新的字段很容易 (必要时需要 update_by_query)1
    • 更新删除字段不允许(需要 Reindex 重建数据)
    • 最好能对 Mappings 加入 Meta 信息,更好的进行版本管理
    • 可以考虑将 Mapping 文件上传 git 进行管理

image.png

CodeDemo


###### Cookie Service

##索引数据,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"
 }
}


DELETE cookie_service
#使用 Nested 对象,增加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
          }
        }
      }
    }
  }
}


##写入数据,使用key和合适类型的value字段
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
                }
              }
            }
          ]
        }
      }
    }
  }
}



# 在Mapping中加入元信息,便于管理
PUT softwares/
{
  "mappings": {
    "_meta": {
      "software_version_mapping": "1.0"
    }
  }
}

GET softwares/_mapping
PUT softwares/_doc/1
{
  "software_version":"7.1.0"
}

DELETE softwares
# 优化,使用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
          }
        }

      ]
    }
  }
}




# Not Null 解决聚合的问题
DELETE ratings
PUT ratings
{
  "mappings": {
      "properties": {
        "rating": {
          "type": "float",
          "null_value": 1.0
        }
      }
    }
}


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


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

POST ratings/_search
{
  "query": {
    "term": {
      "rating": {
        "value": 1
      }
    }
  }
}

相关阅读

本节知识总结

分享了ES建模的最佳实现,通过实例理解如何对建模进行改进提升ES搜索的性能,同时对模型进行改进,提升聚合搜索相关度和准确度,并且对ReindexMapping文件加入mata信息加入Github进行版本管理。

第二部分总结回顾

回顾总结: 搜索与算分

  • 结构化搜索与非结构化搜索
    • Term 查询和基于全文本Match 搜索的区别
    • 对于需要做精确匹配的字段,需要做聚合分析的字段,字段类型设置为 Keyword
  • Query Context v.s Filter Context
    • Filter Context 可以避免算分,并且利用缓存o
    • Bool 查询中 Filter 和 Must Not 都属于 Filter Context

回顾总结: 搜索与算分

  • 搜索的算分
    • TF-IDF/字段 Boosting
  • 单字符串多字段查询: multi-match
    • Best_Field /Most_Fields / Cross Field
  • 提高搜索的相关性
    • 多语言: 设置子字段和不同的分词器提升搜索的效果
    • Search Template 分离代码逻辑和搜索 DSL

回顾总结: 聚合/分页

  • 聚合
    • Bucket / Metric/Pipeline
  • 分页
    • From & Size / Search After / Scroll AP
    • 要避免深度分页,对于数据导出等操作,可以使用 Scroll API

回顾总结:Elasticsearch 的分布式模型

  • 文档的分布式存储
    • 文档通过 hash 算法,route 并存储到相应的分片
  • 分片及其内部的工作机制
    • Segment/ Transaction Log / Refresh / Merge
  • 分布式查询和聚合分析的内部机制
    • Query Then Fetch;IDF 不是基于全局,而是基于分片计算,因此,数据量少的时候,算分不准

回顾总结:数据建模及重要性

  • 数据建模
    • ES如何处理管理关系/数据建模的常见步聚/建模的最佳实践o
  • 建模相关的工具
    • Index Template / Dynamic Template / ingest Node / Update By Query / Reindex / index Alias
  • 最佳实践
    • 避免过多的字段 /避免 wildcard 查询 /在Mapping 中设置合适的字段

测试

  1. 判断题: 生产环境中,对索引使用 index Alias 是一个好的实践
    • √ 在生产环境中要善用index Alias,当我们使用index Alias的时候在对数据做Reindx也不会对生产环境产生任何的影响。
  2. 在 Terms 聚合分析中,有哪些方法可以提高查询的精准度
    • 两种方法。第一种方法是当我们的数据量不是特别大的时候,需要把主分片的数字设置成一 ,这个时候就不会出现任何聚合分析进准度的问题了,当数据量变大的时候,不得不把数据分散在多个分片上的时候,这也在做term查询的时候设定分片的参数来提高聚合分析的精准度。
  3. 如何通过聚合分析知道,每天网站中的访客来自多少不同的 IP
    • 可以通过使用通过Cardinality聚合器获得每天网站中不同IP地址的总数。
  4. 请描述“multi_match”查询中“best_field”的行为4.
    • 当使用这种方式进行查询的时候ES会返回单字符串在多个字符串中的一个算分结果
  5. 对搜索结果分页时,所采用的两个参数
    • 可以使用form 和 size
  6. 判断题: 使用 Scroll API 导出数据时,即使中途有新的数据写入,这些数据也能被导出
    • × Scroll API其实是对搜索结果做了一个拍照,一旦拍照产生,即便有新的数据产生,也不会印象拍照时的数据 。

CodeDemo

DELETE test
PUT test/_doc/1
{
  "content":"Hello World"
}

POST test/_search
{
  "profile": "true",
  "query": {
    "match": {
      "content": "Hello World"
    }
  }
}

POST test/_search
{
  "profile": "true",
  "query": {
    "match": {
      "content": "hello world"
    }
  }
}

POST test/_search
{
  "profile": "true",
  "query": {
    "match": {
      "content.keyword": "Hello World"
    }
  }
}

POST test/_search
{
  "profile": "true",
  "query": {
    "match": {
      "content.keyword": "hello world"
    }
  }
}

POST test/_search
{
  "profile": "true",
  "query": {
    "term": {
      "content": "Hello World"
    }
  }
}

POST test/_search
{
  "profile": "true",
  "query": {
    "term": {
      "content": "hello world"
    }
  }
}

POST test/_search
{
  "profile": "true",
  "query": {
    "term": {
      "content.keyword": "Hello World"
    }
  }
}

本节知识总结


此文章为4月Day7学习笔记,内容来源于极客时间《Elasticsearch 核心技术与实战》