数据建模是创建数据模型的过程,通过抽象的实体及实体之间的联系的形式去描述业务规则,从而实现对现实世界的映射。
mapping 字段的相关设置
enable | 设置为false时,仅存储,不做搜索或聚合分析 |
index | 是否构建倒排索引 |
index_options | 存储倒排索引的哪些信息(docs/freqs/positions/offsets) |
norms | 是否存储归一化相关参数,如果字段仅用于过滤和聚合分析,可关闭 |
doc_values | 是否启用doc_values,用于排序和聚合分析 |
field_data | 是否为text类型启用field data,实现排序和聚合分析 |
store | 是否存储该字段值 |
coerse | 是否开启自动数据类型转换功能,比如字符串转为数字、浮点型转为整型 |
multifields | 灵活使用多字段特性来解决多样的业务需求 |
dynamic | 控制mapping自动更新 |
date_detection | 是否自动识别日期类型 |
mapping 字段属性的设定流程
是何种类型? -> 是否需要检索? -> 是否需要排序和聚合分析? -> 是否需要另行存储?
是何种类型
字符串类型 | 需要分词则设定为text类型,否则设置为keyword类型 |
枚举类型 | 基于性能考虑将其设定为keyword类型,即便该数据为整形 |
数值类型 | 尽量选择贴近的类型,比如byte即可表示所有数值时,即选用byte,不要用long |
其他类型 | 比如布尔类型、日期、地理位置数据等 |
是否需要检索
完全不需要检索、排序、聚合分析,enable设置为false
不需要检索的字段,index设置为false
需要检索的字段,可以通过index_options、norms来设置的存储粒度
是否需要排序和聚合分析
不需要排序或者聚合分析功能,设置doc_values、fielddata为false。
是否需要另行存储
是否需要专门存储当前字段的数据,store设定为true,即可存储该字段的原始内容(与字段存储在_source中不相关),一般结合_source的enableed设定为false时使用。
实例
创建一个blog文章的index,如下的字段:标题 title、发布日期 publish_date、作者 author、摘要 abstract、内容 content、url
如果把content存储在_source,content的内容可能非常大,所以每次检索文档时,都会返回content的内容,这会导致es的性能下降。这时设置mapping的时候可以把_source关闭:
1 | PUT /blog_index |
如下是查询content中包含word的文档,通过stored_fields设定需要返回的字段:
1 | GET /blog_index/_search |
关联关系处理
es不擅长处理关系型数据库中的关联关系,比如blog文章和评论comment,在关系型数据库中可以通过blog_id关联。在es中可以通过如下两种手段变相解决:
nested object
parent/child
nested object
如下把comments的type设置为nested:
1 | PUT /blog_index_nested |
创建如下文档,在文章中包含两条comment:
1 | PUT /blog_index_nested/doc/1 |
如下查询的是comment中username包含lee和content包含awesome的文档:
1 | GET /blog_index_nested/_search |
parent/child
es还提供了类似关系型数据库中join的实现方式,使用join(6.x之后的版本才支持)数据类型实现。
如下创建一个blog_index_parent_child文档,指明父文档为blog,子文档为comment:
1 | PUT /blog_index_parent_child |
如下用join指明创建的两个blog为父文档:
1 | PUT /blog_index_parent_child/doc/1 |
如下创建两个子文档comment,用parent的值指明父文档的id,指明routing值确保父子文档在一个分片上,一般使用父文档id:
1 | PUT /blog_index_parent_child/doc/comment-1?routing=1 |
查询
parent/child 常见query语法包括如下几种:
parent_id: 返回某父文档的子文档
has_child: 返回包含某子文档的父文档
has_parent: 返回包含某父文档的子文档
parent_id
返回某父文档的子文档。
如下是查询的id为2的父文档中的comment的子文档:
1 | GET /blog_index_parent_child/_search |
has_child
返回包含某子文档的父文档。
如下是查询子文档comment中包含comment1的父文档:
1 | GET /blog_index_parent_child/_search |
has_parent
返回包含某父文档的子文档。
如下是查询父文档blog中title包含blog的子文档:
1 | GET /blog_index_parent_child/_search |
nested object 与 parent/child 对比
对比 | nested object | parent/child |
---|---|---|
优点 | 文档存储在一起,因此读取性能高 | 父子文档可以独立更新,互不影响 |
缺点 | 更新父或子文档时需要更新整个文档 | 为了维护join的关系,需要占用部分内存,读取性能较差 |
场景 | 子文档偶尔更新,查询频繁 | 子文档更新频繁 |
建议尽量选择nested object来解决问题
reindex
reindex指重建所有数据的过程,一般发生在如下情况:
mapping设置变更,比如字段类型变化、分词器字段更新等
index设置变更,比如分片数更改等
迁移数据
es提供了现成的API用于完成该工作:
_update_by_query在现有索引上重建
_reindex在其他索引上重建
_update_by_query
如下是对blog_index执行reindex,conflicts=proceed
表示如果版本冲突时,覆盖并执行:
POST /blog_index/_update_by_query?conflicts=proceed
_reindex
将source的数据重建到dest中。
如下是将blog_index重建到blog_new_index:
1 | POST /_reindex |
task
数据重建的时间受源索引文档规模的影响,当规模越大时,所需时间多,此时可以通过设定url参数wait_for_completion
为false来异步执行,es已task来描述执行任务。
es提供了task API来查看任务的执行进度和相关数据
POST /blog_index/_update_by_query?conflicts=proceed&wait_for_completion=false
服务器将会返回对应的task的id:
1 | { |
通过服务器返回对应的task的id,可以查看task的详情:
GET /_tasks/me8cxrESTm-MEu4_rWmwbA:7375