There is multiple ways to measure HBase performances. There are tools included in HBase, external tools, or even home-made scripts.
Let's try to list them first.
Tools included in HBase:
- org.apache.hadoop.hbase.PerformanceEvaluation
- org.apache.hadoop.hbase.util.LoadTestTool
External tools:
Home-made scripts:
If you know other performances/load test tools for HBase, feel free to let me know so I can give them a try and add them on this list.
Why performances tests?
It's important to have a way to measure the performances of your cluster to be able to compare the overall performances with different settings or different hardware. Some clusters are mainly used in write mode, some others in read mode and then some in both. Having a good baseline of your cluster speed will allow you to decide how to configure it to fit your needs and will show you performance impacts of configuration/hardware changes.
Performance test is also used in quality control to ensure that newly added features or API are not impacting the baseline performance.
PerformanceEvaluation
This first post will be about org.apache.hadoop.hbase.PerformanceEvaluation. The other tools will come in other posts and table of contents will link to them directly.
The fist and simplest way to measure HBase performances is to use the PerformanceEvaluation tool shipped with HBase into the test jar. Simply download HBase, extract the .tar.gz file, start HBase and run the test. No configuration required (standalone mode) nor special setting required. Of course you can still run this test against your own cluster to measure its overall performances. But if you simply want to compare the performances of 2 different computers, then nothing more is required.
You can call this tool with this command line:
bin/hbase org.apache.hadoop.hbase.PerformanceEvaluation
And you will get something like this:
Usage: java org.apache.hadoop.hbase.PerformanceEvaluation \
[--miniCluster] [--nomapred] [--rows=ROWS]
Options:
miniCluster Run the test on an HBaseMiniCluster
nomapred Run multiple clients using threads (rather than use mapreduce)
rows Rows each client runs. Default: One million
flushCommits Used to determine if the test should flush the table. Default: false
writeToWAL Set writeToWAL on puts. Default: True
presplit Create presplit table. Recommended for accurate perf analysis (see guide). Default: disabled
Command:
filterScan Run scan test using a filter to find a specific row based on it's value (make sure to use --rows=20)
randomRead Run random read test
randomSeekScan Run random seek and scan 100 test
randomWrite Run random write test
scan Run scan test (read every row)
scanRange10 Run random seek scan with both start and stop row (max 10 rows)
scanRange100 Run random seek scan with both start and stop row (max 100 rows)
scanRange1000 Run random seek scan with both start and stop row (max 1000 rows)
scanRange10000 Run random seek scan with both start and stop row (max 10000 rows)
sequentialRead Run sequential read test
sequentialWrite Run sequential write test
Args:
nclients Integer. Required. Total number of clients (and HRegionServers)
running: 1 Examples:
To run a single evaluation client:
$ bin/hbase org.apache.hadoop.hbase.PerformanceEvaluation sequentialWrite 1
As you can see, PerformanceEvaluation allow you to test the read and write methods under different scenarios. It will start a local MapReduce job to do it, but you can also ask it to use threads instead. You just have to follow the given example to start it. PerformanceEvaluation will create a table called TestTable and will use it for its needs.
Here is an extract from this command line results with HBase 0.94.0
13/02/05 22:08:36 INFO hbase.PerformanceEvaluation: Start class org.apache.hadoop.hbase.PerformanceEvaluation$SequentialWriteTest at offset 0 for 1048576 rows
13/02/05 22:09:23 INFO hbase.PerformanceEvaluation: 0/104857/1048576
13/02/05 22:11:16 INFO hbase.PerformanceEvaluation: 0/209714/1048576
13/02/05 22:12:33 INFO hbase.PerformanceEvaluation: 0/314571/1048576
13/02/05 22:13:39 INFO hbase.PerformanceEvaluation: 0/419428/1048576
13/02/05 22:15:36 INFO hbase.PerformanceEvaluation: 0/524285/1048576
13/02/05 22:16:30 INFO hbase.PerformanceEvaluation: 0/629142/1048576
13/02/05 22:17:27 INFO hbase.PerformanceEvaluation: 0/733999/1048576
13/02/05 22:18:24 INFO hbase.PerformanceEvaluation: 0/838856/1048576
13/02/05 22:19:20 INFO hbase.PerformanceEvaluation: 0/943713/1048576
13/02/05 22:21:03 INFO hbase.PerformanceEvaluation: 0/1048570/1048576
13/02/05 22:21:03 INFO hbase.PerformanceEvaluation: Finished class org.apache.hadoop.hbase.PerformanceEvaluation$SequentialWriteTest in 747156ms at offset 0 for 1048576 rows
How to interpret that? 1048576 is the number of rows the PerformanceEvaluation tool writes for its test. Basically, it's 1024*1024 rows. In the example above, it took him 747 seconds to write those 1048576 lines into my standalone test environment. That mean, performances for this specific test in my environment are 1403 lines per second. Now to compare the performances between different versions of HBase, or for different settings, you just need to run the exact same test on the same computer. I ran the same test with HBase 0.94.1 and got 1409 lines per seconds. It's less than 1% difference, so we can say results are pretty identical.
Also, you can't rely on those results if you run the test only once. To be accurate, you need to run it multiple times so you have a strong baseline. What I recommend is to run the test at least 10 times. Then remove the fastest one, remove the slowest one, and do an average of the 8 remaining results. That will give you a way more accurate picture of the test you run. But you need to be aware that some of those tests can be long. RandomSeekScanTest took 1h20 on my computer... Running it 10 times will take 13h.
Now, I really want to emphasis on the "exact same test on the same computer"... PerformanceEvaluation is a good example of what I mean by "exact same test". As I said previously, the test is creating a table named "TestTable". This table is not pre-split, and if running with a single user, there is nothing done if the table already exist. Some tests are writing into it, some are reading. That mean each time you are using this table, some values are put into cache. And at the end, you might end with a big table, with many regions, full of many rows added by write tests. Before each test you are starting, you need to make sure that your environment is EXACTLY as it was for the previous test... "Exact same test on the same computer"... Just to illustrate, running 10 times SequentialWriteTest has created 10 times the same 1024x1024 rows in my TestTable because I did not remove it between each test. That mean the table was initially empty. After the first run, there was 1024*1024 rows into the table. One version of each. After the 2nd run, there was another 1024*1024 rows written, with the same key. That mean I got 2 versions of each row, and so on. After the 3rd run, the number of version is going over the configured default number of versions (3) and some compactions might occur while rows are added in run 4. You will see that on the performances tests results below. So to run the tests properly, you need to remove the created data and restart HBase to make sure things are in the same state before each test start.
Another thing you need to understand about performances tests. The longer the test is, the more accurate the results are. If your test last 10 seconds and unfortunately it's just at that time that a 100ms log rotate take place, a 100ms write to disk occurs, or your daily ntpdate cron entry start, anything like that will represent 1% or more of your total test duration! Any activity done by the operating system or any other process might impact your results. Now, if you take the same log rotate or nptdate but you dilute it over a 10 minutes test, it's no more 1% that it represents, but it's something like 0.02%.
Here are the results for the same test ran on another computer (without cleaning between each run):
13/02/06 20:02:37 INFO ... SequentialWriteTest in 231577ms at offset 0 for 1048576 rows => 4528
13/02/06 20:06:13 INFO ... SequentialWriteTest in 202305ms at offset 0 for 1048576 rows => 5183 (Max)
13/02/06 20:12:04 INFO ... SequentialWriteTest in 261183ms at offset 0 for 1048576 rows => 4015
13/02/06 20:17:58 INFO ... SequentialWriteTest in 336621ms at offset 0 for 1048576 rows => 3155
13/02/06 20:22:53 INFO ... SequentialWriteTest in 239211ms at offset 0 for 1048576 rows => 4383
13/02/06 20:29:39 INFO ... SequentialWriteTest in 398487ms at offset 0 for 1048576 rows => 2631 (Min)
13/02/06 20:35:59 INFO ... SequentialWriteTest in 330099ms at offset 0 for 1048576 rows => 3177
13/02/06 20:42:13 INFO ... SequentialWriteTest in 332859ms at offset 0 for 1048576 rows => 3150
13/02/06 20:47:07 INFO ... SequentialWriteTest in 284928ms at offset 0 for 1048576 rows => 3680
13/02/06 20:54:14 INFO ... SequentialWriteTest in 398410ms at offset 0 for 1048576 rows => 2632
Average is 3590, standard deviation is 322 (9%).
For those tests, I did not stop HBase and I did not clear the table between each test. This is mainly why it's getting slower because table needs to be compacted (due to the number of versions).
Now, here are the results with a clean between each test. The command line used for that was:
for i in {1..10}; do rm -rf /tmp/*; bin/start-hbase.sh; sleep 60; bin/hbase org.apache.hadoop.hbase.PerformanceEvaluation sequentialWrite 1; bin/stop-hbase.sh; done
13/02/06 22:31:46 INFO ... SequentialWriteTest in 184663ms at offset 0 for 1048576 rows => 5678
13/02/06 22:36:46 INFO ... SequentialWriteTest in 187087ms at offset 0 for 1048576 rows => 5605
13/02/06 22:41:20 INFO ... SequentialWriteTest in 190496ms at offset 0 for 1048576 rows => 5504
13/02/06 22:45:54 INFO ... SequentialWriteTest in 189181ms at offset 0 for 1048576 rows => 5543
13/02/06 22:50:41 INFO ... SequentialWriteTest in 197229ms at offset 0 for 1048576 rows => 5317 (Min)
13/02/06 22:55:20 INFO ... SequentialWriteTest in 187134ms at offset 0 for 1048576 rows => 5603
13/02/06 22:59:49 INFO ... SequentialWriteTest in 185768ms at offset 0 for 1048576 rows => 5645
13/02/06 23:04:25 INFO ... SequentialWriteTest in 191631ms at offset 0 for 1048576 rows => 5472
13/02/06 23:08:53 INFO ... SequentialWriteTest in 184092ms at offset 0 for 1048576 rows => 5696
13/02/06 23:13:23 INFO ... SequentialWriteTest in 183385ms at offset 0 for 1048576 rows => 5718 (Max)
Average 5593, standard deviation is 65 (1.17%)
As you can see, this time tests are more consistent, standard deviation is only 65. This is way better.
If I remove the best and the worst and average the others, for HBase 0.94.4, I have an average of 5593 lines/seconds. Doing exactly the same on the same environment, but with HBase 0.94.0, is giving me an average of 5088 lines/seconds (1.13%). What we can deduct from those tests is that 0.94.4 is almost 10% faster than 0.94.0 for SequentialWriteTest tests (doesn't mean it's the same for the other tests).
As an example of some configuration impacts, I have activated the HBase checksums on 0.94.4 and re-run the same test (again, 10 times) and the result is now 5524 (0.852%) lines/seconds. Since both series are accurate (less to 2% standard deviation), we can conclude that checksums are impacting HBase performance by 1.25%. This shows us the performances impacts the change of the configuration can have.
Conclusion
What you have to retain from those tests is:
- It's very important to make sure you have the exact same conditions for your tests if you want to be able to compare it with other runs.
- org.apache.hadoop.hbase.PerformanceEvaluation provides you an easy way to get quick numbers out of your installation.