ES 32 - Elasticsearch 数据建模的探索与实践
1 什么是数据建模?
数据建模(Data modeling), 是创建数据模型的过程.
数据模型是对真实世界进行抽象描述的一种工具和方法, 实现对现实世界的映射. 比如影视作品、演员、观众评论...
数据建模有三个过程: 概念模型 => 逻辑模型 => 数据模型(第三范式)
数据模型, 需要结合使用的数据库类型, 在满足业务读写性能等需求的前提下, 制定出最终的定义.
2 如何对 ES 中的数据进行建模
ES中的数据建模:
由数据存储、检索等功能需求提炼出实体属性、实体之间的关系 =》形成逻辑模型;
由性能需求提炼制定索引模板、索引Mapping(包括字段的配置、关系的处理) ==》形成物理模型.
ES 中存储、检索的基本单位是索引文档(document), 文档由字段(field)组成, 所以ES的建模就是对字段进行建模.
文档类似于关系型数据库中的一行数据, 字段对应关系型数据库中的某一列数据.
2.1 字段类型的建模方案
(1) text 与 keyword 比较:
text: 用于全文本字段, 文本会被 Analyzer 分词; 默认不支持聚合分析及排序, 设置
"fielddata": true即可支持;keyword: 用于 id、枚举及不需要分词的文本, 比如身份证号码、电话号码,Email地址等; 适用于 Filter(精确匹配过滤)、Sorting(排序) 和 Aggregations(聚合).
设置多字段类型:
默认会为文本类型设置成 text, 并设置一个 keyword 的子字段;
在处理人类自然语⾔时, 可以添加“英⽂”、“拼⾳”、“标准”等分词器, 提高搜索结果的正确性.
(2) 结构化数据:
数值类型: 尽量选择贴近的类型, 例如可以用 byte, 就不要用 long;
枚举类型: 设置为 keyword, 即使是数字, 也应该设置成 keyword, 获取更好的性能; 另外范围检索使用keyword, 速度更快;
其他类型: 日期、二进制、布尔、地理信息等类型.
2.2 检索、聚合及排序的建模方案
如不需要检索、排序和聚合分析, 则可设置
"enable": false;如不需要检索, 则可设置
"index": false;如不需要排序、聚合分析功能, 则可设置
"doc_values": false/"fielddate": false;更新频繁、聚合查询频繁的 keyword 类型的字段, 推荐设置
"eager_global_ordinals": true.
2.3 额外存储的建模方案
- 是否需要专门存储当前字段数据?
"store": true, 可以存储该字段的原始内容;一般结合
"_source": { "enabled": false }进行使用, 因为默认的"_source": { "enabled": true }, 也就是添加索引时文档的原始 JSON 结构都会存储到_source中.
- disable_source: 禁用
_source元字段, 能节约磁盘, 适用于指标型数据 —— 类似于标识字段、时间字段的数据, 不会更新、高亮查询, 多用来进行过滤操作以快速筛选出更小的结果集, 用来支撑更快的聚合操作.
官方建议: 如果更多关注磁盘空间, 那么建议优先考虑增加数据的压缩⽐, 而不是禁用
_source;无法看到
_source字段, 就不能做reindex、update、update_by_query操作;目前为止, Kibana 中无法对禁用了
_source字段的索引进行 Discover 挖掘操作.—— 谨慎禁用
_source字段, 参考: https://www.elastic.co/guide/en/elasticsearch/reference/current/mapping-source-field.html
3 ES 数据建模实例演示
3.1 动态创建映射关系
# 直接写入一本图书信息:
POST books/_doc
{
"title": "Thinking in Elasticsearch 7.2.0",
"author": "Heal Chow",
"publish_date": "2019-10-01",
"description": "Master the searching, indexing, and aggregation features in Elasticsearch.",
"cover_url": "https://healchow.com/images/29dMkliO2a1f.jpg"
}
# 查看自动创建的mapping关系:
GET books/_mapping
# 内容如下:
{
"books" : {
"mappings" : {
"properties" : {
"author" : {
"type" : "text",
"fields" : {
"keyword" : {
"type" : "keyword",
"ignore_above" : 256
}
}
},
"cover_url" : {
"type" : "text",
"fields" : {
"keyword" : {
"type" : "keyword",
"ignore_above" : 256
}
}
},
"description" : {
"type" : "text",
"fields" : {
"keyword" : {
"type" : "keyword",
"ignore_above" : 256
}
}
},
"publish_date" : {
"type" : "date"
},
"title" : {
"type" : "text",
"fields" : {
"keyword" : {
"type" : "keyword",
"ignore_above" : 256
}
}
}
}
}
}
}
3.2 手动创建映射关系
# 删除自动创建的图书索引:
DELETE books
# 手动优化字段的mapping:
PUT books
{
"mappings": {
"_source": { "enabled": true },
"properties": {
"title": {
"type": "text",
"fields": {
"keyword": {
"type": "keyword",
"ignore_above": 100
}
}
},
"author": { "type": "keyword" },
"publish_date": {
"type": "date",
"format": "yyyy-MM-dd HH:mm:ss||yyyyMMddHHmmss||yyyy-MM-dd||epoch_millis"
},
"description": { "type": "text" },
"cover_url": { # index 设置成 false, 不支持搜索, 但支持 Terms 聚合
"type": "keyword",
"index": false
}
}
}
}
说明: _source 元字段默认是开启的, 若禁用后, 就无法对搜索的结果进行展示, 也无法进行 reindex、update、update_by_query 操作.
3.3 新增需求 - 添加大字段
需求描述: 添加图书内容字段, 要求支持全文搜索, 并且能够高亮显示.
需求分析: 新需求会导致
_source的内容过⼤, 虽然我们可以通过source filtering对要搜索结果中的字段进行过滤:"_source": {
"includes": ["title"] # 或 "excludes": ["xxx"] 排除某些字段, includes 优先级更高
}
但这种方式只是 ES 服务端传输给客户端时的过滤, 内部 Fetch 数据时, ES 各数据节点还是会传输
_source中的所有数据到协调节点 —— 网络 IO 没有得到本质上的降低.
3.4 解决大字段带来的性能问题
(1) 在创建 mapping 时手动关闭 _source 元字段: "_source": { "enabled": false} ;
(2) 然后为每个字段设置 "store": true .
# 关闭_source元字段, 设置store=true:
PUT books
{
"mappings": {
"_source": { "enabled": false },
"properties": {
"title": {
"type": "text",
"store": true,
"fields": {
"keyword": {
"type": "keyword",
"ignore_above": 100
}
}
},
"author": { "type": "keyword", "store": true },
"publish_date": {
"type": "date",
"store": true,
"format": "yyyy-MM-dd HH:mm:ss||yyyyMMddHHmmss||yyyy-MM-dd||epoch_millis"
},
"description": { "type": "text", "store": true },
"cover_url": {
"type": "keyword",
"index": false,
"store": true
},
"content": { "type": "text", "store": true }
}
}
}
(3) 加数据, 并进行高亮查询:
# 添加包含新字段的文档:
POST books/_doc
{
"title": "Thinking in Elasticsearch 7.2.0",
"author": "Heal Chow",
"publish_date": "2019-10-01",
"description": "Master the searching, indexing, and aggregation features in Elasticsearch.",
"cover_url": "https://healchow.com/images/29dMkliO2a1f.jpg",
"content": "1. Revisiting Elasticsearch and the Changes. 2. The Improved Query DSL. 3. Beyond Full Text Search. 4. Data Modeling and Analytics. 5. Improving the User Search Experience. 6. The Index Distribution Architecture. .........."
}
# 通过 stored_fields 指定要查询的字段:
GET books/_search
{
"stored_fields": ["title", "author", "publish_date"],
"query": {
"match": { "content": "data modeling" }
},
"highlight": {
"fields": { "content": {} }
}
}
查询结果如下:
{
"took" : 1,
"timed_out" : false,
"_shards" : {
"total" : 1,
"successful" : 1,
"skipped" : 0,
"failed" : 0
},
"hits" : {
"total" : {
"value" : 1,
"relation" : "eq"
},
"max_score" : 0.5753642,
"hits" : [
{
"_index" : "books",
"_type" : "_doc",
"_id" : "dukLoG0BdfGBNhbF13CJ",
"_score" : 0.5753642,
"highlight" : {
"content" : [
"<em>Data</em> <em>Modeling</em> and Analytics. 5. Improving the User Search Experience. 6."
]
}
}
]
}
}
(4) 结果说明:
- 返回结果中不包含
_source字段;- 对需要显示的信息, 要在查询中指定
"stored_fields": ["xxx", "yyy"];- 禁⽌
_source字段后, 仍然支持使用 Highlights API 的使用.
3.5 mapping中字段的常用参数
参考: https://www.elastic.co/guide/en/elasticsearch/reference/current/mapping-params.html
enabled– 设置成 false, 当前字段就只存储, 不支持搜索和聚合分析 (数据保存在 _source 中);index– 是否构建倒排索引, 设置成 false, 就无法被搜索, 但还是支持聚合操作, 并会出现在_source中;norms– 只⽤来过滤和聚合分析(指标数据)、不关心评分的字段, 建议关闭, 节约存储空间;doc_values– 是否启用 doc_values, 用于排序和聚合分析;field_data– 如果要对 text 类型启用排序和聚合分析, fielddata 需要设置成true;coerce– 是否开启数据类型的自动转换 (如: 字符串转数字), 默认开启;multifields- 是否开启多字段特性;dynamic– 控制 mapping 的动态更新策略, 有 true / false / strict 三种.
doc_values 与 fielddata 比较:
doc_values: 聚合和排序的字段需要开启 —— 默认 为所有非text类型的字段 开启 —— 内存不够时, 会写入磁盘文件中;
fielddata: 是否为text类型开启, 以实现排序和聚合分析 —— 默认关闭 —— 全部加载进内存中.
3.6 mapping 设置小结
(1) 支持加入新的字段 (包括子字段)、更换分词器等操作:
可以通过 update_by_query 令旧数据得到清洗.
(2) Index Template: 根据索引的名称匹配不同的 mappings 和 settings;
(3) Dynamic Template: 在一个 mapping 上动态设定字段类型;
(4) Reindex: 如果要修改、删除已经存在的字段, 或者修改分片个数等参数, 就要重建索引.
必须停机, 数据量大时耗时会比较久.
可借助 Index Alias (索引别名) 来实现零停机维护.
4 ES 数据建模最佳实践
4.1 如何处理关联关系
(1) 范式化设计:
我们知道, 在关系型数据库中有“范式化设计”的概念, 有 1NF、2NF、3NF、BCNF 等等, 主要目标是减少不必要的更新, 虽然节省了存储空间, 但缺点是数据读取操作可能会更慢, 尤其是跨表操作, 需要 join 的表会很多.
反范式化设计: 数据扁平, 不使用关联关系, 而是在文档中通过 _source 字段来保存冗余的数据拷贝.
优点: 无需处理 join 操作, 数据读取性能好;
缺点: 不适合数据频繁修改的场景.
==》ES 不擅长处理关联关系, 一般可以通过对象类型(object)、嵌套类型(nested)、父子关联关系(child/parent)解决.
具体使用所占篇幅较大, 这里省略.
4.2 避免太多的字段
(1) 一个⽂档中, 最好不要有⼤量的字段:
- 过多的字段导致数据不容易维护;
- mapping 信息保存在 Cluster State 中, 数据量过⼤, 对集群性能会有影响 (Cluster State 信息需要和所有的节点同步);
- 删除或修改字段时, 需要 reindex;
(2) ES中单个索引最大字段数默认是 1000, 可以通过参数 index.mapping.total_fields.limt 修改最⼤字段数.
思考: 什么原因会导致文档中有成百上千的字段?
ES 是无模式 (schemaless) 的, 默认情况下, 每添加一个字段, ES 都会根据该字段可能的类型自动添加映射关系.
如果业务处理不严谨, 会出现字段爆炸的现象. 为了避免这种现象的发生, 需要制定 dynamic 策略:
- true - 未知字段会被自动加入, 是默认设置;
- false - 新字段不会被索引, 但是会保存到
_source中;- strict - 新增字段不会被索引, ⽂档写入失败, 抛出异常.
—— 生产环境中, 尽量不要使用默认的
"dynamic": true.
4.3 避免正则查询
正则、前缀、通配符查询, 都属于 Term 查询, 但是性能很不好(扫描所有文档, 并逐一比对), 特别是将通配符放在开头, 会导致性能灾难.
(1) 案例:
- 文档中某个字段包含了 Elasticsearch 的版本信息, 例如
version: "7.2.0";- 搜索某系列的 bug_fix 版本(末位非0的版本号)? 每个主要版本号所关联的文档?
(2) 通配符查询示例:
# 插入2条数据:
PUT softwares/_doc/1
{
"version": "7.2.0",
"doc_url": "https://www.elastic.co/guide/en/elasticsearch/.../.html"
}
PUT softwares/_doc/2
{
"version": "7.3.0",
"doc_url": "https://www.elastic.co/guide/en/elasticsearch/.../.html"
}
# 通配符查询:
GET softwares/_search
{
"query": {
"wildcard": {
"version": "7*"
}
}
}
(3) 解决方案 - 将字符串类型转换为对象类型:
# 创建对象类型的映射:
PUT softwares
{
"mappings": {
"properties": {
"version": { # 版本号设置为对象类型
"properties": {
"display_name": { "type": "keyword" },
"major": { "type": "byte" },
"minor": { "type": "byte" },
"bug_fix": { "type": "byte" }
}
},
"doc_url": { "type": "text" }
}
}
}
# 添加数据:
PUT softwares/_doc/1
{
"version": {
"display_name": "7.2.0",
"major": 7,
"minor": 2,
"bug_fix": 0
},
"doc_url": "https://www.elastic.co/guide/en/elasticsearch/.../.html"
}
PUT softwares/_doc/2
{
"version": {
"display_name": "7.3.0",
"major": 7,
"minor": 3,
"bug_fix": 0
},
"doc_url": "https://www.elastic.co/guide/en/elasticsearch/.../.html"
}
# 通过filter过滤, 避免正则查询, 大大提升性能:
GET softwares/_search
{
"query": {
"bool": {
"filter": [
{
"match": { "version.major": 7 }
},
{
"match": { "version.minor": 2 }
}
]
}
}
}
4.4 避免空值引起的聚合不准
(1) 示例:
# 添加数据, 包含1条 null 值的数据:
PUT ratings/_doc/1
{
"rating": 5
}
PUT ratings/_doc/2
{
"rating": null
}
# 对含有 null 值的字段进行聚合:
GET ratings/_search
{
"size": 0,
"aggs": {
"avg_rating": {
"avg": { "field": "rating"}
}
}
}
# 结果如下:
{
"took" : 3,
"timed_out" : false,
"_shards" : {
"total" : 1,
"successful" : 1,
"skipped" : 0,
"failed" : 0
},
"hits" : {
"total" : {
"value" : 2, # 2条数据, avg_rating 结果不正确
"relation" : "eq"
},
"max_score" : null,
"hits" : [ ]
},
"aggregations" : {
"avg_rating" : {
"value" : 5.0
}
}
}
(2) 使用 null_value 解决空值的问题:
# 创建 mapping 时, 设置 null_value:
PUT ratings
{
"mappings": {
"properties": {
"rating": {
"type": "float",
"null_value": "1.0"
}
}
}
}
# 添加相同的数据, 再次聚合, 结果正确:
{
"took" : 0,
"timed_out" : false,
"_shards" : {
"total" : 1,
"successful" : 1,
"skipped" : 0,
"failed" : 0
},
"hits" : {
"total" : {
"value" : 2,
"relation" : "eq"
},
"max_score" : null,
"hits" : [ ]
},
"aggregations" : {
"avg_rating" : {
"value" : 3.0
}
}
}
参考资料
《极客时间》视频课之《Elasticsearch核心技术与实战》
版权声明
出处: 博客园 马瘦风的博客(https://www.cnblogs.com/shoufeng)
感谢阅读, 如果文章有帮助或启发到你, 点个[好文要顶
ES 32 - Elasticsearch 数据建模的探索与实践的更多相关文章
- [转] [Elasticsearch] 数据建模 - 处理关联关系(1)
[Elasticsearch] 数据建模 - 处理关联关系(1) 标签: 建模elasticsearch搜索搜索引擎 2015-08-16 23:55 6958人阅读 评论(0) 收藏 举报 分类: ...
- Elasticsearch 数据建模指南
文章转载自:https://mp.weixin.qq.com/s/vSh6w3eL_oQvU1mxnxsArA 0.题记 我在做 Elasticsearch 相关咨询和培训过程中,发现大家普遍更关注实 ...
- Elasticsearch数据建模笔记
数据建模 数据建模是创建数据模型的过程 数据模型是对真实世界进行抽象描述的一种工具和方法,实现对现实世界的映射 三个过程:概念模型=>逻辑模型=>数据模型 数据模型:结合具体的数据库,在满 ...
- WEB 三维引擎在高精地图数据生产的探索和实践
1. 前言 高精地图(High Definition Map)作为自动驾驶安全性不可或缺的一部分,能有效强化自动驾驶的感知能力和决策能力,提升自动驾驶的等级.对于自动驾驶来说,高精地图主要是给机器用的 ...
- ElasticSearch 数据建模
公号:码农充电站pro 主页:https://codeshellme.github.io 通常在使用 ES 构建数据模型时,需要考虑以下几点: 字段类型 是否需要搜索与分词 是否需要聚合与排序 是否需 ...
- ElasticSearch——数据建模最佳实践
如何建模 mapping 设计非常重要,需要从两个维度进行考虑: 功能:搜索.排序.聚合 性能:存储的开锁.内存的开销.搜索的性能 mapping 注意事项: 加入新字段很容易(必要时需要 updat ...
- 论Elasticsearch数据建模的重要性
文章转载自: https://mp.weixin.qq.com/s?__biz=MzI2NDY1MTA3OQ==&mid=2247484159&idx=1&sn=731562a ...
- Elasticsearch 6.x版本全文检索学习之数据建模
1.什么是数据建模. 答:数据建模,英文为Data Modeling,为创建数据模型的过程.数据模型Data Mdel,对现实世界进行抽象描述的一种工具和方法,通过抽象的实体及实体之间联系的形式去描述 ...
- ElasticSearch 学习记录之 分布式文档存储往ES中存数据和取数据的原理
分布式文档存储 ES分布式特性 屏蔽了分布式系统的复杂性 集群内的原理 垂直扩容和水平扩容 真正的扩容能力是来自于水平扩容–为集群添加更多的节点,并且将负载压力和稳定性分散到这些节点中 ES集群特点 ...
随机推荐
- Spring框架介绍及使用(转载)
原文链接 Spring框架—控制反转(IOC) 1 Spring框架概述1.1 什么是SpringSpring是一个开源框架,Spring是于2003 年兴起的一个轻量级的Java 开发框架,由Rod ...
- Features Track 2018徐州icpc网络赛 思维
Morgana is learning computer vision, and he likes cats, too. One day he wants to find the cat moveme ...
- CF1029C Maximal Intersection 暴力枚举
Maximal Intersection time limit per test 3 seconds memory limit per test 256 megabytes input standar ...
- hdu1255 覆盖的面积(线段树面积交)
题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=1255 面积交与面积并相似相比回了面积并,面积交一定会有思路,当然就是cover标记大于等于两次时. 但 ...
- 自定义Hive UDAF 实现相邻去重
内置的两个聚合函数(UDAF) collect_list():多行字符串拼接为一行collect_set():多行字符串拼接为一行并去重多行字符串拼接为一行并相邻去重UDAF:Concat() con ...
- 试试 IEnumerable 的另外 6 个小例子
IEnumerable 接口是 C# 开发过程中非常重要的接口,对于其特性和用法的了解是十分必要的.本文将通过6个小例子,来熟悉一下其简单的用法. <!-- more --> 阅读建议 在 ...
- Linux入门基础之 中
五.Linux 下获取帮助 没必要记住所有东西 Linux 提供了极为详细的帮助工具及文档,一定要养成查帮助文档的习惯,可以大大减少需要记忆的东西并且提高效率 5.1.HELP 几乎所有命令都可以使用 ...
- Go从入门到放弃
Go语言介绍 为什么你应该学习Go语言? 开发环境准备 从零开始搭建Go语言开发环境 VS Code配置Go语言开发环境 Go语言基础 Go语言基础之变量和常量 Go语言基础之基本数据类型 Go语言基 ...
- 041 模块5-jieba库的使用
目录 一.jieba库基本介绍 1.1 jieba库概述 1.2 jieba库的安装 1.3 jieba分词的原理 二.jieba库使用说明 2.1 jieba分词的三种模式 2.2 jieba库常用 ...
- Vert.x学习之 Web Client
Vert.x Web Client 原文档 组件源码 组件示例 中英对照表 Pump:泵(平滑流式数据读入内存的机制,防止一次性将大量数据读入内存导致内存溢出) Response Codec:响应编解 ...