hi,我是蛋挞,一个初出茅庐的后端开发,希望可以和大家共同努力、共同进步!
开启掘金成长之旅!这是我参与「掘金日新计划 · 4 月更文挑战」的第 7 天,点击查看活动详情
- 起始标记->数据建模(7讲):「54 | Elasticsearch数据建模最佳实践」
- 结尾标记->数据建模(7讲):「55 | 第二部分总结回顾」
Elasticsearch数据建模最佳实践
建模建议 (一)如何处理关联关系
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 的版本? 每个主要版本号所关联的文档?
解决方案:将字符串转换为对象
建模建议(四):避免空值引起的聚合不准
建模建议 (五):为索引的 Mapping 加入 Meta 信息
- Mappings 设置非常重要,需要从两个维度进行考虑
- 功能:搜索,聚合,排序
- 性能:存储的开销;内存的开销;搜索的性能
- Mappings 设置是一个迭代的过程
- 加入新的字段很容易 (必要时需要 update_by_query)1
- 更新删除字段不允许(需要 Reindex 重建数据)
- 最好能对 Mappings 加入 Meta 信息,更好的进行版本管理
- 可以考虑将 Mapping 文件上传 git 进行管理
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 中设置合适的字段
测试
- 判断题: 生产环境中,对索引使用 index Alias 是一个好的实践
- √ 在生产环境中要善用index Alias,当我们使用index Alias的时候在对数据做Reindx也不会对生产环境产生任何的影响。
- 在 Terms 聚合分析中,有哪些方法可以提高查询的精准度
- 两种方法。第一种方法是当我们的数据量不是特别大的时候,需要把主分片的数字设置成一 ,这个时候就不会出现任何聚合分析进准度的问题了,当数据量变大的时候,不得不把数据分散在多个分片上的时候,这也在做term查询的时候设定分片的参数来提高聚合分析的精准度。
- 如何通过聚合分析知道,每天网站中的访客来自多少不同的 IP
- 可以通过使用通过Cardinality聚合器获得每天网站中不同IP地址的总数。
- 请描述“multi_match”查询中“best_field”的行为4.
- 当使用这种方式进行查询的时候ES会返回单字符串在多个字符串中的一个算分结果
- 对搜索结果分页时,所采用的两个参数
- 可以使用form 和 size
- 判断题: 使用 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 核心技术与实战》