来源:cnblogs.com/mikevictor07/p/10006553.html

在此篇幅中偏重于 ES 的优化,关于 HBase,Hadoop 的设计优化有很多文章可以参考,不再赘述。

需求说明

项目背景:在一业务系统中,部分表每天的数据量过亿,已按天分表,但业务上受限于按天查询,并且 DB 中只能保留 3 个月的数据(硬件高配),分库代价较高。

改进版本目标:

  • 数据能跨月查询,并且支持 1 年以上的历史数据查询与导出。

  • 按条件的数据查询秒级返回。

Elasticsearch 检索原理

①关于 ES 和 Lucene 基础结构

谈到优化必须能了解组件的基本原理,才容易找到瓶颈所在,以免走多种弯路。

先从 ES 的基础结构说起(如下图):

一些基本概念:

  • Cluster 包含多个 Node 的集群。

  • Node 集群服务单元。

  • Index 一个 ES 索引包含一个或多个物理分片,它只是这些分片的逻辑命名空间。

  • Type 一个 index 的不同分类,6.x 后只能配置一个 Type,以后将移除。

  • 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 索引实现

Lucene 索引文件结构主要的分为:词典、倒排表、正向文件、DocValues 等。

如下图:

整理来源于 Lucene 官方:

http://lucene.apache.org/core/7_2_1/core/org/apache/lucene/codecs/lucene70/package-summary.html#package.description

Lucene 随机三次磁盘读取比较耗时。其中 .fdt 文件保存数据值损耗空间大,.tim 和 .doc 则需要 SSD 存储提高随机读写性能。另外一个比较消耗性能的是打分流程,不需要则可屏蔽。关于 DocValues:倒排索引解决从词快速检索到相应文档 ID, 但如果需要对结果进行排序、分组、聚合等操作的时候则需要根据文档 ID 快速找到对应的值。通过倒排索引代价却很高:需迭代索引里的每个词项并收集文档的列里面 Token。这很慢而且难以扩展:随着词项和文档的数量增加,执行时间也会增加。

Solr docs 对此的解释如下:

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.)

在 Lucene 4.0 版本前通过 FieldCache,原理是通过按列逆转倒排表将(field value->doc)映射变成(doc->field value)映射,问题为逐步构建时间长并且消耗大量内存,容易造成 OOM。DocValues 是一种列存储结构,能快速通过文档 ID 找到相关需要排序的字段。在 ES 中,默认开启所有(除了标记需 analyzed 的字符串字段)字段的 doc values,如果不需要对此字段做任何排序等工作,则可关闭以减少资源消耗。

③关于 ES 索引与检索分片

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 查询,如下图。

  • 提高索引与检索的性能建议,可参考官方文档:

https://www.elastic.co/guide/en/elasticsearch/reference/current/tune-for-indexing-speed.html

一些细节优化项官方与其他的一些文章都有描述,在此文章中仅提出一些本案例的重点优化项。

优化索引性能

①批量写入,看每条数据量的大小,一般都是几百到几千。②多线程写入,写入线程数一般和机器数相当,可以配多种情况,在测试环境通过 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 删除和更新数据,性能下降不是很明显。⑦关于段合并,合并在后台定期执行,比较大的 Segment 需要很长时间才能完成。

为了减少对其他操作的影响(如检索),Elasticsearch 进行阈值限制,默认是 20MB/s,可配置的参数(根据磁盘性能调整):

"indices.store.throttle.max_bytes_per_sec" : "200mb" 

合并线程数默认是:

Math.max(1, Math.min(4, Runtime.getRuntime().availableProcessors() / 2))

如果是机械磁盘,可以考虑设置为 1:

index.merge.scheduler.max_thread_count: 1

在我们的案例中使用 SSD,配置了 6 个合并线程。

优化检索性能

①关闭不需要字段的 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 页,不能跳到最后一页。

scroll:用于大结果集查询,缺陷是需要维护 scroll_id。

⑥关于排序:我们增加一个 long 字段,它用于存储时间和 ID 的组合(通过移位即可),正排与倒排性能相差不明显。⑦关于 CPU 消耗,检索时如果需要做排序则需要字段对比,消耗 CPU 比较大,如果有可能尽量分配 16cores 以上的 CPU,具体看业务压力。⑧关于合并被标记删除的记录,我们设置为 0 表示在合并的时候一定删除被标记的记录,默认应该是大于 10% 才删除:

"merge.policy.expunge_deletes_allowed": "0"
{    "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": {......}}

性能测试

优化效果评估基于基准测试,如果没有基准测试无法了解是否有性能提升,在这所有的变动前做一次测试会比较好。在我们的案例中:

  • 单节点 5000 万到 1 亿的数据量测试,检查单点承受能力。

  • 集群测试 1 亿-30 亿的数量,磁盘 IO/内存/CPU/网络 IO 消耗如何。

  • 随机不同组合条件的检索,在各个数据量情况下表现如何。

  • 另外 SSD 与机械盘在测试中性能差距如何。

性能的测试组合有很多,通常也很花时间,不过作为评测标准时间上的投入有必要,否则生产出现性能问题很难定位或不好改善。对于 ES 的性能研究花了不少时间,最多的关注点就是 Lucene 的优化,能深入了解 Lucene 原理对优化有很大的帮助。

生产效果

目前平台稳定运行,几十亿的数据查询 100 条都在 3 秒内返回,前后翻页很快,如果后续有性能瓶颈,可通过扩展节点分担数据压力。

