由于 Elasticsearch(后文简称es) 的简单易用及其在大数据处理方面的良好性能,越来越多的公司选用 es 作为自己的业务解决方案。然而在引入新的解决方案前,不免要做一番调研和测试,本文便是介绍官方的一个 es 压测工具 esrally,希望能为大家带来帮助。
关于压测,我们先来看下百度百科上的一个定义。
压测,即压力测试,是确立系统稳定性的一种测试方法,通常在系统正常运作范围之外进行,以考察其功能极限和隐患。
从定义不难看出压测的目的,是要测出一个系统的极限,提早发现隐患,早作打算。那么对于 es 来讲,我认为压测一般有以下几个目的:
现在我们知道压测的目的了,接下来该如何进行压测呢?一般有以下几个方案:
各个压测方案各有优劣,大家可以根据自己的需求和工具熟悉度来选择自己的压测工具。接下来我们就来具体了解下 esrally。
esrally 是 elastic 官方开源的一款基于 python3 实现的针对 es 的压测工具,源码地址为https://github.com/elastic/rally,相关博客介绍在这里。esrally主要功能如下:
elastic 官方也是基于 esrally 进行 es 的性能测试,并将结果实时发布到 https://elasticsearch-benchmarks.elastic.co/ ,大家可以从该网站上直接查看 es 的性能。官方使用两台服务器进行压测,一台运行 esrally ,一台运行 es,服务器的配置如下:
CPU: Intel(R) Core(TM) i7-6700 CPU @ 3.40GHz
RAM: 32 GB
SSD: Crucial MX200
OS: Linux Kernel version 4.8.0-53
JVM: Oracle JDK 1.8.0_131-b11
网站顶部的 Geonames、Geopoint、Percolator等都是针对不同的数据集做的压测,比如下面这些图展示了 logging 日志类数据的压测结果。



esrally 的文档在这里,这里简单说下安装与运行。
esrally 对于软件环境的要求如下:
安装方法为:
pip3 install esrally
Tips:
可以使用国内的pip源,比如豆瓣或者阿里的,这样安装会快很多。
安装完毕后执行如下的配置命令,确认一些数据存放的路径即可。
esrally configure
接下来就可以开跑了,比如下面这条命令是针对 es 5.0.0 版本进行压力测试。
esrally --distribution-version=5.0.0
运行结束后,会得到如下的结果。

