做者简介git
巧爷,饿了么物流研发部数据中心技术负责人,有丰富的ES、Influxdb使用经验和数据应用开发经验,but...文风惊奇,校稿人累觉不爱github
Elasticsearch ,简称es,主要运用于全文搜索、数据分析, 底层使用开源库Lucene,拥有丰富的REST API,开箱即用。分布式的数据存储、倒排索引等设计,使其能够快速存储、搜索、分析海量数据。典型的使用方和应用场景,如github,StackOverflow,elasticsearch+logstash+kibana 一体化的日志分析。数据库
下面主要从咱们如何存储数据、用这些数据干什么两个方面来展开咱们对于es的应用。有啥子不合理、不足之处欢迎你们指正。apache
随着饿了么运单数据的增加,传统的数据库很难支撑现有的业务:api
一、各类场景的数据查询、统计,致使数据库必须加入各类字段的索引,大大增长了在生成一条运单,插入数据所须要的成本,严重影响了生成运单的并发度;大量的索引同时占用了很大的磁盘空间,同时给数据库变动带来的更大的风险;数组
二、不少场景没有办法或者很难实现,如:运单分页查询。大致量的数据数据库必然是sharding的,此时数据在分页上面必须须要经过别的工具。此时咱们引入了ES,来处理容许必定延迟的数据查询、统计的业务。bash
如你们所知的那样,ES不支持事务、复杂的数据关系(后期版本稍有改善,可是仍然支持的不是很好),利用_version (版本号)的方式来确保应用中相互冲突的变动不会致使数据丢失,那么咱们是如何存储咱们的数据,数据结构是什么样子,如何保证数据的完整性和一致性的呢?数据结构
首先说下咱们的运单数据索引的数据结构。并发
一、合适的分片数和副本数。网上有不少关于如何规划分片数的文章,本人感受能够做为参考, 在机器性能、数据量的大小、使用场景等等的不一样,分片、副本的数量最好能够经过压测或者是线上实际流量来作调整。app
二、咱们会尽可能减小咱们所须要的字段,作到够用就好,mapping设置方面:设置"_all"为false,String类型"index"尽可能设置为不分词("not_analyzed",根据须要设置analyzed),商家名称这类String类型字段只存储索引结构,不存储原始文档(后面会聊到如何拿到原始文档)。
起初咱们在建运单索引的时候,咱们是尽可能冗余运单上面的全部信息,致使一个星期的运单数据达到一个T的大小,而上面大部分的字段都是不须要的,磁盘利用率很低,而用于该集群的都是ssd盘,经常因为磁盘存不下,而须要添加机器,致使大量的资源浪费。这也须要咱们支持一个额外的能力,万一须要添加某个运单字段,咱们须要在需求上线以前迅速将历史数据补齐这个字段,同时不影响线上。(咱们如今能够一个晚上重刷咱们须要周期内的历史数据)。
"mappings": {
"index_type_name": {
"_all": {
"enabled": false
},
"_source": {
"excludes": ["merchant_name"]
},
"properties": {
"order_id": {
"type": "long"
},
"merchant_name": {
"type": "string",
"index": "not_analyzed"
}
...
}
}
}
复制代码
三、以一天为一个索引(根据业务场景,由于咱们的业务场景大部分要的都是某天的数据),这也为咱们根据实际线上流量调整咱们分片、副本数量提供了方便,修改完索引的模板("_template")以后,次日会自动生效,而查询多天不一样分片数量的运单索引的联合查询不会影响查询结果。
四、尽可能避免Nested Objects数据类型(nested数据结构)。每个nested object 将会做为一个隐藏的单独文本创建索引,虽然官网上说在查询的时候将根文本和nested文档拼接是很快的,就跟把他们当成一个单独的文本同样的快。可是其实仍是有一部分的额外的消耗,尤为是在aggs聚合的时候,它会使一层聚合其实变成了两层聚合:须要先聚合隐藏文件,再对实际需求进行聚合。若是真的须要放入数组类型的数据,能够根据实际需求,转化为一个字段,直接建在主数据上面(有必要的话,能够对nested object直接建一个新的索引)。
好比:咱们如今有一个索引,里面有某个学校天天每一个学生的学习、生活状况,每一个学生天天会产生一条数据。如今咱们想统计每一个班级某天 中午在校吃饭的人数、以及一天在校的用餐次数,咱们能够设计一个nested Objects数据结构来存储一天三餐的状况,也能够在主数据上添加四个字段:早上是否在校吃饭,中午是否在校吃饭、晚上是否在校吃饭,三餐在校用餐次数,这样就能够直接对着这四个字段进行数据统计。
五、尽可能减小script line的使用。一样的道理,咱们能够预先将须要用script line 的中间值先存到主数据上面。避免查询、统计时候的额外消耗。
一、考虑在不影响已有的业务状况下,咱们采起解析运单数据落库产生的binlog日志来建索引(binlog日志公司有一套解决方案,不必定非要使用binlog日志,运单状态变化的mq消息也是能够的),使其与运单正常业务解耦。
二、此时咱们不会直接拿这条数据插入ES,由于运单状态变化在同一个时刻可能会发生屡次,每次的数据插入不必定是数据库当前的状态,并且不论binlog日志、仍是运单状态变化消息都只是涵盖了部分数据,若是要运单在发消息的时候,把全部须要的数据补齐,对于运单的业务来讲,会面临常常修改消息结构的问题,这已经违背了咱们要使其与运单正常业务解耦的初衷, 因此咱们在收到这条数据变动的时候,会经过运单id反查运单数据,运单确定会时时刻刻保持有最新的经过运单id查询运单所有信息的接口,这样咱们就能够拿到咱们想要的任何最新数据。
三、经过es建索引的 bulk api 减小与es集群的交互次数,提升数据写入的吞吐量。
四、同一条运单数据,在同一个时刻可能会在机器A和机器B中同时发生更新操做,机器A查询到的是旧数据,机器B查询到了新数据,可是写入索引的时候机器B先写入ES集群,机器A后写入集群,致使数据错误。解决方案:每条数据写入的时候,添加一个分布式锁,相同运单号的数据在同一个时刻只能有一条发生写索引的动做,没有得到分布式锁消息,丢入延迟队列,下次再消费。
五、数据的补偿(此处就不展开了)。
前面咱们讲述了咱们ES中的索引结构遵循的一些原则,其中有一条是,咱们不会在ES中存储原始文档,那么咱们是如何支持查询运单的具体数据的呢?其实这就是一个ES集群的定位问题,咱们的ES集群仅仅是用来丰富运单查询、支持数据统计的功能,咱们并不支持数据的实际存储,咱们存储的仅仅只是每一个字段的索引而已,经过每一个字段的索引支持各类各样的运单查询、数据统计,若是须要查询运单的详细信息,咱们经过ES查询获得运单id后,会去运单的查询服务查询到该信息,再吐给需求方,咱们会将这个步骤包掉,需求方无感知,且返回的数据只是将运单的查询服务的数据包了一层,尽量减小其余方的接入成本。
ES在作数据统计的时候每每会很消耗ES集群的资源,因此咱们一般不容许需求方直接经过接口访问ES,咱们会将各个维度的数据提早算好放入其余类型的数据库中,供业务方使用,此处也不进行展开了。
(此处想到什么讲什么了。)
一、ES数据统计查询的时候,一样的查询条件,两次查询出来的数据结果可能会不同,这是由于副本分片和主分片数据不一致(ES只保证最终一致),ES在写操做的时候有个consistency的参数来控制写入的一致性,具体值为one(primary shard),all(all shard),quorum(default)。
one:要求咱们这个写操做,只要有一个primary shard是active活跃可用的,就能够执行
all:要求咱们这个写操做,必须全部的primary shard和replica shard都是活跃的,才能够执行这个写操做
quorum:默认的值,要求全部的shard中,必须是大部分的shard都是活跃的,可用的,才能够执行这个写操做
可是就算设置成了all以后,查询仍是有不一致的状况,这是使用lucene索引机制带来的refresh问题,完全解决该问题就势必会增长写入的成本,咱们选取了另外一种方式:对于会短期内出现先后两次查询的需求指定从primary shard读。
二、ES查询成功,部分shard失败;这个问题很尴尬,由于我在前期很长时间都没注意到这个问题,发现查询成功后,就直接把结果丢出去了,后来一次ES集群异常,发现查询出来的数据要比正常小不少,不多是ES主、副本分片数据不一致的问题,才发现时该问题。
三、新增字段的时候,必定要先更新全部已存在的索引的Mapping,再更新template,最后才能发更新后的程序。因为ES集群写操做在默认状况下,Mapping中没有的字段,会被自动识别,而自动识别的字段可能不是咱们想要的字段类型,而这个时候想要不断服务的修改,会很复杂。因此必定要在发新的程序以前修改好Mapping、template。
四、有时候为了提升ES集群的性能,咱们会按期的手工作一些段合并,此时要注意设置段合并的线程数,防止影响到正常业务。
五、监控ES的慢查询,虽然ES集群是分布式的,可是同样会因为过分的慢查询而打爆集群的状况。
六、作好ES集群的监控很重要,网上有不少教程,此处也再也不重述了。
在ElaticSearch里面,路由功能算是一个高级用法,大多数时候咱们用的都是系统默认的路由功能,ES的_routing字段的取值默认是_id字段,而现实在咱们的业务中,有太多的字段能够且须要做为路由字段。若是有机会,后续篇中,我将会介绍饿了么物流数据中心是如何经过公司已有的多活系统来支持咱们的ES路由功能的相关话题。
阅读博客还不过瘾?
欢迎你们扫二维码经过添加群助手,加入交流群,讨论和博客有关的技术问题,还能够和博主有更多互动
![]()
博客转载、线下活动及合做等问题请邮件至 shadowfly_zyl@hotmail.com 进行沟通