数据平台已迭代三个版本,从头开始遇到不少常见的难题,终于有片断时间整理一些已完善的文档,在此分享以供所需朋友的实现参考,少走些弯路,在此篇幅中偏重于ES的优化,关于HBase,Hadoop的设计优化估计有不少文章能够参考,再也不赘述。html
在一业务系统中,部分表天天的数据量过亿,已按天分表,但业务上受限于按天查询,而且DB中只能保留3个月的数据(硬件高配),分库代价较高。apache
数据能跨月查询,而且支持1年以上的历史数据查询与导出。缓存
谈到优化必须能了解组件的基本原理,才容易找到瓶颈所在,以避免走多种弯路,先从ES的基础结构提及(以下图):安全
一些基本概念:网络
Cluster: 包含多个Node的集群数据结构
Node: 集群服务单元多线程
Index: 一个ES索引包含一个或多个物理分片,它只是这些分片的逻辑命名空间elasticsearch
Type: 一个index的不一样分类,6.x后只能配置一个type,之后将移除ide
Document: 最基础的可被索引的数据单元,如一个JSON串工具
Shards : 一个分片是一个底层的工做单元,它仅保存所有数据中的一部分,它是一个Lucence实例 (一个Lucene: 索引最大包含2,147,483,519 (= Integer.MAX_VALUE - 128)个文档数量)
Replicas: 分片备份,用于保障数据安全与分担检索压力
ES依赖一个重要的组件Lucene,关于数据结构的优化一般来讲是对Lucene的优化,它是集群的一个存储于检索工做单元,结构以下图:
在Lucene中,分为索引(录入)与检索(查询)两部分,索引部分包含分词器、过滤器、字符映射器等,检索部分包含查询解析器等。
一个Lucene索引包含多个segments,一个segment包含多个文档,每一个文档包含多个字段,每一个字段通过分词后造成一个或多个term。
经过Luke工具查看ES的lucene文件以下,主要增长了_id和_source字段:
Lucene 索引文件结构主要的分为:词典、倒排表、正向文件、DocValues等,以下图:
Lucene随机三次磁盘读取比较耗时。其中.fdt文件保存数据值损耗空间大,.tim和.doc则须要SSD存储提升随机读写性能。另一个比较消耗性能的是打分流程,不须要则可屏蔽。
倒排索引解决从词快速检索到相应文档ID, 但若是须要对结果进行排序、分组、聚合等操做的时候则须要根据文档ID快速找到对应的值。
经过倒排索引代价缺很高:需迭代索引里的每一个词项并收集文档的列里面 token。这很慢并且难以扩展:随着词项和文档的数量增长,执行时间也会增长。Solr docs对此的解释以下:
在lucene 4.0版本前经过FieldCache,原理是经过按列逆转倒排表将(field value ->doc)映射变成(doc -> field value)映射,问题为逐步构建时间长而且消耗大量内存,容易形成OOM。
DocValues是一种列存储结构,能快速经过文档ID找到相关须要排序的字段。在ES中,默认开启全部(除了标记需analyzed的字符串字段)字段的doc values,若是不须要对此字段作任何排序等工做,则可关闭以减小资源消耗。
ES中一个索引由一个或多个lucene索引构成,一个lucene索引由一个或多个segment构成,其中segment是最小的检索域。
数据具体被存储到哪一个分片上:shard = hash(routing) \% number_of_primary_shards
默认状况下 routing参数是文档ID (murmurhash3),可经过 URL中的 _routing 参数指定数据分布在同一个分片中,index和search的时候都须要一致才能找到数据,若是能明确根据_routing进行数据分区,则可减小分片的检索工做,以提升性能。
在咱们的案例中,查询字段都是固定的,不提供全文检索功能,这也是几十亿数据能秒级返回的一个大前提:
ES仅提供字段的检索,仅存储HBase的Rowkey不存储实际数据。
实际数据存储在HBase中,经过Rowkey查询,以下图。
一些细节优化项官方与其余的一些文章都有描述,在此文章中仅提出一些本案例的重点优化项。
批量写入,看每条数据量的大小,通常都是几百到几千。
多线程写入,写入线程数通常和机器数至关,能够配多种状况,在测试环境经过Kibana观察性能曲线。
增长segments的刷新时间,经过上面的原理知道,segment做为一个最小的检索单元,好比segment有50个,目的须要查10条数据,但须要从50个segment分别查询10条,共500条记录,再进行排序或者分数比较后,截取最前面的10条,丢弃490条。在咱们的案例中将此 "refresh_interval": "-1" ,程序批量写入完成后进行手工刷新(调用相应的API便可)。
内存分配方面,不少文章已经提到,给系统50\%的内存给Lucene作文件缓存,它任务很繁重,因此ES节点的内存须要比较多(好比每一个节点能配置64G以上最好)。
磁盘方面配置SSD,机械盘作阵列RAID5 RAID10虽然看上去很快,可是随机IO仍是SSD好。
使用自动生成的ID,在咱们的案例中使用自定义的KEY,也就是与HBase的ROW KEY,是为了能根据rowkey删除和更新数据,性能降低不是很明显。
关闭不须要字段的doc values。
尽可能使用keyword替代一些long或者int之类,term查询总比range查询好 (参考lucene说明 http://lucene.apache.org/core/7\_4\_0/core/org/apache/lucene/index/PointValues.html)。
关闭不须要查询字段的_source功能,不将此存储仅ES中,以节省磁盘空间。
评分消耗资源,若是不须要可以使用filter过滤来达到关闭评分功能,score则为0,若是使用constantScoreQuery则score为1。
from + size: 每分片检索结果数最大为 from + size,假设from = 20, size = 20,则每一个分片须要获取20 * 20 = 400条数据,多个分片的结果在协调节点合并(假设请求的分配数为5,则结果数最大为 400*5 = 2000条) 再在内存中排序后而后20条给用户。这种机制致使越日后分页获取的代价越高,达到50000条将面临沉重的代价,默认from + size默认以下:index.max_result_window :10000
search_after: 使用前一个分页记录的最后一条来检索下一个分页记录,在咱们的案例中,首先使用from+size,检索出结果后再使用search_after,在页面上咱们限制了用户只能跳5页,不能跳到最后一页。
关于排序:咱们增长一个long字段,它用于存储时间和ID的组合(经过移位便可),正排与倒排性能相差不明显。
关于CPU消耗,检索时若是须要作排序则须要字段对比,消耗CPU比较大,若是有可能尽可能分配16cores以上的CPU,具体看业务压力。
优化效果评估基于基准测试,若是没有基准测试没法了解是否有性能提高,在这全部的变更前作一次测试会比较好。在咱们的案例中:
单节点5千万到一亿的数据量测试,检查单点承受能力。
集群测试1亿-30亿的数量,磁盘IO/内存/CPU/网络IO消耗如何。
随机不一样组合条件的检索,在各个数据量状况下表现如何。
性能的测试组合有不少,一般也很花时间,不过做为评测标准时间上的投入有必要,不然生产出现性能问题很难定位或很差改善。对于ES的性能研究花了很多时间,最多的关注点就是lucene的优化,能深刻了解lucene原理对优化有很大的帮助。
目前平台稳定运行,几十亿的数据查询100条都在3秒内返回,先后翻页很快,若是后续有性能瓶颈,可经过扩展节点分担数据压力。