对于第一次见到压测结果的同学来说可能有些晕,这么多数据,该怎么看?!别急,一步步来!
Tips:
由于 esrally 的测试数据存储在国外 aws 上,导致下载很慢甚至会超时失败,从而导致整个压测无法进行。后面我会把这些测试数据的压缩包放到国内,大家可以下载后直接放到 esrally 的数据文件夹下面,保证压测的正常进行。另外由于数据量过大,压测的时间一般会很久,可能在1个小时左右,所以大家要有耐心哦~
如果你只是想体验下,可以加上 --test-mode 的参数,此时只会下载1000条文档进行测试。
rally 是汽车拉力赛的意思,也就是说 esrally 是将压测比作了汽车拉力赛,因此其中的很多术语都是从汽车拉力赛中借鉴来的。
track
track 是赛道的意思,在这里是指压测用的数据和测试策略,详细文档在这里。esrally 自带的track都在 github 上,地址在这里 https://github.com/elastic/rally-tracks。在该 repository 中,有很多测试数据,比如 geonames geopoint logging nested 等,每个数据文件夹中的 README.md 中有详细的数据介绍,而 track.json 便是压测策略的定义文件。
我们来看下 loggins/track.json 文件
{% import "rally.helpers" as rally with context %}
{
"short-description": "Logging benchmark",
"description": "This benchmark indexes HTTP server log data from the 1998 world cup.",
"data-url": "http://benchmarks.elasticsearch.org.s3.amazonaws.com/corpora/logging",
"indices": [
{
"name": "logs-181998",
"types": [
{
"name": "type",
"mapping": "mappings.json",
"documents": "documents-181998.json.bz2",
"document-count": 2708746,
"compressed-bytes": 13815456,
"uncompressed-bytes": 363512754
}
]
},
{
"name": "logs-191998",
"types": [
{
"name": "type",
"mapping": "mappings.json",
"documents": "documents-191998.json.bz2",
"document-count": 9697882,
"compressed-bytes": 49439633,
"uncompressed-bytes": 1301732149
}
]
}
],
"operations": [
{{ rally.collect(parts="operations/*.json") }}
],
"challenges": [
{{ rally.collect(parts="challenges/*.json") }}
]
}
该 json 文件主要包含下面几个部分:
operations/default.json 中的一个定义如下:
{
"name": "index-append",
"operation-type": "index",
"bulk-size": 5000
}
其中 operation-type 包含 index、force-merge、index-stats、node-stats、search等,每一个operation-type都有自己的可定义参数,比如 index 中可以通过指定 bulk-size 来决定批量写入的文档数。
challenges/default.json 中的一个定义如下:
{
"name": "append-no-conflicts",
"description": "",
"default": true,
"index-settings": {
"index.number_of_replicas": 0
},
"schedule": [
{
"operation": "index-append",
"warmup-time-period": 240,
"clients": 8
},
{
"operation": "force-merge",
"clients": 1
},
{
"operation": "index-stats",
"clients": 1,
"warmup-iterations": 100,
"iterations": 100,
"target-throughput": 50
},
{
"operation": "node-stats",
"clients": 1,
"warmup-iterations": 100,
"iterations": 100,
"target-throughput": 50
},
{
"operation": "default",
"clients": 1,
"warmup-iterations": 100,
"iterations": 500,
"target-throughput": 10
},
{
"operation": "term",
"clients": 1,
"warmup-iterations": 100,
"iterations": 500,
"target-throughput": 60
},
{
"operation": "range",
"clients": 1,
"warmup-iterations": 100,
"iterations": 200,
"target-throughput": 2
},
{
"operation": "hourly_agg",
"clients": 1,
"warmup-iterations": 100,
"iterations": 100,
"target-throughput": 0.2
},
{
"operation": "scroll",
"clients": 1,
"warmup-iterations": 100,
"iterations": 200,
"target-throughput": 10
}
]
}
这里定义了一个名为 append-no-conflicts 的 challenge。由于每次压测只能运行一个challenge,这里的 default 参数是指当压测未指定时默认运行的 challenge。schedule 中指定了该 challenge 中按顺序执行 index-append、force-merge、index-stats、node-stats、default、term、range、hourly_agg、scroll 等 9 个task,其中每个 task 都指定了 一个 operation,除此之外还可以设定 clients (并发客户端数)、warmup-iterations(预热的循环次数)、iterations(operation 执行的循环次数)等,详情请参见此处。
通过下面的命令可以查看当前 esrally 可用使用的track。
esrally list tracks
esrally 的 track 数据位于 rally 目录(mac默认是 ~/.rally)中 benchmarks/tracks/ 下面。
car
car 是赛车的意思,这里是指不同配置的 es 实例。通过下面的命令可以查看 esrally 当前可用的 car。
esrally list cars
Name
----------
16gheap
1gheap
2gheap
4gheap
8gheap
defaults
ea
verbose_iw
cars 的配置位于 rally 目录(mac默认是 ~/.rally)中 benchmarks/teams/default/cars/ 下面。具体配置可以参见 cars 的文档,除了 heap 的配置,所有的 es 配置都可以修改。
race
race 是一次比赛的意思,这里是指某一次压测。要比赛,就要有赛道和赛车,如果不指定赛车,就用 default 配置,如果不指定赛道,则默认使用 geonames track。通过下面的命令来执行一次 race。
esrally race --track=logging --challenge=append-no-conflicts --car="4gheap"
上面的命令便是执行一次压测,并指定使用 logging 的track,运行该 track 中的 append-no-conflicts 的 challenge,指定的 car 为 4gheap 的 es 实例。详情可以查看 race 相关文档。
Tournament
tournament 是锦标赛的意思,是由多个 race 组成的。通过下面的命令可以查看所有的 race。
esrally list races
Recent races:
Race Timestamp Track Challenge Car User Tag
---------------- ------- ------------------- -------- ------------------------------
20160518T122341Z pmc append-no-conflicts defaults intention:reduce_alloc_1234
20160518T112057Z pmc append-no-conflicts defaults intention:baseline_github_1234
20160518T101957Z pmc append-no-conflicts defaults
当有了多个 race 后,可以通过下面的命令方便地比较不同 race 之间的数据。
esrally compare --baseline=20160518T112057Z --contender=20160518T112341Z

