因爲 Elasticsearch(後文簡稱es) 的簡單易用及其在大數據處理方面的良好性能,愈來愈多的公司選用 es 做爲本身的業務解決方案。然而在引入新的解決方案前,難免要作一番調研和測試,本文即是介紹官方的一個 es 壓測工具 esrally,但願能爲你們帶來幫助。html
關於壓測,咱們先來看下百度百科上的一個定義。node
壓測,即壓力測試,是確立系統穩定性的一種測試方法,一般在系統正常運做範圍以外進行,以考察其功能極限和隱患。python
從定義不難看出壓測的目的,是要測出一個系統的極限,提前發現隱患,早做打算。那麼對於 es 來說,我認爲壓測通常有如下幾個目的:git
如今咱們知道壓測的目的了,接下來該如何進行壓測呢?通常有如下幾個方案:github
各個壓測方案各有優劣,你們能夠根據本身的需求和工具熟悉度來選擇本身的壓測工具。接下來咱們就來具體瞭解下 esrally。算法
esrally 是 elastic 官方開源的一款基於 python3 實現的針對 es 的壓測工具,源碼地址爲https://github.com/elastic/rally,相關博客介紹在這裏。esrally主要功能以下:json
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 日誌類數據的壓測結果。app
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 是賽道的意思,在這裏是指壓測用的數據和測試策略,詳細文檔在這裏。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 是賽車的意思,這裏是指不一樣配置的 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 是一次比賽的意思,這裏是指某一次壓測。要比賽,就要有賽道和賽車,若是不指定賽車,就用 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 是錦標賽的意思,是由多個 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。
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 棒棒噠,你們趕忙去試試吧,若是有問題歡迎來討論!