ES101系列08 | 数据建模和索引重建

本篇文章主要讲解 ElasticSearch 中的数据建模内容,包括如何处理关联关系、索引重建和字段建模最佳实践等。

在 ElasticSearch 中处理关联关系

关系型数据库,一般会考虑 Normalize 数据;在 Elasticsearch,往往考虑 Denormalize 数据。

Denormalize 的好处:读的速度变快/无需表连接/无需行锁

Elasticsearch 并不擅长处理关联关系。我们一般采用以下四种方法处理关联:

  • 对象类型
  • 嵌套对象 (Nested Object)
  • 父子关联关系 (Parent/Child)
  • 应用端关联
特性 对象类型 嵌套对象 父子关联 应用端关联
本质 默认处理 JSON 对象的方式 特殊的对象类型 同一索引内文档间的逻辑链接 由应用程序维护关联
存储结构 对象属性扁平化存储到父文档 作为父文档内部的独立隐藏文档 父子文档独立存储在同一分片 关联数据存储在不同文档或外部系统
数据边界 无边界,对象属性合并到父文档 有边界,每个嵌套对象独立存储 父子文档完全独立 完全独立,无 ES 内部关联
查询特点 可能匹配不同对象的字段组合 确保条件匹配同一嵌套对象内部 需用 has_childhas_parent 等查询 需多次查询,先查主文档再查关联文档
更新特点 更新需重索引整个父文档 更新需重索引整个父文档 可单独更新子文档,不影响父文档 每个文档可单独更新
适用场景 简单键值对对象,无需独立查询 对象数组需独立查询和匹配 子文档频繁更新或数量大 关系简单、查询量小或数据分散
缺点 无法精确匹配数组内对象组合 更新成本高,嵌套数量有限制 查询性能低,内存消耗大 网络开销大,应用逻辑复杂

Nested Data Type

  • Nested 数据类型:允许对象数组中的对象被独立索引。
  • 使用 nested 和 properties 关键字,将所有数组索引到多个分隔的文档。
  • 在内部,Nested 文档会被保存在两个 Lucene 文档中,在查询时做 Join 处理。

示例

json 复制代码
PUT my_movies
{
    "mappings": {
        "properties": {
            "actors": {
                "type": "nested",
                "properties": {
                    "first_name": {
                        "type": "keyword"
                    },
                    "last_name": {
                        "type": "keyword"
                    }
                }
            },
            "title": {
                "type": "text",
                "fields": {
                    "keyword": {
                        "type": "keyword",
                        "ignore_above": 256
                    }
                }
            }
        }
    }
}

若没有指定 "type": "nested" 时,数组数据会扁平化存储,搜索会出现不该出现的内容。

Parent / Child

对象和 Nested 对象每次更新都需要重新索引整个对象。

ES 提供了类似关系型数据库中 Join 的实现。可通过维护 Parent / Child 的关系,从而分离两个对象。

  • 父文档和子文档是两个独立的文档。
  • 更新父文档无需重新索引子文档。子文档被添加,更新或者删除也不会影响到父文档和其他的子文档。

示例

1、添加数据

json 复制代码
PUT my_blogs
{
  "settings": {
    "number_of_shards": 2
  },
  "mappings": {
    "properties": {
      "blog_comments_relation": {
        "type": "join",
        "relations": {
          "blog": "comment"
        }
      },
      "content": {
        "type": "text"
      },
      "title": {
        "type": "keyword"
      }
    }
  }
}

// 索引父文档
PUT my_blogs/_doc/blog2
{
  "title":"Learning Hadoop",
  "content":"learning Hadoop",
    "blog_comments_relation":{
    "name":"blog"
  }
}


// 索引子文档
PUT my_blogs/_doc/comment1?routing=blog1
{
  "comment":"I am learning ELK",
  "username":"Jack",
  "blog_comments_relation":{
    "name":"comment",
    "parent":"blog1"
  }
}

// 索引子文档
PUT my_blogs/_doc/comment2?routing=blog2
{
  "comment":"I like Hadoop!!!!!",
  "username":"Jack",
  "blog_comments_relation":{
    "name":"comment",
    "parent":"blog2"
  }
}

// 索引子文档
PUT my_blogs/_doc/comment3?routing=blog2
{
  "comment":"Hello Hadoop",
  "username":"Bob",
  "blog_comments_relation":{
    "name":"comment",
    "parent":"blog2"
  }
}

2、父子关系查询

json 复制代码
// 根据父文档ID查看
GET my_blogs/_doc/blog2