架构摆渡人,助你通往架构师方向的领路人。本号会定期分享架构相关的文章,专注于架构方向,关注我们,下一个架构师就是你。

es 仅返回单个字段 查询_ES性能优化实战,几十亿数据查询 3 秒返回!相关推荐

  1. mysql做十亿条数据查询_数据库优化:mysql数据库单机数十亿数据查询设计

    很久没写文章,是不是想着写点什么东西,分享下我的数据库设计思路,主要是针对单机数十亿及以上数据查询优化技巧. 如果只是简单的查询,没有频繁的写入操作,对查询速度不要求在毫秒级别,就不需要什么大型的数据 ...

  2. currenttimemillis 毫秒还是秒_Elasticsearch如何做到数十亿数据查询毫秒级响应?

    如果面试的时候碰到这样一个面试题:ES 在数据量很大的情况下(数十亿级别)如何提高查询效率? 这个问题说白了,就是看你有没有实际用过 ES,因为啥?其实 ES 性能并没有你想象中那么好的. 很多时候数 ...

  3. Elasticsearch如何做到数十亿数据查询毫秒级响应?

    如果面试的时候碰到这样一个面试题:ES 在数据量很大的情况下(数十亿级别)如何提高查询效率? 这个问题说白了,就是看你有没有实际用过 ES,因为啥?其实 ES 性能并没有你想象中那么好的. 很多时候数 ...

  4. es对几十亿数据能达到秒级响应吗_万亿数据下的多维实时分析系统,如何做到亚秒级响应...

    导语 当业务发展到一定规模,实时数据仓库是一个必要的基础服务.从数据驱动方面考虑,多维实时数据分析系统的重要性也不言而喻.但是当数据量巨大的情况下,拿腾讯看点来说,一天上报的数据量达到万亿级的规模,要 ...

  5. python做数据查询系统_[Python实战] 功能简单的数据查询及可视化系统

    前言 数据时代,数据的多源集成和快速检索查询是第一步,配上数据分析及可视化才能算窥得大数据一角. 创建这个项目的主要目的一是对前期工作的一些总结,二是提升自己. 这里简单介绍一下sqlpro这个项目的 ...

  6. SQL Server 2016 查询存储性能优化小结

    SQL Server 2016已经发布了有半年多,相信还有很多小伙伴还没有开始使用,今天我们来谈谈SQL Server 2016 查询存储性能优化,希望大家能够喜欢 作为一个DBA,排除SQL Ser ...

  7. JavaScript系列—性能优化之《网站性能优化实战——从12.67s到1.06s的故事》

    本篇博文来源于网络 226 人赞同了该文章 原文作者:IMWeb jerryOnlyZRJ  原文链接:网站性能优化实战--从12.67s到1.06s的故事 - 腾讯Web前端 IMWeb 团队社区 ...

  8. 《java性能优化实战》之编程性能优化

    目录 一.java编程性能优化实战 1.如何使用String.intern 节省内存 2.如何使用字符串的分割方法? 3.ArrayList还是LinkedList?使用不当性能差千倍 4.Strea ...

  9. 2 周流量激增百倍的腾讯课堂后台扩容和性能优化实战

    作者:andyawang,腾讯 CSIG 后台开发工程师 疫情期间,学校网课需求激增,腾讯课堂 2 天上线极速版,2 周内支持同时在线人数超百倍增长,对整个后台挑战非常大.整整 2 个月下来,同合作团 ...

最新文章

  1. 我的笔记本电脑有一个自带的摄像头 可是开机后在我的电脑里没有这个图标
  2. 解决idea中执行maven命令失败的问题
  3. The server time zone value is unrecognized or repr
  4. es python demo
  5. 最简单的方式实现QML无边框窗口边缘拖动调整大小
  6. 在项目中寻找代码的坏味道(命名)
  7. springboot配置https访问
  8. win32 汇编基础概念整理
  9. BZOJ 1898: [Zjoi2005]Swamp 沼泽鳄鱼 [矩阵乘法]
  10. AWT_事件监听(Java)
  11. 【水果识别】基于matlab GUI自助水果超市【含Matlab源码 594期】
  12. 独奏骑士服务器维护,独奏骑士最强流派天赋加点攻略
  13. 更改配置本地host地址
  14. 微信小程序——车牌键盘js+css
  15. cms自动更新php文件,PHPCMS站群管理系统-PHPCMS自动采集-PHPCMS自动更新
  16. 下一代 Web 应用模型 —— Progressive Web App
  17. Java Excel文件内容替换
  18. 构建nas_我的第一个diy nas服务器构建
  19. java乘方运算符号_Java 4. 运算符号
  20. 【正则】包含大写小写字母数字的8到16位的密码正则

热门文章

  1. 在Windows环境下为Python 2.5安装SSL模块
  2. rm如何在Linux中删除一个大文件
  3. Ecliplse安装tomcat插件
  4. 看寄存代码中的方式时,一定要看到方法的底部 (有时代码会在finally中写东西) 。
  5. 计算机网络2020秋--第三次测验
  6. jquery.easyui.tabs 中的首个tabs被最后tabs覆盖的问题解决方法
  7. 解决手机浏览器顶部下拉出现网页源或刷新的问题
  8. Eclipse里编辑代码,进度条出现“Remote System Explorer Operation”解决方法
  9. QT学习之解决QT中QIcon图标不显示的问题
  10. nginx+php-fpm页面显示空白的解决方法