一、前言
数据平台已迭代三个版本,从头开始遇到很多常见的难题,终于有片段时间整理一些已完善的文档,在此分享以供所需朋友的
实现参考,少走些弯路,在此篇幅中偏重于ES的优化,关于HBase,Hadoop的设计优化估计有很多文章可以参考,不再赘述。
二、需求说明
项目背景:
在一业务系统中,部分表每天的数据量过亿,已按天分表,但业务上受限于按天查询,并且DB中只能保留3个月的数据(硬件高配),分库代价较高。
改进版本目标:
1. 数据能跨月查询,并且支持1年以上的历史数据查询与导出。
2. 按条件的数据查询秒级返回。
三、elasticsearch检索原理
3.1 关于ES和Lucene基础结构
Cluster 包含多个Node的集群 Node 集群服务单元 Index 一个ES索引包含一个或多个物理分片,它只是这些分片的逻辑命名空间 Type 一个index的不同分类,6.x后只能配置一个type,以后将移除 Document 最基础的可被索引的数据单元,如一个JSON串 Shards 一个分片是一个底层的工作单元,它仅保存全部数据中的一部分,它是一个Lucence实例 (一个lucene索引最大包含2,147,483,519 (= Integer.MAX_VALUE - 128)个文档数量) Replicas 分片备份,用于保障数据安全与分担检索压力
3.2 Lucene索引实现
http://lucene.apache.org/core/7_2_1/core/org/apache/lucene/codecs/lucene70/package-summary.html#package.description
For other features that we now commonly associate with search, such as sorting, faceting, and highlighting, this approach is not very efficient. The faceting engine, for example, must look up each term that appears in each document that will make up the result set and pull the document IDs in order to build the facet list. In Solr, this is maintained in memory, and can be slow to load (depending on the number of documents, terms, etc.)
3.3 关于ES索引与检索分片
shard = hash(routing) % number_of_primary_shards
四、优化案例
-
ES仅提供字段的检索,仅存储HBase的Rowkey不存储实际数据。
-
实际数据存储在HBase中,通过Rowkey查询,如下图。
-
提高索引与检索的性能建议,可参考官方文档
https://www.elastic.co/guide/en/elasticsearch/reference/current/tune-for-indexing-speed.html
4.1 优化索引性能
4.2 优化检索性能
http://lucene.apache.org/core/7_4_0/core/org/apache/lucene/index/PointValues.html
-
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页,不能跳到最后一页。
-
scroll: 用于大结果集查询,缺陷是需要维护scroll_id
{
"mappings": {
"data": {
"dynamic": "false",
"_source": {
"includes": ["XXX"] -- 仅将查询结果所需的数据存储仅_source中
},
"properties": {
"state": {
"type": "keyword", -- 虽然state为int值,但如果不需要做范围查询,尽量使用keyword,因为int需要比keyword增加额外的消耗。
"doc_values": false -- 关闭不需要字段的doc values功能,仅对需要排序,汇聚功能的字段开启。
},
"b": {
"type": "long" -- 使用了范围查询字段,则需要用long或者int之类 (构建类似KD-trees结构)
}
}
}
},
"settings": {......}
}
五、性能测试
-
单节点5千万到一亿的数据量测试,检查单点承受能力。
-
集群测试1亿-30亿的数量,磁盘IO/内存/CPU/网络IO消耗如何。
-
随机不同组合条件的检索,在各个数据量情况下表现如何。
-
另外SSD与机械盘在测试中性能差距如何。
六、生产效果