{"id":55302,"date":"2020-01-17T00:00:00","date_gmt":"2020-01-16T21:00:00","guid":{"rendered":"https:\/\/prohoster.info\/blog\/blog_prohoster\/teoriya-i-praktika-ispolzovaniya-hbase"},"modified":"2020-02-18T14:03:24","modified_gmt":"2020-02-18T11:03:24","slug":"teoriya-i-praktika-ispolzovaniya-hbase","status":"publish","type":"post","link":"https:\/\/prohoster.info\/en\/blog\/administrirovanie\/teoriya-i-praktika-ispolzovaniya-hbase","title":{"rendered":"Theory and Practice of Using HBase","gt_translate_keys":[{"key":"rendered","format":"text"}]},"content":{"rendered":"<p>Good day! My name is Danil Lipovoy, and our team at Sbertech has started using HBase as an operational data store. During its study, we have accumulated experience that we wanted to systematize and describe (we hope many will find it useful). All the experiments below were conducted with versions HBase 1.2.0-cdh5.14.2 and 2.0.0-cdh6.0.0-beta1. <\/p>\n<ol>\n<li>Overall Architecture <\/li>\n<li>Data Writing in HBASE<\/li>\n<li>Data Reading from HBASE<\/li>\n<li>Data Caching<\/li>\n<li>Batch Processing with MultiGet\/MultiPut<\/li>\n<li>Table Region Splitting Strategy<\/li>\n<li>Fault Tolerance, Compaction, and Data Locality<\/li>\n<li>Settings and Performance<\/li>\n<li>Load Testing<\/li>\n<li>Conclusions<\/li>\n<\/ol>\n<p><noindex><a rel=\"nofollow\" name=\"habracut\"><\/a><\/noindex><\/p>\n<h2>1. Overall Architecture<\/h2>\n<p>\n<img decoding=\"async\" alt=\"Theory and Practice of Using HBase\" src=\"\/wp-content\/uploads\/2020\/01\/30800d8a48de4c52ff1650a4f1dec4c8.jpg\" style=\"display:block;margin: 0 auto;\" \/><br \/>\nThe backup Master listens for heartbeats from the active one on the ZooKeeper node and takes over the master functions in case of its disappearance. <\/p>\n<h2>2. Data Writing in HBASE<\/h2>\n<p>\nFirst, let\u2019s consider the simplest case \u2013 writing a key-value object to a table using put(rowkey). The client must first determine where the root region server (Root Region Server \u2014 RRS) is located, which holds the table hbase:meta. This information is obtained from ZooKeeper. After that, it contacts the RRS and reads the hbase:meta table, from which it retrieves information regarding which RegionServer (RS) is responsible for storing data for the specified rowkey in the table of interest. For further use, the meta-table is cached by the client, allowing subsequent requests to be faster, directly to the RS.<\/p>\n<p>Next, upon receiving the request, the RS first writes it to the WriteAheadLog (WAL), which is necessary for recovery in case of a failure. It then saves the data in MemStore. This is a memory buffer that contains a sorted set of keys for this region. The table can be divided into regions (partitions), each containing a non-overlapping set of keys. This allows for better performance by placing regions on different servers. However, despite the obviousness of this statement, we will see later that it does not work in all cases.<\/p>\n<p>After placing the record in MemStore, the client receives a response that the record has been successfully saved. However, it is actually stored only in the buffer and will be written to disk only after a certain interval of time or when it becomes filled with new data. <\/p>\n<p><img decoding=\"async\" alt=\"Theory and Practice of Using HBase\" src=\"\/wp-content\/uploads\/2020\/01\/10f4d5d600a20882690f5cc81322bc41.jpg\" style=\"display:block;margin: 0 auto;\" \/><br \/>\nWhen performing the 'Delete' operation, the physical deletion of data does not occur. They are simply marked as deleted, and the actual destruction happens when the major compact function is called, which is described in more detail in section 7.<\/p>\n<p>Files in HFile format accumulate in HDFS, and from time to time, a minor compact process is launched, which just merges small files into larger ones without deleting anything. Over time, this becomes a problem that manifests itself only when reading data (we will return to this shortly). <\/p>\n<p>In addition to the loading process described above, there is a much more efficient procedure that arguably represents the strongest side of this database \u2013 BulkLoad. This involves us manually creating HFiles and placing them on disk, which allows for excellent scalability and achieving quite impressive speeds. In essence, the limitation here is not HBase, but the capabilities of the hardware. Below are the loading results on a cluster consisting of 16 RegionServers and 16 NodeManager YARN (CPU Xeon E5-2680 v4 @ 2.40GHz * 64 threads), HBase version 1.2.0-cdh5.14.2. <\/p>\n<p><img decoding=\"async\" alt=\"Theory and Practice of Using HBase\" src=\"\/wp-content\/uploads\/2020\/01\/4e4452b216eda3269a364ea97c49120f.jpg\" style=\"display:block;margin: 0 auto;\" \/><br \/>\n<br \/>\nIt can be seen that by increasing the number of partitions (regions) in the table, as well as the Spark executors, we observe an increase in loading speed. The speed also depends on the volume of the write operation. Larger blocks provide gains in MB\/sec, while smaller blocks contribute to the number of inserted records per unit of time, assuming other factors are equal. <\/p>\n<p>You can also start loading into two tables simultaneously and achieve double the speed. Below, it can be seen that writing 10 KB blocks to two tables happens at a speed of about 600 Mb\/sec for each (totaling 1275 Mb\/sec), which matches the writing speed to a single table of 623 MB\/sec (see #11 above).<\/p>\n<p><img decoding=\"async\" alt=\"Theory and Practice of Using HBase\" src=\"\/wp-content\/uploads\/2020\/01\/9f1b4fc0c80b797111494d400e9ce332.jpg\" style=\"display:block;margin: 0 auto;\" \/><br \/>\nThe second run with 50 KB records shows that the loading speed increases only marginally, indicating that we are approaching the threshold values. It should be noted that there is virtually no load on HBASE itself; all that is required from it is to first provide data from hbase:meta, and after the HFiles are placed, to flush the BlockCache and save the MemStore buffer to disk, if it is not empty.<\/p>\n<h2>3. Reading data from HBASE<\/h2>\n<p>\nAssuming that the client already has all the information from hbase:meta (see point 2), the request goes directly to the RS where the required key is stored. First, the search occurs in MemCache. Regardless of whether there is data there or not, the search also occurs in the BlockCache buffer and, if necessary, in HFiles. If the data is found in the file, it is placed in BlockCache and will be returned faster in the next request. The search in HFile is relatively quick due to the use of a Bloom filter, meaning that by reading a small amount of data, it can immediately determine whether this file contains the required key, and if not, it moves on to the next one.<\/p>\n<p><img decoding=\"async\" alt=\"Theory and Practice of Using HBase\" src=\"\/wp-content\/uploads\/2020\/01\/a21b1b825a84cfd02507ec334e9452fb.jpg\" style=\"display:block;margin: 0 auto;\" \/><br \/>\nAfter obtaining data from these three sources, the RS forms the response. In particular, it may return several found versions of the object if the client requested versioning.<\/p>\n<h2>4. Data Caching<\/h2>\n<p>\nThe MemStore and BlockCache buffers consume up to 80% of the allocated on-heap memory of the RS (the rest is reserved for service tasks of the RS). If the typical usage mode is such that processes write and immediately read the same data, it makes sense to reduce BlockCache and increase MemStore, as when writing, data does not go into the read cache, thus BlockCache will be utilized less frequently. The BlockCache buffer consists of two parts: LruBlockCache (always on-heap) and BucketCache (usually off-heap or on SSD). BucketCache should be used when there are many read requests that do not fit in LruBlockCache, leading to active Garbage Collector operation. However, one should not expect a radical increase in performance from using the read cache, but we will return to this in point 8.<\/p>\n<p><img decoding=\"async\" alt=\"Theory and Practice of Using HBase\" src=\"\/wp-content\/uploads\/2020\/01\/17d7791a998dd7da747cb7089dce1f10.jpg\" style=\"display:block;margin: 0 auto;\" \/><br \/>\nBlockCache is shared across the entire RS, while MemStore is specific to each table (one for each Column Family).<\/p>\n<p>How <noindex><a rel=\"nofollow\" href=\"https:\/\/blog.cloudera.com\/blog\/2012\/06\/hbase-write-path\/\">is described<\/a><\/noindex> In theory, when writing, data does not go into the cache, and in fact, such parameters as CACHE_DATA_ON_WRITE for the table and 'Cache DATA on Write' for the RS are set to false. However, in practice, if you write data to MemStore, then flush it to disk (effectively clearing it), and then delete the resulting file, executing a get request will still successfully return the data. Moreover, even if BlockCache is completely disabled and the table is filled with new data, then after flushing MemStore to disk, deleting them, and querying from another session, they will still be retrieved from somewhere. Therefore, HBase retains not just data, but also mysterious enigmas.<\/p>\n<pre><code class=\"bash\">hbase(main):001:0&gt; create 'ns:magic', 'cf'\nCreated table ns:magic\nTook 1.1533 seconds\nhbase(main):002:0&gt; put 'ns:magic', 'key1', 'cf:c', 'try_to_delete_me'\nTook 0.2610 seconds\nhbase(main):003:0&gt; flush 'ns:magic'\nTook 0.6161 seconds\nhdfs dfs -mv \/data\/hbase\/data\/ns\/magic\/* \/tmp\/trash\nhbase(main):002:0&gt; get 'ns:magic', 'key1'\n cf:c      timestamp=1534440690218, value=try_to_delete_me\n<\/code><\/pre>\n<p>\nThe parameter 'Cache DATA on Read' is set to false. If you have any ideas, feel free to discuss them in the comments.<\/p>\n<h2>5. Batch processing of data MultiGet\/MultiPut<\/h2>\n<p>\nProcessing single requests (Get\/Put\/Delete) is quite an expensive operation, so it is advisable to combine them whenever possible into a List or List, which allows for significant performance gains. This is especially true for write operations, but there is a catch when it comes to reading. The graph below shows the time taken to read 50,000 entries from MemStore. The reading was performed in a single thread, and the horizontal axis shows the number of keys in the request. It is evident that as the number of keys in a single request increases to a thousand, the execution time decreases, i.e., the speed increases. However, with the default MSLAB mode enabled, after reaching this threshold, performance drastically declines, and the larger the amount of data in the entry, the longer the processing time. <\/p>\n<p><img decoding=\"async\" alt=\"Theory and Practice of Using HBase\" src=\"\/wp-content\/uploads\/2020\/01\/42d3a77d66770a7fed03f83fecdca470.jpg\" style=\"display:block;margin: 0 auto;\" \/><br \/>\n<br \/>\nThe tests were conducted on a virtual machine with 8 cores, using HBase version 2.0.0-cdh6.0.0-beta1.<\/p>\n<p>The MSLAB mode is designed to reduce heap fragmentation caused by mixing new and old generation data. To address this issue, when MSLAB is enabled, data is placed into relatively small chunks and processed in batches. As a result, when the size of the requested data packet exceeds the allocated size, performance drops sharply. On the other hand, disabling this mode is also undesirable because it leads to stops due to GC during intensive data operations. A good solution is to increase the chunk sizes when actively writing via put while simultaneously reading. It is worth noting that the problem does not occur if the flush command is executed after writing, which flushes the MemStore to disk, or if loading is performed using BulkLoad. The table below shows that requests from MemStore with larger data volumes (and the same number) lead to slowdowns. However, by increasing the chunksize, processing time can be returned to normal.<\/p>\n<p><img decoding=\"async\" alt=\"Theory and Practice of Using HBase\" src=\"\/wp-content\/uploads\/2020\/01\/0076215bd171257548f8a4c4d4229ba0.jpg\" style=\"display:block;margin: 0 auto;\" \/><br \/>\nIn addition to increasing chunksize, data partitioning by regions, i.e., table splitting, helps. This results in fewer requests for each region, and if they fit within a cell, the response remains good.<\/p>\n<h2>6. Table Partitioning Strategy by Regions (Splitting)<\/h2>\n<p>\nSince HBase is a key-value store and partitioning is performed by key, it is crucial to evenly distribute data across all regions. For example, partitioning such a table into three parts will result in data being divided into three regions:<\/p>\n<p><img decoding=\"async\" alt=\"Theory and Practice of Using HBase\" src=\"\/wp-content\/uploads\/2020\/01\/a811ae81e77b48dbf28ed86dbdc35739.jpg\" style=\"display:block;margin: 0 auto;\" \/><br \/>\nSometimes, this leads to a significant slowdown if the data being loaded later consists of long values that mostly start with the same digit, for example:<\/p>\n<p>1000001<br \/>\n1000002<br \/>\n\u2026<br \/>\n1100003<\/p>\n<p>Since keys are stored as byte arrays, all of them will start similarly and belong to region #1, which holds that range of keys. There are several partitioning strategies:<\/p>\n<p>HexStringSplit \u2013 Converts the key into a string with hexadecimal encoding in the range \"00000000\" =&gt; \"FFFFFFFF\" and fills with zeros on the left.<\/p>\n<p>UniformSplit \u2013 Converts the key into a byte array with hexadecimal encoding in the range \"00\" =&gt; \"FF\" and fills with zeros on the right.<\/p>\n<p>Additionally, you can specify any range or set of keys for partitioning and configure auto-splitting. However, one of the simplest and most effective approaches is UniformSplit, utilizing hash concatenation, for example, the upper pair of bytes from running the key through the CRC32(rowkey) function and the rowkey itself:<\/p>\n<p>hash + rowkey<\/p>\n<p>Then, all data will be evenly distributed across regions. When reading, the first two bytes are simply discarded, leaving the original key. Moreover, the RS monitors the amount of data and keys in the region, and if limits are exceeded, it automatically splits it into parts. <\/p>\n<h2>7. Fault Tolerance and Data Locality<\/h2>\n<p>\nSince each set of keys is managed by only one region, addressing issues related to RS failures or decommissioning involves storing all necessary data in HDFS. When an RS fails, the master detects this through the absence of a heartbeat on the ZooKeeper node. It then assigns the affected region to another RS, and because HFiles are stored in a distributed file system, the new host reads them and continues serving the data. However, since some data may still be in MemStore and not yet written to HFiles, the WAL is used to restore the operation history, with these logs also stored in HDFS. After applying the changes, the RS is able to respond to requests; however, the relocation results in some data and the processes servicing it being on different nodes, thus decreasing locality. <\/p>\n<p>The solution to the problem is major compaction \u2013 this procedure moves files to the nodes responsible for them (where their regions are located), resulting in a significant increase in load on the network and disks during this process. However, this ultimately speeds up data access noticeably. Additionally, major compaction consolidates all HFiles into a single file within the region and cleans up data according to table settings. For instance, one can specify the number of object versions to retain or the time after which the object is physically deleted.<\/p>\n<p>This procedure can have a very positive impact on HBase performance. The picture below shows how performance degraded as a result of intensive data writing. Here, it's evident that 40 threads were writing to a single table while 40 other threads simultaneously read data. The writing threads generated more and more HFiles, which were read by the other threads. Consequently, more data needed to be removed from memory and eventually, GC kicked in, which practically paralyzed all operations. Initiating major compaction helped clean up the generated debris and restore performance.<\/p>\n<p><img decoding=\"async\" alt=\"Theory and Practice of Using HBase\" src=\"\/wp-content\/uploads\/2020\/01\/6c93b2c10c197663785d3de0bb185481.jpg\" style=\"display:block;margin: 0 auto;\" \/><br \/>\nThe test was conducted on 3 DataNodes and 4 RS (CPU Xeon E5-2680 v4 @ 2.40GHz * 64 threads). HBase version 1.2.0-cdh5.14.2<\/p>\n<p>It is worth noting that the major compaction was performed on a \"live\" table, where data was actively being written and read. There were claims in the network that this could lead to incorrect responses when reading data. To verify this, a process was initiated that generated new data and wrote it to the table. Immediately afterwards, it read the data and checked if the retrieved value matched what had been written. During the operation of this process, major compaction was initiated about 200 times without any failures recorded. The problem may occur rarely and only under high load, so it is safer to schedule the stopping of read and write processes and perform the cleanup to avoid such GC drops.<\/p>\n<p>Moreover, major compaction does not affect the state of MemStore, to flush it to disk and compact it, flush must be used (connection.getAdmin().flush(TableName.valueOf(tblName))).<\/p>\n<h2>8. Settings and Performance<\/h2>\n<p>\nAs mentioned earlier, HBase shows the greatest success where it has nothing to do during BulkLoad. However, this applies to most systems and people. Yet, this tool is primarily suited for bulk data loading, while if the process requires performing many competing read and write requests, the aforementioned Get and Put commands are used. To determine the optimal parameters, runs were conducted with different combinations of table parameters and settings:<\/p>\n<ul>\n<li>10 threads were launched simultaneously 3 times in a row (let's call this a thread block). <\/li>\n<li>The execution time of all threads in the block was averaged and served as the final result of the block's operation.<\/li>\n<li>All threads worked with the same table. <\/li>\n<li>Before each launch of the thread block, a major compaction was performed.<\/li>\n<li>Each block performed only one of the following operations: <\/li>\n<\/ul>\n<p>\n \u2014 Put<br \/>\n \u2014 Get<br \/>\n \u2014 Get+Put<\/p>\n<ul>\n<li>Each block executed 50,000 repetitions of its operation.<\/li>\n<li>The record size in the block was 100 bytes, 1000 bytes, or 10000 bytes (random).<\/li>\n<li>Blocks were launched with varying numbers of requested keys (either one key or ten).<\/li>\n<li>Blocks were executed under different table settings. The parameters were adjusted:<\/li>\n<\/ul>\n<p>\n \u2014 BlockCache = enabled or disabled<br \/>\n \u2014 BlockSize = 65 KB or 16 KB<br \/>\n \u2014 Partitions = 1, 5, or 30<br \/>\n \u2014 MSLAB = enabled or disabled<\/p>\n<p>Thus, the block looks like this:<\/p>\n<p>a. The MSLAB mode was toggled on\/off.<br \/>\nb. A table was created with the following parameters: BlockCache = true\/none, BlockSize = 65\/16 Kb, Partitions = 1\/5\/30. <br \/>\nc. GZ compression was set.<br \/>\nd. Ten threads were started simultaneously performing 1\/10 put\/get\/get+put operations on this table with records of 100\/1000\/10000 bytes, executing 50,000 requests in a row (keys random).<br \/>\ne. Item d was repeated three times.<br \/>\nf. The runtime of all threads was averaged. <\/p>\n<p>All possible combinations were tested. It was predictable that as the record size increased, the speed would decrease or that disabling caching would lead to a slowdown. However, the goal was to understand the degree and significance of the influence of each parameter, so the collected data was fed into a linear regression function, which allows for validity assessment using t-statistics. Below are the results of the blocks performing Put operations. The full set of combinations is 2*2*3*2*3 = 144 options + 72 since some were executed twice. Therefore, a total of 216 runs:<\/p>\n<p><img decoding=\"async\" alt=\"Theory and Practice of Using HBase\" src=\"\/wp-content\/uploads\/2020\/01\/7a13c7a9b3ff1859976800f5d45cd51b.jpg\" style=\"display:block;margin: 0 auto;\" \/><br \/>\nTesting was conducted on a mini-cluster consisting of 3 DataNodes and 4 RS (CPU Xeon E5-2680 v4 @ 2.40GHz * 64 threads). HBase version 1.2.0-cdh5.14.2.<\/p>\n<p>The highest insertion speed of 3.7 seconds was achieved with the MSLAB mode turned off, on a table with one partition, with BlockCache enabled, BlockSize = 16, records of 100 bytes in batches of 10.<br \/>\nThe lowest insertion speed of 82.8 seconds was achieved with the MSLAB mode turned on, on a table with one partition, with BlockCache enabled, BlockSize = 16, records of 10000 bytes one by one.<\/p>\n<p>Now let's look at the model. We see good model quality by R2, but it is quite clear that extrapolation here is contraindicated. The real behavior of the system when changing parameters will be nonlinear; this model is not for predictions but for understanding what happened within the given parameters. For example, here we see from the Student criterion that for the Put operation, the parameters BlockSize and BlockCache are not significant (which is generally quite predictable):<\/p>\n<p><img decoding=\"async\" alt=\"Theory and Practice of Using HBase\" src=\"\/wp-content\/uploads\/2020\/01\/2fb65d1809d53f2b47f4f8829a9efc65.jpg\" style=\"display:block;margin: 0 auto;\" \/><br \/>\nIt's somewhat unexpected that increasing the number of partitions leads to a decrease in performance (we have already seen a positive impact from increasing the number of partitions during BulkLoad), though it is explainable. Firstly, processing requires forming queries for 30 regions instead of one, and the data volume is not sufficient to gain an advantage. Secondly, the overall operation time is determined by the slowest RS, and since the number of DataNodes is less than the number of RS, some regions have zero locality. Now, let's look at the top five leaders:<\/p>\n<p><img decoding=\"async\" alt=\"Theory and Practice of Using HBase\" src=\"\/wp-content\/uploads\/2020\/01\/8ad7e832879156eaa96e630458405cdc.jpg\" style=\"display:block;margin: 0 auto;\" \/><br \/>\nNow let's evaluate the results of executing the Get blocks:<\/p>\n<p><img decoding=\"async\" alt=\"Theory and Practice of Using HBase\" src=\"\/wp-content\/uploads\/2020\/01\/8a5dd45422c74e8815011d7e9b805ccb.jpg\" style=\"display:block;margin: 0 auto;\" \/><br \/>\nThe number of partitions has lost significance, likely because the data is well cached, and the read cache is the most significant (statistically) parameter. Naturally, increasing the number of messages in the request is also quite beneficial for performance. The best results are:<\/p>\n<p><img decoding=\"async\" alt=\"Theory and Practice of Using HBase\" src=\"\/wp-content\/uploads\/2020\/01\/9d237f9fdbeaf5412daec2fc2fa24b01.jpg\" style=\"display:block;margin: 0 auto;\" \/><br \/>\nFinally, let's look at the block model that executed get first and then put:<\/p>\n<p><img decoding=\"async\" alt=\"Theory and Practice of Using HBase\" src=\"\/wp-content\/uploads\/2020\/01\/75137207a4a69acccad036882794094b.jpg\" style=\"display:block;margin: 0 auto;\" \/><br \/>\nAll parameters here are significant. And the results of the leaders:<\/p>\n<p><img decoding=\"async\" alt=\"Theory and Practice of Using HBase\" src=\"\/wp-content\/uploads\/2020\/01\/c166933d81e86f36958e3af58297f876.jpg\" style=\"display:block;margin: 0 auto;\" \/><\/p>\n<h2>9. Load Testing<\/h2>\n<p>\nFinally, let's run a reasonably substantial load, but it\u2019s always more interesting when there\u2019s something to compare against. On the DataStax site\u2014the key developer of Cassandra\u2014there are <noindex><a rel=\"nofollow\" href=\"https:\/\/www.datastax.com\/wp-content\/themes\/datastax-2014-08\/files\/NoSQL_Benchmarks_EndPoint.pdf\">results<\/a><\/noindex> test results of a number of NoSQL storage systems, including HBase version 0.98.6-1. The loading was conducted with 40 threads, data size of 100 bytes, SSD disks. The results of the Read-Modify-Write operations testing showed these results.<\/p>\n<p><img decoding=\"async\" alt=\"Theory and Practice of Using HBase\" src=\"\/wp-content\/uploads\/2020\/01\/521e9dff60f462e6dabebc1234e028be.jpg\" style=\"display:block;margin: 0 auto;\" \/><br \/>\n As far as I understand, reading was conducted in blocks of 100 records, and the DataStax test for 16 HBase nodes showed a performance of 10,000 operations per second. <\/p>\n<p>It's fortunate that our cluster also has 16 nodes, but not so fortunate that each has 64 cores (threads), while in the DataStax test there were only 4. On the other hand, they have SSDs, whereas we have HDDs, and a newer version of HBase, with CPU utilization during the load hardly increasing significantly (visually by 5-10 percent). Nonetheless, we'll try to start with this configuration. The default table settings are being used, with reads occurring randomly in the key range from 0 to 50 million (i.e., essentially a new set each time). The table contains 50 million records divided into 64 partitions. Keys are hashed using crc32. The table settings are default, and MSLAB is enabled. We're running 40 threads, each reading a set of 100 random keys and immediately writing back 100 bytes generated for those keys. <\/p>\n<p><img decoding=\"async\" alt=\"Theory and Practice of Using HBase\" src=\"\/wp-content\/uploads\/2020\/01\/776490f01121cc434135f733311b7722.jpg\" style=\"display:block;margin: 0 auto;\" \/><br \/>\n Stand: 16 DataNodes and 16 RS (CPU Xeon E5-2680 v4 @ 2.40GHz * 64 threads). HBase Version 1.2.0-cdh5.14.2.<\/p>\n<p>The average result is closer to 40,000 operations per second, which is significantly better than in the DataStax test. However, for experimental purposes, we can alter the conditions slightly. It's rather unlikely that all work will be done exclusively with a single table, or only with unique keys. Let's assume there is a certain 'hot' set of keys generating the main load. Therefore, we'll try to create load with larger records (10 KB), also in batches of 100, across 4 different tables while limiting the requested key range to 50,000. The chart below shows the launch of 40 threads, each reading a set of 100 keys and immediately writing random 10 KB back to those keys. <\/p>\n<p><img decoding=\"async\" alt=\"Theory and Practice of Using HBase\" src=\"\/wp-content\/uploads\/2020\/01\/30d29f1b98663e3c07cb2be20e93876a.jpg\" style=\"display:block;margin: 0 auto;\" \/><br \/>\nStand: 16 DataNodes and 16 RS (CPU Xeon E5-2680 v4 @ 2.40GHz * 64 threads). HBase Version 1.2.0-cdh5.14.2.<\/p>\n<p>During the load, major compaction was launched several times, as shown above without this procedure, performance would gradually degrade. However, during execution, additional load also arises. The drops in performance are caused by various reasons. Sometimes threads finished their work and while they were restarting, there was a pause; sometimes, external applications created load on the cluster.<\/p>\n<p>Reading and writing simultaneously is one of the most challenging scenarios for HBase. By only making small put requests, such as 100 bytes, and batching them in groups of 10-50 thousand, it is possible to achieve hundreds of thousands of operations per second; the same applies to read-only requests. It is noteworthy that the results are significantly better than those obtained by DataStax, primarily due to the batch requests of 50 thousand.<\/p>\n<p><img decoding=\"async\" alt=\"Theory and Practice of Using HBase\" src=\"\/wp-content\/uploads\/2020\/01\/412bda50a55093224169f048ea2f863a.jpg\" style=\"display:block;margin: 0 auto;\" \/><br \/>\nStand: 16 DataNodes and 16 RS (CPU Xeon E5-2680 v4 @ 2.40GHz * 64 threads). HBase Version 1.2.0-cdh5.14.2.<\/p>\n<h2>10. Conclusions<\/h2>\n<p>\nThis system is quite flexible, but the impact of numerous parameters remains largely unknown. Some have been tested but were not included in the final test set. For example, preliminary experiments indicated a minor significance of the DATA_BLOCK_ENCODING parameter, which encodes information using values from neighboring cells, which is quite understandable for randomly generated data. In cases involving many repeating objects, the gains can be substantial. Overall, it can be said that HBase appears to be a serious and thoughtful database that can be quite efficient when handling large blocks of data, especially if there is an opportunity to stagger read and write processes in time.<\/p>\n<p>If you find that something is inadequately covered, I am ready to provide more details. We encourage sharing your experiences or discussing if you disagree with something.<br \/>\n<br \/>Source: <a content=\"nofollow\" rel=\"nofollow\" href=\"https:\/\/habr.com\/ru\/company\/sberbank\/blog\/420425\/\">habr.com<\/a><\/p>","protected":false,"gt_translate_keys":[{"key":"rendered","format":"html"}]},"excerpt":{"rendered":"<p>\u0414\u043e\u0431\u0440\u044b\u0439 \u0434\u0435\u043d\u044c! \u041c\u0435\u043d\u044f \u0437\u043e\u0432\u0443\u0442 \u0414\u0430\u043d\u0438\u043b \u041b\u0438\u043f\u043e\u0432\u043e\u0439, \u043d\u0430\u0448\u0430 \u043a\u043e\u043c\u0430\u043d\u0434\u0430 \u0432 \u0421\u0431\u0435\u0440\u0442\u0435\u0445\u0435 \u043d\u0430\u0447\u0430\u043b\u0430 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c HBase \u0432 \u043a\u0430\u0447\u0435\u0441\u0442\u0432\u0435 \u0445\u0440\u0430\u043d\u0438\u043b\u0438\u0449\u0430 \u043e\u043f\u0435\u0440\u0430\u0442\u0438\u0432\u043d\u044b\u0445 \u0434\u0430\u043d\u043d\u044b\u0445. \u0412 \u0445\u043e\u0434\u0435 \u0435\u0433\u043e \u0438\u0437\u0443\u0447\u0435\u043d\u0438\u044f \u043d\u0430\u043a\u043e\u043f\u0438\u043b\u0441\u044f \u043e\u043f\u044b\u0442, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u0437\u0430\u0445\u043e\u0442\u0435\u043b\u043e\u0441\u044c \u0441\u0438\u0441\u0442\u0435\u043c\u0430\u0442\u0438\u0437\u0438\u0440\u043e\u0432\u0430\u0442\u044c \u0438 \u043e\u043f\u0438\u0441\u0430\u0442\u044c (\u043d\u0430\u0434\u0435\u0435\u043c\u0441\u044f, \u0447\u0442\u043e \u043c\u043d\u043e\u0433\u0438\u043c \u0431\u0443\u0434\u0435\u0442 \u043f\u043e\u043b\u0435\u0437\u043d\u043e). \u0412\u0441\u0435 \u043f\u0440\u0438\u0432\u0435\u0434\u0435\u043d\u043d\u044b\u0435 \u043d\u0438\u0436\u0435 \u044d\u043a\u0441\u043f\u0435\u0440\u0438\u043c\u0435\u043d\u0442\u044b \u043f\u0440\u043e\u0432\u043e\u0434\u0438\u043b\u0438\u0441\u044c \u0441 \u0432\u0435\u0440\u0441\u0438\u044f\u043c\u0438 HBase 1.2.0-cdh5.14.2 \u0438 2.0.0-cdh6.0.0-beta1. \u041e\u0431\u0449\u0430\u044f \u0430\u0440\u0445\u0438\u0442\u0435\u043a\u0442\u0443\u0440\u0430 \u0417\u0430\u043f\u0438\u0441\u044c \u0434\u0430\u043d\u043d\u044b\u0445 \u0432 HBASE \u0427\u0442\u0435\u043d\u0438\u0435 \u0434\u0430\u043d\u043d\u044b\u0445 \u0438\u0437 HBASE [&hellip;]<\/p>\n","protected":false,"gt_translate_keys":[{"key":"rendered","format":"html"}]},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[688],"tags":[],"class_list":["post-55302","post","type-post","status-publish","format-standard","hentry","category-administrirovanie"],"aioseo_notices":[],"aioseo_head":"\n\t\t<!-- All in One SEO 5.0.2 - aioseo.com -->\n\t<meta name=\"description\" content=\"\u0414\u043e\u0431\u0440\u044b\u0439 \u0434\u0435\u043d\u044c! \u041c\u0435\u043d\u044f \u0437\u043e\u0432\u0443\u0442 \u0414\u0430\u043d\u0438\u043b \u041b\u0438\u043f\u043e\u0432\u043e\u0439, \u043d\u0430\u0448\u0430 \u043a\u043e\u043c\u0430\u043d\u0434\u0430 \u0432 \u0421\u0431\u0435\u0440\u0442\u0435\u0445\u0435 \u043d\u0430\u0447\u0430\u043b\u0430 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c HBase \u0432 \u043a\u0430\u0447\u0435\u0441\u0442\u0432\u0435 \u0445\u0440\u0430\u043d\u0438\u043b\u0438\u0449\u0430 \u043e\u043f\u0435\u0440\u0430\u0442\u0438\u0432\u043d\u044b\u0445 \u0434\u0430\u043d\u043d\u044b\u0445.\" \/>\n\t<meta name=\"robots\" content=\"max-image-preview:large\" \/>\n\t<meta name=\"author\" content=\"Yuri Gagarin\"\/>\n\t<link rel=\"canonical\" href=\"https:\/\/prohoster.info\/en\/blog\/administrirovanie\/teoriya-i-praktika-ispolzovaniya-hbase\" \/>\n\t<meta name=\"generator\" content=\"All in One SEO (AIOSEO) 5.0.2\" \/>\n\t\t<meta property=\"og:locale\" content=\"en_US\" \/>\n\t\t<meta property=\"og:site_name\" content=\"ProHoster | \u041a\u0443\u043f\u0438\u0442\u044c \u043d\u0430\u0434\u0435\u0436\u043d\u044b\u0439 \u0445\u043e\u0441\u0442\u0438\u043d\u0433 \u0434\u043b\u044f \u0441\u0430\u0439\u0442\u043e\u0432 \u0441 \u0437\u0430\u0449\u0438\u0442\u043e\u0439 \u043e\u0442 DDoS, VPS VDS \u0441\u0435\u0440\u0432\u0435\u0440\u044b\" \/>\n\t\t<meta property=\"og:type\" content=\"article\" \/>\n\t\t<meta property=\"og:title\" content=\"\ud83e\udd47\u0422\u0435\u043e\u0440\u0438\u044f \u0438 \u043f\u0440\u0430\u043a\u0442\u0438\u043a\u0430 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u043d\u0438\u044f HBase | ProHoster\" \/>\n\t\t<meta property=\"og:description\" content=\"\u0414\u043e\u0431\u0440\u044b\u0439 \u0434\u0435\u043d\u044c! \u041c\u0435\u043d\u044f \u0437\u043e\u0432\u0443\u0442 \u0414\u0430\u043d\u0438\u043b \u041b\u0438\u043f\u043e\u0432\u043e\u0439, \u043d\u0430\u0448\u0430 \u043a\u043e\u043c\u0430\u043d\u0434\u0430 \u0432 \u0421\u0431\u0435\u0440\u0442\u0435\u0445\u0435 \u043d\u0430\u0447\u0430\u043b\u0430 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c HBase \u0432 \u043a\u0430\u0447\u0435\u0441\u0442\u0432\u0435 \u0445\u0440\u0430\u043d\u0438\u043b\u0438\u0449\u0430 \u043e\u043f\u0435\u0440\u0430\u0442\u0438\u0432\u043d\u044b\u0445 \u0434\u0430\u043d\u043d\u044b\u0445.\" \/>\n\t\t<meta property=\"og:url\" content=\"https:\/\/prohoster.info\/en\/blog\/administrirovanie\/teoriya-i-praktika-ispolzovaniya-hbase\" \/>\n\t\t<meta property=\"og:image\" content=\"https:\/\/prohoster.info\/wp-content\/uploads\/2021\/11\/logo-350.jpg\" \/>\n\t\t<meta property=\"og:image:secure_url\" content=\"https:\/\/prohoster.info\/wp-content\/uploads\/2021\/11\/logo-350.jpg\" \/>\n\t\t<meta property=\"og:image:width\" content=\"350\" \/>\n\t\t<meta property=\"og:image:height\" content=\"350\" \/>\n\t\t<meta property=\"article:published_time\" content=\"2020-01-16T21:00:00+00:00\" \/>\n\t\t<meta property=\"article:modified_time\" content=\"2020-02-18T11:03:24+00:00\" \/>\n\t\t<meta property=\"article:publisher\" content=\"https:\/\/www.facebook.com\/prohoster\" \/>\n\t\t<meta property=\"article:author\" content=\"https:\/\/www.facebook.com\/prohoster\" \/>\n\t\t<!-- All in One SEO -->\n\n","aioseo_head_json":{"title":"\ud83e\udd47Theory and Practice of Using HBase | ProHoster","description":"Good day! My name is Danil Lipovoy, and our team at Sbertech has started using HBase as an operational data storage solution.","canonical_url":"https:\/\/prohoster.info\/en\/blog\/administrirovanie\/teoriya-i-praktika-ispolzovaniya-hbase","robots":"max-image-preview:large","keywords":"","webmasterTools":{"miscellaneous":""},"schema":null,"og:locale":"en_US","og:site_name":"ProHoster | \u041a\u0443\u043f\u0438\u0442\u044c \u043d\u0430\u0434\u0435\u0436\u043d\u044b\u0439 \u0445\u043e\u0441\u0442\u0438\u043d\u0433 \u0434\u043b\u044f \u0441\u0430\u0439\u0442\u043e\u0432 \u0441 \u0437\u0430\u0449\u0438\u0442\u043e\u0439 \u043e\u0442 DDoS, VPS VDS \u0441\u0435\u0440\u0432\u0435\u0440\u044b","og:type":"article","og:title":"\ud83e\udd47\u0422\u0435\u043e\u0440\u0438\u044f \u0438 \u043f\u0440\u0430\u043a\u0442\u0438\u043a\u0430 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u043d\u0438\u044f HBase | ProHoster","og:description":"\u0414\u043e\u0431\u0440\u044b\u0439 \u0434\u0435\u043d\u044c! \u041c\u0435\u043d\u044f \u0437\u043e\u0432\u0443\u0442 \u0414\u0430\u043d\u0438\u043b \u041b\u0438\u043f\u043e\u0432\u043e\u0439, \u043d\u0430\u0448\u0430 \u043a\u043e\u043c\u0430\u043d\u0434\u0430 \u0432 \u0421\u0431\u0435\u0440\u0442\u0435\u0445\u0435 \u043d\u0430\u0447\u0430\u043b\u0430 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c HBase \u0432 \u043a\u0430\u0447\u0435\u0441\u0442\u0432\u0435 \u0445\u0440\u0430\u043d\u0438\u043b\u0438\u0449\u0430 \u043e\u043f\u0435\u0440\u0430\u0442\u0438\u0432\u043d\u044b\u0445 \u0434\u0430\u043d\u043d\u044b\u0445.","og:url":"https:\/\/prohoster.info\/en\/blog\/administrirovanie\/teoriya-i-praktika-ispolzovaniya-hbase","og:image":"https:\/\/prohoster.info\/wp-content\/uploads\/2021\/11\/logo-350.jpg","og:image:secure_url":"https:\/\/prohoster.info\/wp-content\/uploads\/2021\/11\/logo-350.jpg","og:image:width":350,"og:image:height":350,"article:published_time":"2020-01-16T21:00:00+00:00","article:modified_time":"2020-02-18T11:03:24+00:00","article:publisher":"https:\/\/www.facebook.com\/prohoster","article:author":"https:\/\/www.facebook.com\/prohoster"},"aioseo_meta_data":{"post_id":"55302","title":null,"description":null,"keywords":null,"keyphrases":null,"primary_term":null,"canonical_url":null,"og_title":null,"og_description":null,"og_object_type":"default","og_image_type":"default","og_image_url":null,"og_image_width":null,"og_image_height":null,"og_image_custom_url":null,"og_image_custom_fields":null,"og_video":null,"og_custom_url":null,"og_article_section":null,"og_article_tags":null,"twitter_use_og":false,"twitter_card":"default","twitter_image_type":"default","twitter_image_url":null,"twitter_image_custom_url":null,"twitter_image_custom_fields":null,"twitter_title":null,"twitter_description":null,"schema":{"blockGraphs":[],"customGraphs":[],"default":{"data":{"Article":[],"Course":[],"Dataset":[],"FAQPage":[],"Movie":[],"Person":[],"Product":[],"ProductReview":[],"Car":[],"Recipe":[],"Service":[],"SoftwareApplication":[],"WebPage":[]},"graphName":"","isEnabled":true},"graphs":[]},"schema_type":null,"schema_type_options":null,"pillar_content":false,"robots_default":true,"robots_noindex":false,"robots_noarchive":false,"robots_nosnippet":false,"robots_nofollow":false,"robots_noimageindex":false,"robots_noodp":false,"robots_notranslate":false,"robots_max_snippet":null,"robots_max_videopreview":null,"robots_max_imagepreview":"large","priority":null,"frequency":null,"local_seo":null,"seo_analyzer_scan_date":null,"breadcrumb_settings":null,"limit_modified_date":false,"reviewed_by":null,"ai":null,"created":"2021-02-28 19:46:28","updated":"2022-09-28 08:11:41","focus_keyword":null,"additional_keywords":null,"truseo_locale":null},"gt_translate_keys":[{"key":"link","format":"url"}],"_links":{"self":[{"href":"https:\/\/prohoster.info\/en\/wp-json\/wp\/v2\/posts\/55302","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/prohoster.info\/en\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/prohoster.info\/en\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/prohoster.info\/en\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/prohoster.info\/en\/wp-json\/wp\/v2\/comments?post=55302"}],"version-history":[{"count":0,"href":"https:\/\/prohoster.info\/en\/wp-json\/wp\/v2\/posts\/55302\/revisions"}],"wp:attachment":[{"href":"https:\/\/prohoster.info\/en\/wp-json\/wp\/v2\/media?parent=55302"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/prohoster.info\/en\/wp-json\/wp\/v2\/categories?post=55302"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/prohoster.info\/en\/wp-json\/wp\/v2\/tags?post=55302"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}