详细信息可以参见 tournament 的文档。
Pipeline
Pipeline 在这里是指压测的一个流程,通过下面的命令可以查看已有的pipeline。
esrally list pipeline
Name Description
----------------------- ---------------------------------------------------------------------------------------------
from-sources-complete Builds and provisions Elasticsearch, runs a benchmark and reports results.
from-sources-skip-build Provisions Elasticsearch (skips the build), runs a benchmark and reports results.
from-distribution Downloads an Elasticsearch distribution, provisions it, runs a benchmark and reports results.
benchmark-only Assumes an already running Elasticsearch instance, runs a benchmark and reports results
详细信息请参见 pipeline 的文档。
esrally 的压测流程主要分为以下三个步骤:
压测结束后,esrally 会将结果输出到终端和结果文件(位于 esrally 目录logs 和 benchmarks/races)中,如下图所示:

在 Metric 一栏,有非常多的指标数据,详细的解释可以参见该文档。一般要关注的数据有:
先搞懂每个 metric 的含义,然后根据自己的需求去确认自己要关注的指标。
每一次压测都会以压测时的时间命名,比如 logs/rally_out_20170822T082858Z.log ,这个日志便是记录的 2017年8月22日 8:28:58开始的压测日志。而在 benchmarks/races/2017-08-22-08-28-58 中记录着最终的结果和 es 的运行日志。
另外对于 benchmark-only 模式的测试,即针对已有集群的压力测试,也可以通过安装 X-Pack Basic 版本进行监控(Monitoring),在压测的过程中就能查看相关指标。

esrally 可以在配置的时候指定将所有的 race 压测结果数据存入一个指定的 es 实例中,配置如下(在 esrally 目录中 rally.ini 文件中):
[reporting]
datastore.type = elasticsearch
datastore.host = localhost
datastore.port = 9200
datastore.secure = False
datastore.user =
datastore.password =
esrally 会将数据存储在如下 3 个index中,下面 * 代指月份,即按月存储结果数据。

第一列时间是指某一次压测的时间,第二列时间是指标采集的时间,第三列 operation 指具体执行的操作,operation 为空的指标都是总计类的,比如indexing total time 记录的是总索引数据的时间、segments_count 是总段数等等。其他的 operation 都记录了每一个操作的数据。需要注意的是,这里记录的是 operation 的所有采样数据,不是一个最终的汇总数据。上面截图中也可以看出同一个 hour_agg 的operation 有多项名为 service_time 的指标数据,但他们的采集时间是不同的。基于这些数据,我们可以做出某一次 race 中某个指标的可视化图表,比如你想观察本次 race 中 index-log 这个 task 的 throughput 指标数据,便可以通过如下图的方式实现。

rally-result-* 该索引分指标记录了每次 race 的最终汇总结果,比如下面这条数据。
{
"user-tag": "shardSizeTest:size6",
"distribution-major-version": 5,
"environment": "local",
"car": "external",
"plugins": [
"x-pack"
],
"track": "logging",
"active": true,
"distribution-version": "5.5.2",
"node-count": 1,
"value": {
"50_0": 19.147876358032228,
"90_0": 21.03116340637207,
"99_0": 41.644479789733886,
"100_0": 47.20634460449219
},
"operation": "term",
"challenge": "default-index",
"trial-timestamp": "20170831T063724Z",
"name": "latency"
}
这个记录了 term operation 的 latency 指标数据,汇总值以 percentile(百分位数) 的形式展示。基于该数据,我们可以绘制针对某个指标的多race对比,比如下图便是对比多 race 之间 hourly_agg(按小时做聚合)、default(match_all 查询)、term(term查询)、range(range查询)的latency(延迟时间)对比。