// 根据 Parent Id 查询
POST my_blogs/_search
{
  "query": {
    "parent_id": {
      "type": "comment",
      "id": "blog2"
    }
  }
}

// has_child 查询,返回父文档
POST my_blogs/_search
{
  "query": {
    "has_child": {
      "type": "comment",
      "query" : {
            "match": {
                "username" : "Jack"
            }
        }
    }
  }
}

// has_parent 查询,返回相关的子文档
POST my_blogs/_search
{
  "query": {
    "has_parent": {
      "parent_type": "blog",
      "query" : {
			"match": {
				"title" : "Learning Hadoop"
			}
		}
    }
  }
}

索引重建

需要索引重建的场景如下:

  • 索引的 Mappings 发生变更:字段类型更改,分词器及字典更新。
  • 索引的 Settings 发生变更:索引的主分片数发生改变。
  • 集群内,集群间需要做数据迁移。

索引重建的方式:

  • Update By Query:在现有索引上重建。
  • Reindex:在其他索引上重建索引。
特性 Update By Query Reindex
核心操作 原地更新现有索引文档 复制数据到新索引
操作对象 单个索引 源索引和目标索引
数据迁移 不支持 支持
索引结构 不可更改 Mapping/Settings/分片 可更改 Mapping/Settings/分片
主要用途 批量修改文档内容或删除文档 重建索引结构/迁移数据
性能影响 对原索引读写压力大 源索引读压力,目标索引写压力
原子性 非原子操作,失败需手动处理 目标索引独立,失败可重试
适用场景 字段值更新、文档删除 更改字段类型、分片数或跨集群迁移等

字段建模

flowchart LR 字段类型["字段类型"] --> 是否要搜索及分词["是否要搜索及分词"] 是否要搜索及分词 --> a1["是否要聚合及排序"] a1 --> 是否要额外的存储

Mapping 字段的相关设置:

  • Enabled ------ 设置成 false,仅做存储,不支持搜索和聚合分析(数据保存在 _source 中)。
  • Index ------ 是否构建倒排索引。设置成 false,无法被搜索,但还是支持 aggregation,并出现在 _source 中。
  • Norms ------ 如果字段用来过滤和聚合分析,可以关闭,节约存储。
  • Doc_values ------ 是否启用 doc_values,用于排序和聚合分析。
  • Field_data ------ 如果要对 text 类型启用排序和聚合分析,fielddata 需要设置成 true。
  • Store ------ 默认不存储,数据默认存储在 _source。
  • Coerce ------ 默认开启,是否开启数据类型的自动转换(例如,字符串转数字)。
  • Multifields 多字段特性。
  • Dynamic ------ true/false/strict 控制 Mapping 的自动更新。

写在最后

这是该系列的第八篇,主要讲解 ElasticSearch 中的数据建模内容,包括如何处理关联关系、索引重建和字段建模最佳实践。可以自己去到 Kibana 的 Dev Tool 实战操作,未来会持续更新该系列,欢迎关注👏🏻。

同时欢迎关注公众号:LanTech指南。不定时分享职场思考、独立开发日志、大厂方法论和后端经验❤️

参考

  1. github.com/onebirdrock...
  2. www.elastic.co/elasticsear...
相关推荐
王者鳜錸19 分钟前
Vue3+SpringBoot全栈开发:从零实现增删改查与分页功能
vue.js·spring boot·后端
无妄-202420 分钟前
“刹车思维”:慢,是为了更快
java·经验分享
玉~你还好吗22 分钟前
【FreeRTOS#1】多任务处理&任务调度器&任务状态
java·开发语言
Yusei_052323 分钟前
C++ 模版复习
android·java·c++
ytttr87324 分钟前
k8s的出现解决了java并发编程胡问题了
java·容器·kubernetes
red润29 分钟前
奇怪?为什么 floor((n + t - 1) / t) 比 ceil(n / t) 更高效?(因为没有浮点转换带来的性能损耗)
前端·后端·算法
WindSearcher32 分钟前
关于ReAct Agent的实践
人工智能·后端
前端付豪32 分钟前
连夜睡服低管,后端回滚没你想的简单:网易如何用一套体系保障“无痛撤退”?
前端·后端·架构
纪元A梦44 分钟前
分布式拜占庭容错算法——权益证明(PoS)算法详解
java·分布式·算法
老友@1 小时前
Spring Boot 应用中实现配置文件敏感信息加密解密方案
java·spring boot·后端·数据安全·加密·配置文件