我在做 Elasticsearch 相关咨询和培训过程中,发现大家普遍更关注实战中涉及的问题,下面我选取几个常见且典型的问题,和大家一起分析一下。
如果我们对上述实战问题进行归类,就都可以归结为 Elasticsearch <span style="letter-spacing: 0.5px;">数据建模
问题。
本文将以实战问题为基准,手把手带你实践 Elasticsearch 数据建模全流程,重点解析基于业务角度、数据量角度、Setting 、Mapping ,以及复杂索引关联,这五个层面中涉及的数据建模实战问题,让你学完即可应用到工作中。
我们选型传统的数据库,这里以 MySQL 为例,做数据存储前需要考虑的问题如下:
以上这些疑问也均是[数据建模] 问题。在 MySQL 中我们往往认为建模非常有必要,但反观 Elasticsearch ,“上手快”这类先入为主的观念已根植在很多同学心中,使得大家忽略了 Elasticsearch 数据建模的重要性。
接下来,我们基于 MySQL 做数据存储需要考虑的问题,重新审视数据建模的定义,内容如下。
到这里,相信你已经初步明晰了数据建模的重要性。但我还想提醒你的是,“[一把梭] 用法,上来就是干”并不是捷径,尤其到了项目中后期,极易暴露出问题。经历的项目越多,你会发现建模的时间不能省。
下面我们具体分析一下为什么要数据建模?
相比于 MySQL,Elasticsearch 有非常快捷的<span style="letter-spacing: 0.5px;">优势
:
Elasticsearch 支持动态类型检查和匹配。也就是说,当我们写入索引数据的时候,可以不提前指定数据类型,直接插入数据。
以类似天眼查、企查查的工商实战数据为例(已做脱敏处理),如果利用以下语句直接创建索引和写入一条数据,岂不是很快?
PUT company_index/_doc/1
{
"regist_id": 1XX1600000000012,
"company_name": "北京XX长江创业投资有限公司",
"regist_id_new": "191XX160066933968XC",
"legal_representative": "徐X武",
"scope_bussiness": "创业投资业务;代理其他创业投资企业等机构或个人的创业投资业务;创业投资咨询业务;为创业企业提供管理服务业务;参与设立创业投资企业与企业投资管理顾问机(依法须经批准的项目,经相关部门批准后方可开展经营活)",
"registration_status": "在营(开业)企业",
"approval_date": "201X年04月13日",
"registration_number": "191XX160066933968XC",
"establishment_time": "200X年12月03日",
"address": "北京市黄河XX路西首育青小区",
"register_capital": 3000,
"business_starttime": "20XX年12月03日",
"registration_authority": "北XX工商行政管理局",
"company_type": "其他有限责任公司",
"enttype": 1190,
"enttypename": "法定代表人:",
"pripid": "1XXX102201305305801X",
"uniscid": "1XXX160066933968XC"
}
相比于 MySQL 中一个字段一个字段地敲定,这样操作确实节省了很多时间。但随着后续数据量激增,副作用便会很快显现出来。该处理方式的弊端:
接下来,结合我自己工作中早期系统的一个案例,我们做进一步分析。
5 个数据节点集群(5 个分片,1 个副本),微博数据每日增量 5000W+(增量存储 150GB),核心数据磁盘 10TB 左右,很明显该系统面临存储上限问题。
我们当时就上述业务数据规划了一个大索引,比如微博数据一个索引,微信数据一个索引。但微博索引最多只能存储 20 天左右的数据,然后就得走删除索引数据的操作。由于 1 个索引只能通过 delete_by_query 删除部分数据,而 delete_by_query 的特点是版本号更新的逻辑删除,实际效果是越删数据量越大,磁盘占用率激增。加上是线上环境,压力之大,处理难度之大,经历过你就知道有多苦。
这也是很多大厂在面试候选人的时候,尤其偏爱数据建模能力强的工程师的主要原因之一。
比如下图是美团对大数据开发高级工程师的岗位要求,第一条就是“深入理解业务,对业务服务流程进行合理的抽象和建模。”
从以上两个反例,以及这条招聘信息中便可以窥探出数据建模的重要性。下面我们具体说说如何做数据建模。
在做数据建模之前,会先进行架构设计,架构环节涉及选型、集群规划、节点角色划分。
本文涉及的建模倾向于索引层面、数据层面的建模。为了让你学完即可应用到工作中,我会结合项目实战进行讲解。
Elasticsearch 适用范围非常广,包括电商、快递、日志等各行各业。涉及索引层面的设计,和业务贴合紧密。
其一:业务一定要细分。
分成哪几类数据,每类数据归结为一个索引还是多个索引,这是产品经理、架构师、项目经理要讨论敲定的问题。比如大数据类的数据,可以按照业务数据分为微博索引、微信索引、Twiiter 索引、Facebook 索引等。
其二:多个业务类型需不需要跨索引检索?
跨索引检索的痛点是字段不统一、不一致,需要写非常复杂的 bool 组合查询语句来实现。为了避免这种情况,最好的方式就是提前建模。每一类业务数据的相同或者相似字段,采取统一建模的方式。
下面我们举一个实际的例子加以分析。微博、微信、Twitter、Facebook 都有的字段,可以设计如下:
字段名称 | 字段中文含义 | 字段类型 |
---|---|---|
publish_time | 发布时间 | date |
author | 作者 | keyword |
cont | 正文内容 | text |
这样设计的好处是:字段统一,写查询 DSL 无需特殊处理,非常快捷方便。所以,在设计阶段,多个业务索引数据要尽可能地“求同存异”。具体来说:
比如微博信息来源字段有手机 App 或者网页等,别的业务索引如果没有,独立建模就可以。
类似这些建模信息可以统一 Excel 存储,统一 git 多人协作管理。
多索引管理一般优先推荐使用[模板] (alias)结合的方式。
如本文前面所述,我是吃过单索引激增的亏,所以对于时序性数据(日志数据、大数据类数据)等,我强烈建议你[基于时间切分索引] ,具体如下图所示。
当然,其他可用的方案非常多,这里我列举如下,供你选型参考。
由此可见,时序管理数据的优点非常明显。
Setting 层面又分为静态 Setting 和动态 Setting 两种。
一种是静态 Settings,一旦设置后,后续不可修改。如 number_of_shards
。
另一种是动态 Setting,索引创建后,后面随时可以更新。如 number_of_replicas
, max_result_window
, refresh_interval
。
仅就建模阶段最核心的问题,拆解如下。
这里有个认知前提,就是主分片数一旦设置后就不可以修改,副本分片数可以灵活动态调整。
主分片设计一般会考量总体数据量、集群节点规模,这点在集群规划层面会着重强调。一般主分片数要考虑集群未来动态扩展,通常设置为数据节点的 1 倍或者 1~3 倍之间的值。
副本分片是保证集群的高可用性,普通业务场景建议至少设置一个副本。
默认值 1s,这意味着在写入阶段,每秒都会生成一个分段。
refresh_interval
的目的是:数据由 index buffer
的堆内存缓存区刷新到堆外内存区域,形成 segment
,以使得搜索可见。
在实际业务场景里,如果写入的数据不需要近实时搜索可见,可以适当地在模板、索引层面调大这个值,当然也可以动态调整,比如调整为 30s 或者 60s。
这里同样有个认知前提,就是对于[深度翻页] 的 from + size 实现,越往后翻页越慢。其实你对比看主流搜索引擎,比如 Google、百度、360、Bing 均不支持一下跳转到最后一页,这就是最大翻页上限限制。
其实在基本业务层面也很好理解,按照相关度返回结果,前面几页是最相关的,越往后相关度越低。比如默认值 10000,也就是说如果每页显示 10 条数据,可以翻 1000 页。基本业务场景已经足够了。因此不建议调大该值。
如果需要向后翻页查询,推荐 search_after 查询方式。如果需要全量遍历或者全量导出数据,推荐 scroll 查询方式。
[管道预处理] 的好处很多,虽然 5.X 版本就有了这个功能,但实战环境用起来还不多。
管道 ingest pipeline
就相当于大数据的 ETL 抽取、转换、加载的环节,或者类似 logstash filter
处理环节。一些数据打标签、字段类型切分、加默认字段、加默认值等的预处理操作都可以借助 ingest pipelie
实现。
这里给出索引层面 Setting
设置的简单模板,供你进一步学习参考,如下定义了 indexed_at 缺省的管道,同时在索引 my_index_0001 指定了该缺省管道,这样做的好处,是每个新增的数据都会加了插入时刻的时间戳:indexed_at 字段,无需我们在业务层面手动处理,非常灵活和方便。
更多设置,推荐阅读官方文档,地址如下:
https://www.elastic.co/guide/en/elasticsearch/reference/current/index-modules.html#index-modules-settings
PUT _ingest/pipeline/indexed_at
{
"description": "Adds indexed_at timestamp to documents",
"processors": [
{
"set": {
"field": "_source.indexed_at",
"value": "{{_ingest.timestamp}}"
}
}
]
}
PUT my_index_0001
{
"settings": {
"number_of_replicas": 1,
"number_of_shards": 3,
"refresh_interval": "30s",
"index": {
"default_pipeline": "indexed_at"
}
},
"mappings": {
"properties": {
"cont": {
"type": "text",
"analyzer": "ik_max_word",
"fields": {
"keyword": {
"type": "keyword"
}
}
}
}
}
}
Mapping 层面核心是字段名称、字段类型、分词器选型、多字段 multi_fields 选型,以及字段细节(是否索引、是否存储等)的敲定。
索引名称不允许用大写,字段名称官方没有限制,但是可以参考 Java 编码规范。我还真见过学员用中文或者拼音命名的,非常不专业,大家一定要避免。
要结合业务类型选择合适的字段类型。比如 integer 能搞定的,就不要用 long、float 或 double。
注意,字符串类型在 5.X 版本之后分为两种类型:
再举个例子,实战中情感值介于 0~100 之间,50 代表中性,0~50 代表负面,50~100 代表正面。如果使用 integer 查询的时候要 range query,而实际存储可以增加字段:0~50 设置为 -1,50 设置为 0,50~100 设置为 1,三种都是 keyword 类型,检索时直接走 term 检索会非常快。
实战中中文分词器用得比较多,中文分词又分为 ansj,结巴,IK 等。以 IK 举例,可以细分为 ik_smart 粗粒度分词、ik_max_word 细粒度分词。
在工作中,要结合业务选择合适的分词器,分词器一旦设定是不可以修改的,除非 reindex。
分词器选型后,都会有动态词典的更新问题。更新的前提是不要仅使用开源插件原生词典,而是要在平时业务中自己多积累特定业务数据词典、词库。
如果要动态更新:一般推荐第三方更新插件借助数据库更新实现。如果普通分词都不能满足业务需要,可以考虑 [ngram
自定义分词] 方式实现更细粒度分词。
同一个字段根据需要可以设置多种类型。实战业务中,对用特定中文词明明存在,却无法召回的情况,采用字词混合索引的方式得以满足。
所谓字词混合,实际就是 standard 分词器实现单字拆解,以及 ik_max_word 实现中文切词结合的方式。检索的时候 bool 对两种分词器结合,就可以实现相对精准的召回效果。
PUT mix_index
{
"mappings": {
"properties": {
"content": {
"type": "text",
"analyzer": "ik_max_word",
"fields": {
"standard": {
"type": "text",
"analyzer": "standard"
},
"keyword": {
"type": "keyword",
"ignore_above": 256
}
}
}
}
}
}
POST mix_index/_search
{
"query": {
"bool": {
"should": [
{
"match_phrase": {
"content": "佟大"
}
},
{
"match_phrase": {
"content.standard": "佟大"
}
}
]
}
}
}
为了方便你记忆和使用,这里我把字段细节总结在如下这张表格中。
核心参数 | 默认值 | 释义 |
enabled | true | 仅适用于 Mapping 顶层以及 Object 对象,设置为 false 后该字段将不再被解析。 |
index | true | 控制是否对字段值进行索引,设置为 false 的字段不能被查询。 |
doc_values | true | 正排索引,除了 text 类型外的其他类型默认开启,用于聚合和排序分析。 |
fielddata | false | 是否为 text 类型启动 fielddata,实现 text 字段排序和聚合分析。 |
store | false | 是否存储该字段值。 |
coerce | true | 是否开启自动数据类型转换功能,比如 字符串转数字、浮点转整型。true 代表可以转换,false 代表不可以转换。 |
fields | fields | 灵活使用多字段解决多样的业务需求。 |
dynamic | true | 控制 mapping 的动态自动更新。 |
date_detection | true | 是否自动识别类型。 |
我们再来分析一下数据建模的流程,如下图所示。
数据建模的流程图
首先,根据业务选择合适的数据类型。
注意字符串类型分为两种 text 和 keyword类型;尽量选择贴近实际大小的数据类型;nested 和 join [复杂类型] 需根据业务特点选型,具体会在下一部分详细阐述。
其次,判定是否需要检索,如果不需要,index 设置为 false 即可。
然后,判定是否需要排序和聚合操作,如果不需要可以设置 doc_values 为 false。
最后,考虑一下是否需要另行存储,会结合使用 store 和 _source 字段。
Mapping 层面要强调的是:尽量不要使用默认的 dynamic 动态字段类型,强烈建议 strict 严格控制字段,避免字段“暴涨”导致不可预知的风险,比如字段数超过默认 1000 个的上限、磁盘大于预期的激增等。
要摒弃 MySQL 的多表关联建模思想,因为 MySQL 中的范式思想都不再适用于 Elasticsearch。回顾文章开头的几个多表关联问题,Elasticsearch 能提供的核心解决方案如下。
这是空间换时间的方案,就是允许部分字段冗余存储的存储方式。实战举例如下。
用户索引:user。
博客索引:blogpost。
一个用户可以发表多篇博客。按照传统的 MySQL 建表思想:两个表建立个用户外键,即可搞定一切。而对于 Elasticsearch,我们更愿意在每篇博文后面都加上用户信息(这就是宽表存储的方案),看似存储量大了,但是一次检索就能搞定搜索结果。
PUT user/_doc/1
{
"name": "John Smith",
"email": "john@smith.com",
"dob": "1970/10/24"
}
PUT blogpost/_doc/2
{
"title": "Relationships",
"body": "It's complicated...",
"user": {
"id": 1,
"name": "John Smith"
}
}
GET /blogpost/_search
{
"query": {
"bool": {
"must": [
{
"match": {
"title": "relationships"
}
},
{
"match": {
"user.name": "John"
}
}
]
}
}
}
如果需要索引对象数组并保持数组中每个对象的独立性,则应使用嵌套 Nested 数据类型而不是对象 Oject 数据类型。
[nested 文档] 的优点是可以将父子关系的两部分数据(如博客+评论)关联起来,我们可以基于nested 类型做任何的查询。但缺点是查询速度相对较慢,更新子文档需要更新整篇文档。
比如 1 个产品和供应商之间就是 1 对 N 的关联关系。当使用父子文档时,使用 has_child 或者 has_parent 做父子关联查询。优点是父子文档可独立更新,但维护 Join 关系需要占据部分内存,查询较 Nested 更耗资源。
注意:5.X 之前版本叫父子文档(多 type 实现),6.X 之后高版本是 join 类型(单 type 类型)。
需通过多次检索获取所需的关键字段,业务层面自己写代码实现。
这里小结一下,以上四种方式便是 Elasticsearch 能实现的全量多表关联方案。实战建模阶段,一定要结合自己的业务场景,尽量往上靠,先通过 kibana dev tool 模拟实现,找到契合自己业务的多表关联方案。
此外我还要强调的是:多表关联都会有性能问题,数据量极大且检索性能要求高的场景需要慎用。这里我摘取了官方文档对应的描述如下,供你参考。
尤其应该[避免多表关联] 。Nested 嵌套可以使查询慢几倍,而 Join 父子关系可以使查询慢数百倍。
最后,我们再来总结一下建模其他核心考量因素。
数据建模是 Elasticsearch 开发实战中非常重要的一环,也是项目管理角度中的设计环节的重中之重,你一定要重视!千万不要着急写业务代码,以“代码之前,设计先行”作为行动准绳。
感谢你的时间!有 Elasticsearch 建模问题欢迎留言交流。
本文成文于:2021年8月11日
本文由哈喽比特于2年以前收录,如有侵权请联系我们。
文章来源:https://mp.weixin.qq.com/s/vSh6w3eL_oQvU1mxnxsArA
京东创始人刘强东和其妻子章泽天最近成为了互联网舆论关注的焦点。有关他们“移民美国”和在美国购买豪宅的传言在互联网上广泛传播。然而,京东官方通过微博发言人发布的消息澄清了这些传言,称这些言论纯属虚假信息和蓄意捏造。
日前,据博主“@超能数码君老周”爆料,国内三大运营商中国移动、中国电信和中国联通预计将集体采购百万台规模的华为Mate60系列手机。
据报道,荷兰半导体设备公司ASML正看到美国对华遏制政策的负面影响。阿斯麦(ASML)CEO彼得·温宁克在一档电视节目中分享了他对中国大陆问题以及该公司面临的出口管制和保护主义的看法。彼得曾在多个场合表达了他对出口管制以及中荷经济关系的担忧。
今年早些时候,抖音悄然上线了一款名为“青桃”的 App,Slogan 为“看见你的热爱”,根据应用介绍可知,“青桃”是一个属于年轻人的兴趣知识视频平台,由抖音官方出品的中长视频关联版本,整体风格有些类似B站。
日前,威马汽车首席数据官梅松林转发了一份“世界各国地区拥车率排行榜”,同时,他发文表示:中国汽车普及率低于非洲国家尼日利亚,每百户家庭仅17户有车。意大利世界排名第一,每十户中九户有车。
近日,一项新的研究发现,维生素 C 和 E 等抗氧化剂会激活一种机制,刺激癌症肿瘤中新血管的生长,帮助它们生长和扩散。
据媒体援引消息人士报道,苹果公司正在测试使用3D打印技术来生产其智能手表的钢质底盘。消息传出后,3D系统一度大涨超10%,不过截至周三收盘,该股涨幅回落至2%以内。
9月2日,坐拥千万粉丝的网红主播“秀才”账号被封禁,在社交媒体平台上引发热议。平台相关负责人表示,“秀才”账号违反平台相关规定,已封禁。据知情人士透露,秀才近期被举报存在违法行为,这可能是他被封禁的部分原因。据悉,“秀才”年龄39岁,是安徽省亳州市蒙城县人,抖音网红,粉丝数量超1200万。他曾被称为“中老年...
9月3日消息,亚马逊的一些股东,包括持有该公司股票的一家养老基金,日前对亚马逊、其创始人贝索斯和其董事会提起诉讼,指控他们在为 Project Kuiper 卫星星座项目购买发射服务时“违反了信义义务”。
据消息,为推广自家应用,苹果现推出了一个名为“Apps by Apple”的网站,展示了苹果为旗下产品(如 iPhone、iPad、Apple Watch、Mac 和 Apple TV)开发的各种应用程序。
特斯拉本周在美国大幅下调Model S和X售价,引发了该公司一些最坚定支持者的不满。知名特斯拉多头、未来基金(Future Fund)管理合伙人加里·布莱克发帖称,降价是一种“短期麻醉剂”,会让潜在客户等待进一步降价。
据外媒9月2日报道,荷兰半导体设备制造商阿斯麦称,尽管荷兰政府颁布的半导体设备出口管制新规9月正式生效,但该公司已获得在2023年底以前向中国运送受限制芯片制造机器的许可。
近日,根据美国证券交易委员会的文件显示,苹果卫星服务提供商 Globalstar 近期向马斯克旗下的 SpaceX 支付 6400 万美元(约 4.65 亿元人民币)。用于在 2023-2025 年期间,发射卫星,进一步扩展苹果 iPhone 系列的 SOS 卫星服务。
据报道,马斯克旗下社交平台𝕏(推特)日前调整了隐私政策,允许 𝕏 使用用户发布的信息来训练其人工智能(AI)模型。新的隐私政策将于 9 月 29 日生效。新政策规定,𝕏可能会使用所收集到的平台信息和公开可用的信息,来帮助训练 𝕏 的机器学习或人工智能模型。
9月2日,荣耀CEO赵明在采访中谈及华为手机回归时表示,替老同事们高兴,觉得手机行业,由于华为的回归,让竞争充满了更多的可能性和更多的魅力,对行业来说也是件好事。
《自然》30日发表的一篇论文报道了一个名为Swift的人工智能(AI)系统,该系统驾驶无人机的能力可在真实世界中一对一冠军赛里战胜人类对手。
近日,非营利组织纽约真菌学会(NYMS)发出警告,表示亚马逊为代表的电商平台上,充斥着各种AI生成的蘑菇觅食科普书籍,其中存在诸多错误。
社交媒体平台𝕏(原推特)新隐私政策提到:“在您同意的情况下,我们可能出于安全、安保和身份识别目的收集和使用您的生物识别信息。”
2023年德国柏林消费电子展上,各大企业都带来了最新的理念和产品,而高端化、本土化的中国产品正在不断吸引欧洲等国际市场的目光。
罗永浩日前在直播中吐槽苹果即将推出的 iPhone 新品,具体内容为:“以我对我‘子公司’的了解,我认为 iPhone 15 跟 iPhone 14 不会有什么区别的,除了序(列)号变了,这个‘不要脸’的东西,这个‘臭厨子’。