除了es相关指标数据外,esrally 还会同时记录测试的一些环境信息,比如操作系统、JVM等等,你可以方便的查看本次测试的软硬件环境。
终于到了开赛的时候,下面我们采用问答的形式来进行,希望大家看到问题后先自己思考下再看答案。
提问:如何对比 5.5.0 相比 2.4.6 的性能改进?
回答:
分别针对 5.5.0 和 2.4.6 做一次压测,然后比较两者两者的相关指标即可,这里我们的 track 和 challenge 如下:
测试步骤如下:
esrally race --distribution-version=2.4.6 --track=nyc_taxis --challenge=append-no-conflicts --user-tag="version:2.4.6"
esrally race --distribution-version=5.5.0 --track=nyc_taxis --challenge=append-no-conflicts --user-tag="version:5.5.0"
esrally list races
esrally compare --baseline=[2.4.6 race] --contender=[5.5.0 race]
Tips:
--user-tag 用于为 race 打标签,方便后续查找
如果只是试一下,可以加上 --test-mode ,用测试数据来跑,很快。
提问:如何测试 _all 关闭后对于写性能的影响?
回答:
针对 5.5.0 版本的 es 做两次测试,第一次开启 _all,第二次关闭 _all,对比两次的结果,由于只测试写性能,所以我们只需要 index 类型的 operation执行。这里我们的 track 和 challenge 如下:
测试步骤如下:
esrally race --distribution-version=5.5.0 --track=nyc_taxis --challenge=append-no-conflicts --user-tag="enableAll:false" --include-tasks="type:index"
benchmarks/tracks/default/nyc_taxis/mappings.json,修改 _all.enabled 为 true。esrally race --distribution-version=5.5.0 --track=nyc_taxis --challenge=append-no-conflicts --user-tag="enableAll:true" --include-tasks="type:index"
esrally list races
esrally compare --baseline=[enableAll race] --contender=[disableAll race]
下图是我在 --test-mode 模式下运行的对比结果,也可以看出关闭 _all 可以提升写性能。

Tips:
--include-tasks 用于只运行 challenge 中的部分 task
提问:如何测试已有集群的性能?
回答:
使用 benchmark-only 的 pipeline 即可,这里我们的 track 和 challenge 如下:
测试步骤如下:
esrally race --pipeline=benchmark-only --target-hosts=127.0.0.1:9200 --cluster-health=yellow --track=nyc_taxis --challenge=append-no-conflicts
Tips:
--cluster-health=yellow 默认 esrally 会检查集群状态,非 green 状态会直接退出。添加该参数可以避免该情况
希望这三个问答可以帮助到大家快速掌握 esrally 的用法。
前面讲解 car 的时候,我们提到 esrally 已经自带了一些可用的 es 配置,但是如果这些还不能满足你的时候,可以通过下面两个方案解决。
benchmarks/teams/default/cars,在这里新增一个自己的 car 配置文件就可以了。这里就不赘述了,感兴趣的可以查阅 car 的文档。虽然 esrally 自带了很多 track,而且这些数据本身也不小,简单列在下面:
| Track | 压缩数据大小 | 解压数据大小 | 文档数 |
|---|---|---|---|
| geonames | 252 MB | 3.3 GB | 11396505 |
| geopoint | 482 MB | 2.3 GB | 60844404 |
| logging | 1.2 GB | 31 GB | 247249096 |
| nested | 663 MB | 3.3 GB | 11203029 |
| noaa | 947 MB | 9 GB | 33659481 |
| nyc_taxis | 4.5 GB | 74 GB | 165346692 |
| percolator | 103KB | 105 MB | 2000000 |
| pmc | 5.5 GB | 22 GB | 574199 |
这些数据文件位于 esrally 目录 benchmarks/data 下面。不同的 Track 有不同的测试目的,详情可以去该 github repo 下面去查看。
当我们做定向测试的时候,还是希望针对自己的数据进行压测,此时可以自定义 track。操作也很简单,详情可以参考官方文档。这里简单列一下操作步骤。
esrally list rack 就可以看到自定义的 track。esrally 还支持分布式压测,即如果一个节点的 esrally 无法达到要求的并发数、请求数,那么可以将 esrally 分布到多台机器上去同时执行。分布式压测文档在这里,此处用到了 esrally dameon,对应命令是 esrallyd 。简单讲就是 esrally 通过 esrallyd 将多台机器组合成一个集群,然后 esrally 在执行测试任务的时候通过制定 --load-driver-hosts 便可以将测试任务分发到对应的机器上执行。这里便不赘述了,感兴趣的去看前面提到的文档。
让我们回到开头提到的容量规划的问题吧!
提问:一个 index 的 shard 数该如何确认?
回答:
其实针对这个提问,还可以再问下面两个问题。
要回到这两个问题,我们得先知道 shard 的作用。shard 是 es 实现分布式特性的基石,文档在索引进 es 时,es 会根据一个路由算法,将每一个文档分配到对应的 shard 上。每个 shard 实际对应一个 lucene index。那么每个 shard 能存储的文档数是否有上限呢?答案是有!每个shard最多存储 2^31 个文档,即 20亿。这是 lucene 设计决定的。那是不是只要我的文档数没有超过20亿,就可以只用一个或者很少的shard 呢?不尽然。因为随着 shard 体积的增大,其查询效率会下降,而且数据迁移和恢复的成本也会增高。官方建议单个 shard 大小不要超过 50GB,可以参见讨论一和讨论二。
现在回答上面的两个问题。
shard数过小不一定好,如果数据量很大,导致每个 shard 体积过大,会影响查询性能。
shard数过大也不一定好,因为 es 的每次查询是要分发给所有的 shard 来查询,然后再对结果做聚合处理,如果 shard 数过多也会影响查询性能。因此 shard 的数量需要根据自己的情况测出来。
官方文档有一节关于容量规划的章节,建议大家去看一下,链接在这里,其给出的步骤如下:
测试的过程中,关注相关指标数据,比如索引性能、查询性能,如果在某一个点相关性能数据超出了你的预期值,那么此时的 shard size大小便是符合你预期的单个 shard size的大小。接下来通过下面这个简单的计算公式便大致能确定一个 index 需要设定的 shard 数了。
shard数 = index 的数据总大小/单个shard size的极限值
比如你测出单个 shard size 最大为 20 GB,而你预测该索引数据最大量在1年或者2年内不会超过 200GB,那么你的 shard 数就可以设置为10。
接下来要做的事情也很明确,我们要用 esrally 完成上面的压测步骤:
自定义 track,这里有以下两个重点:
iterations 参数,即循环进行同一个操作,这样也可以测试大数据量的写性能。operations 中是可以定义自己的查询语句的,比如下面这个{
"name": "hourly_agg",
"operation-type": "search",
"index": "logs-*",
"type": "type",
"body": {
"size": 0,
"aggs": {
"by_hour": {
"date_histogram": {
"field": "@timestamp",
"interval": "hour"
}
}
}
}
}
其中的 body 便是自定义的查询语句,所以你可以通过自己的需求来设定查询语句,以贴近实际使用的情况。
Tips:
esrally 默认在每次压测是会删除已有的索引后再重新创建索引,如果你不想这样,可以在每个 index 的配置中设置auto-managed为 false,具体文档在这里。
通过这个参数,你就可以单独压测查询性能了,而不用每次都要先经过漫长的导入数据的过程。
esrally 针对 es 的压测设计了一套完备的基于配置文件的测试流程,极大地简化了操作难度,并且提供了可重复验证的方式。对国内用户来讲,我认为最大的难处还是在于 esrally 自带的 track 文件太大,从 国外 aws 下载很慢。好在可以自定义 track,不必完全依赖自带的 track。
其他没啥好说的,esrally 棒棒哒,大家赶紧去试试吧,如果有问题欢迎来讨论!
来源:网络
全文检索与数据库检索的区别: 1、检索方式 数据库检索:是 like 检索,数据逐条检索,性能慢; 全文检索:先把数据拿过来建立检索,然后在根据建立的索引进行查找。 2、全文检索的结果有相关度排序 数据库和ES的对比 ES6之后一个index只能有一个Type,因为在数据库种表与表之间的独立的,而在一个index中,如果多个Type有相同字段的话会出现冲突。...
ElasticSearch简介 2.1 什么是ElasticSearch 2.2 ElasticSearch的使用案例 2.3 ElasticSearch对比Solr ElasticSearch相关概念 概述 Elasticsearch核心概念 索引 index 类型 type 字段Field 相当于是数据表的字段,对文档数据根据不同属性进行的分类标识 映射 mapping 文档 document...
1.背景介绍 性能测试、负载测试、压力测试 负载测试:负载测试是一种性能测试,指数据在超负荷环境中运行,程序是否能够承担。在一定的工作负荷下,给系统造成的负荷及系统响应的时间。 压力测试:压力测试是一种性能测试,他在系统资源特别低的情况下软件系统运行情况,目的是找到系统在哪里失效以及如何失效的地方。在一定的负荷条件下,长时间连续运行系统给系统性能造成的影响。包括: Spike testing(尖峰...
以下内容整理自网上: 一、什么是Elasticsearch Elasticsearch (ES)是一个基于 Lucene 的开源搜索引擎,它不但稳定、可靠、快速,而且也具有良好的水平扩展能力,是专门为分布式环境设计的,Elasticsearch是面向文档型数据库,这意味着它存储的是整个对象或者文档,它不但会存储它们,还会为他们建立索引,这样你就可以搜索他们了。你可以在 Elasticsearch ...
es存储结构: 我们从es存储一个数据来分析es的结构: index相当于我们的mysql中的数据库的database _type相当于我们数据的表名 id:相当于我们表中的某一条主键id,也是一个唯一标识 version相当于数据的版本号 _source相当于我们的表数据,在es存储都是json数据 一.环境搭建 esrally 对于软件环境的要求如下: Python 3.4+ 和 pip3 J...
目录 一、ELK 二、Elasticsearch 1、定义: 2、涉及的几个基本概念 3、Elasticsearch 的架构 4、Elasticsearch 和Solr 的区别 :都是当前比较火的全文搜索引擎 一、ELK 1、定义 :ELK 是Elastic 公司旗下三款产品,Elasticsearch 、Logstash 、Kibana 的组合 2、为什么使用ELK ? ...
读书应具有学习和求知的欲望,也就是说,要用人类的天才积累的知识财富充实自己的头脑和心灵。 ——《书话》 1、简介 ElasticSearch是一个基于Lucene的搜索服务器。它提供了一个分布式多用户能力的全文搜索引擎,基于RESTful web接口。Elasticsearch是用Java开发的,并作为Apache许可条款下的开放源码发布,是当前流行的企业级搜索引擎。设计用...
前言 我们建立一个网站或者一个应用通常都会有搜索的功能,如果我们做的只是用户量很少的内网项目,并且搜索的字段都是一些内容很简短的字段,比如名字,编号之类的,那完全可以用数据库like语句 但是,数据库like查询性能非常低,如果搜索的请求多,或者需要搜索的是大文本类型的内容(全文搜索),那么...
如果需要一块视图,有背景颜色,设置固定尺寸以及边距,圆角边框的设定等,Container 这个Widget值得考虑 Container 可让您创建矩形视觉元素。container 可以装饰为一个BoxDecoration, 如 background、一个边框、或者一个阴影。 Container 也可以具有边距(margins)、填充(padding)和应用于其大小的约束(constraints)。...
1. 启动LR自带的web服务用于后续测试。 LR安装目录下: HP\LoadRunner\WebTours\StartServer.bat 若启动没有错误信息可访问链接: http://localhost:1080/WebTours/ 查看某个端口被占用: netstat -ano|finds...
I have a window with a fixed shape and put up some text on the GUI. Here is my code: This code by default alligns the text to the left. How to keep it in the middle? Here is a picture of my window for...
First time I execute this program, the resulting file does not have anything in it besides a new line. But the second time I execute it, it writes to 'out.txt' correctly, but the new line from the fir...
Whenver I update Newtonsoft.Json to version 10, I keep getting the following error when running DocuSign.eSign.dll The type initializer for 'DocuSign.eSign.Client.Configuration' threw an exception. I:...
A web server responds to an HTTP request and sends the client a set of HTML, CSS, and JS files. After we build a website or a web application, we usually have to host it on a well-known web server lik...
I have a Vaadin 7 application that has a common layout and in which there is a button, that starts a thread, that does some computing. There is a component that receives information from the computati...