Facebook created MyRocks, a storage engine for MySQL, based on their storage RocksDB. A working implementation of MyRocks in the form of MySQL 5.6 source with Facebook patches is open sourced and hosted on GitHub — https://github.com/facebook/mysql-5.6. As an alternative to InnoDB, RocksDB offers several advantages:
All this greatly increases the transaction speed on the HDD, reduces wear on the SSD, and accelerates replication.
MariaDB and Percona are already working to integrate MyRocks into their MySQL forks: Facebook MyRocks at MariaDB, Announcing MyRocks in Percona Server for MySQL. MariaDB announced that MyRocks will be available in release candidate 10.2 this winter. Jetware has included Facebook’s original MyRocks implementation into MySQL alternatives.
Benchmarking on synthetic tests shows impressive results. Depending on the type of storage device, the gain in speed is from 20% to 10 times. LinkBench test results are available in the publications of Yoshinori Matsunobu MyRocks: A space- and write-optimized MySQL database and on Mark Callaghan’s blog, e.g., MyRocks: use less IO on writes to have more IO for reads. These tests are mainly oriented toward large data sets (tens or hundreds of gigabytes) and powerful machines.
In addition to the synthetic tests and benchmarking on large data sets, we decided to measure and evaluate the performance benefits for typical Web applications and small- and medium-sized sites.
First, we tested Redmine. We know how it works and we use it actively in our development, therefore, testing also has practical value for us: if the result is good, then we’ll switch to MyRocks.
We use Redmine 3.3.1 on Ruby 2.3.1 (default configuration) without additional plug-ins.
As database servers, we use:
All binaries are built by GCC 4.9.3, with recommended build and optimization settings.
Operating system - Ubuntu 14.04 x86_64, Linux kernel 3.13.0. Filesystem - ext4.
The database is filled before performing the tests with pre-generated projects, users, and tasks:
Most real-use cases with Redmine have sizes ranging between small and large datasets. Cases of the giant dataset level are much rarer.
File space usage
size | myrocks-mysqld-5.6 | mysqld-5.6.31 | mariadb-mysqld-10.1.16 |
---|---|---|---|
small | 6.715 | 30.527 | 30.113 |
large | 61.168 | 123.246 | 122.832 |
giant | 596.523 | 913.168 | 912.754 |
The bars show the volume of the used space for different databases and different datasets (less is better).
We imitate Redmine’s work in conditions similar to the cloud provider or on the office server. For this purpose, we allocate not the entire physical server, but a virtual machine with far fewer resources, and simulate different disk system loads from neighbors. We use Xen 4.6 as the virtualization platform with Linux kernel 3.16.7 in dom0. The storage device is partitioned using LVM - simple linear, without thin provision and snapshots. The volume is located in the middle of the HDD.
We use three configurations:
We test the speed of the most commonly used Redmine operations: create issue, add comments to issue, and change the status and the assigned person of issue. Using these operations, we create and test two tasks:
Create issue
Create issue in a project by one of the project members, assign the issue to another member of the project, and add 10 comments to the issue from other project members.
Process 10 issues
Select some random user, get 10 issues assigned to this user, set them all to the “In progress” status, then change each one to “Resolved” status and assign it back to the author.
Benchmarks are carried out with 1, 2 and 4 parallel Redmine processes.
The load is created using the fio
utility that reads and writes 50/50 random blocks in the remaining part of the disk. We simulate several disk load levels, which represent the typical use cases of virtual machines from public cloud and VPS providers, or when running multiple virtual machines on your own server under VMWare, Hyper-V, KVM, or XenServer.
To simulate partial load, we run fio with IOPS limits using the --rate_iops
option and measure the disk utilization. With 100% single-threaded load, we have about 80 IOPS. A limit of 14 IOPS creates 25% load utilization. The greater load is simulated by increasing the IO flow number using the --iodepth
option.
Depending on the number of neighboring virtual machines and the nature of their work and the load peaks, the disk load may be quite different on public cloud, VPS, or your own server. Therefore, we performed tests in the absence of external loads, with little single-flow load (14 IOPS, 25%) and with the full external load, with 1, 2, and 4 flows.
We measure the full time for each Redmine operation execution on a large number of operations and compare the average execution time. The first 10% of the results are ignored as they warm up the system. The last 10% of the results are ignored in order to avoid tail distortion due to the different completion times of parallel processes.
Measurements are made with different condition combinations:
Execution time is measured for all three databases, MyRocks MySQL, MySQL, and MariaDB. We also calculate the difference in MyRocks MySQL speed relative to MySQL and MariaDB. The collected data are presented as charts.
Operation execution time charts
1) Create issue; 2) Process 10 issues
parallel/load | myrocks-mysqld-5.6 | mysqld-5.6.31 | mariadb-mysqld-10.1.16 | mysqld-5.6.31 slower | mariadb-mysqld-10.1.16 slower |
---|---|---|---|---|---|
1/0 | 0.018 | 0.022 | 0.022 | 1.237 | 1.233 |
1/25% | 0.018 | 0.025 | 0.026 | 1.378 | 1.398 |
1/x1 | 0.019 | 0.023 | 0.023 | 1.261 | 1.248 |
1/x2 | 0.019 | 0.033 | 0.034 | 1.802 | 1.828 |
1/x4 | 0.018 | 0.038 | 0.038 | 2.054 | 2.071 |
2/0 | 0.02 | 0.024 | 0.024 | 1.196 | 1.199 |
2/25% | 0.019 | 0.026 | 0.027 | 1.362 | 1.388 |
2/x1 | 0.02 | 0.025 | 0.025 | 1.255 | 1.27 |
2/x2 | 0.02 | 0.038 | 0.037 | 1.885 | 1.819 |
2/x4 | 0.02 | 0.044 | 0.043 | 2.188 | 2.154 |
4/0 | 0.022 | 0.049 | 0.031 | 2.203 | 1.385 |
4/25% | 0.024 | 0.08 | 0.08 | 3.407 | 3.384 |
4/x1 | 0.024 | 0.133 | 0.122 | 5.489 | 5.049 |
4/x2 | 0.023 | 0.158 | 0.16 | 6.777 | 6.845 |
4/x4 | 0.023 | 0.227 | 0.226 | 9.713 | 9.651 |
parallel/load | myrocks-mysqld-5.6 | mysqld-5.6.31 | mariadb-mysqld-10.1.16 | mysqld-5.6.31 slower | mariadb-mysqld-10.1.16 slower |
---|---|---|---|---|---|
1/0 | 0.245 | 0.283 | 0.289 | 1.155 | 1.18 |
1/25% | 0.25 | 0.304 | 0.303 | 1.212 | 1.209 |
1/x1 | 0.263 | 0.292 | 0.293 | 1.112 | 1.115 |
1/x2 | 0.252 | 0.387 | 0.397 | 1.532 | 1.573 |
1/x4 | 0.251 | 0.427 | 0.445 | 1.702 | 1.774 |
2/0 | 0.264 | 0.311 | 0.305 | 1.178 | 1.156 |
2/25% | 0.263 | 0.308 | 0.331 | 1.174 | 1.259 |
2/x1 | 0.266 | 0.307 | 0.307 | 1.153 | 1.153 |
2/x2 | 0.268 | 0.42 | 0.399 | 1.568 | 1.489 |
2/x4 | 0.273 | 0.478 | 0.469 | 1.754 | 1.72 |
4/0 | 0.295 | 0.484 | 0.338 | 1.639 | 1.145 |
4/25% | 0.311 | 0.659 | 0.708 | 2.118 | 2.274 |
4/x1 | 0.313 | 1.033 | 0.993 | 3.3 | 3.173 |
4/x2 | 0.327 | 1.344 | 1.311 | 4.11 | 4.009 |
4/x4 | 0.308 | 2.149 | 2.245 | 6.984 | 7.295 |
The bars show the full operation execution time (less is better). The lines show how many times the MySQL or MariaDB server was slower than MyRocks MySQL.
Minimum load: one Redmine process and the absence of external load. Maximum load - four Redmine processes and four full flows of the external disk load.
We see that the time to create an issue for MyRocks varies slightly when increasing load to the maximum, from 0.018 seconds to 0.023 seconds at 23%. For MySQL and MariaDB, the minimum issue creating time is 0.022 seconds, which increases tenfold to 0.23 seconds at maximum load. At minimum load, MySQL and MariaDB are slower at 24% than MyRocks; at maximum load, they are 9.5 times slower.
Issue processing time for MyRocks increases from 0.245 seconds with a minimum load to 0.327 seconds at maximum load, at 33%. For MySQL and MariaDB, the minimum issue processing time increases about seven times, from 0.283 seconds at minimum load to 2.245 seconds at the maximum.
The amount of RAM is not enough to effectively cache reads and this seriously affects the InnoDB speed.
Operation execution time charts
1) Create issue; 2) Process 10 issues
parallel/load | myrocks-mysqld-5.6 | mysqld-5.6.31 | mariadb-mysqld-10.1.16 | mysqld-5.6.31 slower | mariadb-mysqld-10.1.16 slower |
---|---|---|---|---|---|
1/0 | 0.018 | 0.024 | 0.024 | 1.301 | 1.307 |
1/25% | 0.018 | 0.026 | 0.026 | 1.43 | 1.408 |
1/x1 | 0.018 | 0.025 | 0.025 | 1.332 | 1.34 |
1/x2 | 0.019 | 0.036 | 0.036 | 1.955 | 1.916 |
1/x4 | 0.019 | 0.041 | 0.041 | 2.217 | 2.185 |
2/0 | 0.02 | 0.026 | 0.026 | 1.32 | 1.33 |
2/25% | 0.019 | 0.027 | 0.028 | 1.413 | 1.432 |
2/x1 | 0.02 | 0.027 | 0.029 | 1.333 | 1.444 |
2/x2 | 0.02 | 0.039 | 0.04 | 1.953 | 2.028 |
2/x4 | 0.02 | 0.048 | 0.047 | 2.458 | 2.414 |
4/0 | 0.022 | 0.03 | 0.029 | 1.397 | 1.334 |
4/25% | 0.024 | 0.031 | 0.03 | 1.252 | 1.223 |
4/x1 | 0.022 | 0.032 | 0.031 | 1.427 | 1.39 |
4/x2 | 0.022 | 0.046 | 0.045 | 2.068 | 2.001 |
4/x4 | 0.023 | 0.056 | 0.054 | 2.419 | 2.343 |
parallel/load | myrocks-mysqld-5.6 | mysqld-5.6.31 | mariadb-mysqld-10.1.16 | mysqld-5.6.31 slower | mariadb-mysqld-10.1.16 slower |
---|---|---|---|---|---|
1/0 | 0.248 | 0.296 | 0.294 | 1.191 | 1.185 |
1/25% | 0.25 | 0.317 | 0.312 | 1.271 | 1.251 |
1/x1 | 0.255 | 0.343 | 0.324 | 1.346 | 1.271 |
1/x2 | 0.254 | 0.458 | 0.427 | 1.803 | 1.681 |
1/x4 | 0.257 | 0.5 | 0.478 | 1.946 | 1.861 |
2/0 | 0.265 | 0.319 | 0.321 | 1.205 | 1.212 |
2/25% | 0.265 | 0.338 | 0.339 | 1.277 | 1.281 |
2/x1 | 0.267 | 0.34 | 0.336 | 1.273 | 1.259 |
2/x2 | 0.27 | 0.475 | 0.479 | 1.757 | 1.77 |
2/x4 | 0.273 | 0.559 | 0.537 | 2.05 | 1.969 |
4/0 | 0.328 | 0.366 | 0.362 | 1.117 | 1.104 |
4/25% | 0.324 | 0.372 | 0.367 | 1.15 | 1.135 |
4/x1 | 0.329 | 0.364 | 0.392 | 1.105 | 1.191 |
4/x2 | 0.31 | 0.511 | 0.506 | 1.648 | 1.631 |
4/x4 | 0.331 | 0.595 | 0.581 | 1.799 | 1.757 |
The bars show the full operation execution time (less is better). The lines show how many times the MySQL or MariaDB server was slower than MyRocks MySQL.
Minimum load: one Redmine process and the absence of external load. Maximum load - four Redmine processes and four full flows of the external disk load.
In this configuration, virtual machine resources better fit the data size and load profile. For MyRocks, the issue creating time remains the same (0.018–0.023 seconds), growing by 23%. For MySQL and MariaDB, the minimum time becomes a little more—from 0.023 seconds (growing only two times) to 0.056 seconds at maximum load; in other words, they are 30% slower than MyRocks at minimum load and 2.3 times slower at the maximum.
For issue processing, the situation is similar. Execution time for MyRocks grows slightly with increasing load: from 0.248 seconds to 0.331 seconds. For MySQL and MariaDB, the minimum time is 10% more than for a small dataset (0.296 s). At the maximum load, the time is almost doubled (0.595 s). MySQL and MariaDB are 18% slower than MyRocks at minimum load and 80% slower at the maximum.
Operation execution time charts
1) Create issue; 2) Process 10 issues
parallel/load | myrocks-mysqld-5.6 | mysqld-5.6.31 | mariadb-mysqld-10.1.16 | mysqld-5.6.31 slower | mariadb-mysqld-10.1.16 slower |
---|---|---|---|---|---|
1/0 | 0.02 | 0.026 | 0.029 | 1.301 | 1.434 |
1/25% | 0.021 | 0.028 | 0.031 | 1.374 | 1.511 |
1/x1 | 0.021 | 0.036 | 0.038 | 1.734 | 1.862 |
1/x2 | 0.021 | 0.051 | 0.053 | 2.466 | 2.585 |
1/x4 | 0.021 | 0.059 | 0.059 | 2.808 | 2.797 |
2/0 | 0.022 | 0.029 | 0.031 | 1.344 | 1.413 |
2/25% | 0.021 | 0.029 | 0.031 | 1.372 | 1.468 |
2/x1 | 0.022 | 0.039 | 0.045 | 1.784 | 2.035 |
2/x2 | 0.022 | 0.059 | 0.062 | 2.689 | 2.844 |
2/x4 | 0.022 | 0.068 | 0.074 | 3.031 | 3.308 |
4/0 | 0.027 | 0.033 | 0.036 | 1.242 | 1.348 |
4/25% | 0.027 | 0.034 | 0.037 | 1.261 | 1.381 |
4/x1 | 0.026 | 0.043 | 0.046 | 1.676 | 1.793 |
4/x2 | 0.028 | 0.069 | 0.072 | 2.495 | 2.612 |
4/x4 | 0.027 | 0.086 | 0.088 | 3.175 | 3.253 |
parallel/load | myrocks-mysqld-5.6 | mysqld-5.6.31 | mariadb-mysqld-10.1.16 | mysqld-5.6.31 slower | mariadb-mysqld-10.1.16 slower |
---|---|---|---|---|---|
1/0 | 0.255 | 0.313 | 0.309 | 1.229 | 1.213 |
1/25% | 0.251 | 0.335 | 0.33 | 1.332 | 1.314 |
1/x1 | 0.256 | 0.453 | 0.444 | 1.769 | 1.733 |
1/x2 | 0.258 | 0.624 | 0.557 | 2.422 | 2.164 |
1/x4 | 0.26 | 0.675 | 0.621 | 2.595 | 2.384 |
2/0 | 0.267 | 0.338 | 0.335 | 1.266 | 1.255 |
2/25% | 0.269 | 0.381 | 0.376 | 1.414 | 1.397 |
2/x1 | 0.274 | 0.573 | 0.601 | 2.092 | 2.194 |
2/x2 | 0.266 | 0.754 | 0.781 | 2.833 | 2.931 |
2/x4 | 0.272 | 0.881 | 0.856 | 3.245 | 3.154 |
4/0 | 0.313 | 0.387 | 0.381 | 1.236 | 1.218 |
4/25% | 0.328 | 0.437 | 0.494 | 1.331 | 1.506 |
4/x1 | 0.341 | 0.712 | 0.768 | 2.087 | 2.251 |
4/x2 | 0.338 | 1.041 | 1.068 | 3.084 | 3.164 |
4/x4 | 0.33 | 1.259 | 1.242 | 3.812 | 3.758 |
The bars show the full operation execution time (less is better). The lines show how many times the MySQL or MariaDB server was slower than MyRocks MySQL.
Minimum load: one Redmine process and the absence of external load. Maximum load - four Redmine processes and four full flows of the external disk load.
A ten-fold growth in data size slightly increased the issue creating time for all databases: 0.020 seconds for MyRocks, 0.026 for MySQL, and 0.029 for MariaDB. Load increment slows MyRocks by 35% (to 0.027 seconds). Load increment affects MySQL and MariaDB more significantly; for example, at maximum load, the time is increased by 3 times (to 0.088 seconds) and they become 3.2 times slower than MyRocks.
For issue processing, the execution time in MyRocks increases by 32% (0.255 to 0.33 seconds), whereas for MySQL and MariaDB it increases by 4 times (0.309 to 1.242 seconds); that is, they lag behind MyRocks by 3.8 times.
The amount of data grows to such a size that they begin to affect the random write delays when updating the InnoDB indexes and the difference in speed between RocksDB and InnoDB at maximum loads increases.
1 Gb RAM is the minimum recommended for Redmine. This memory is too small for efficient data caching in the page cache; thus, performance is very sensitive to the disk load. The delays happen even in SELECT-queries since they have to read data from the disk. The smaller amount of stored data in RocksDB leads to more efficient read caching than with InnoDB. Therefore, the operations speed is only slightly changed when using MyRocks even under heavy load.
After doubling the memory, the main data now fit in the page cache, which means the database server no longer needs to constantly read them from disk. In this case, the disk is a bottleneck only for database changes. Transactions are written on the disk without the writeback cache and the intensive disk load increases the time for write completion.
The organization of data storage in RocksDB supports linear writing while the lower writable data size reduces the number of write operations. Thus, we observe that even at high disk load, the speed of the transaction in RocksDB is only slightly reduced and is much higher than when using InnoDB.
Knowing the work principle of RocksDB, we expected accelerated transactions. The developers obtained a 10x speed boost of the database in synthetic tests. For applications such as Redmine, the full time of the operation consists of the Ruby-script execution time and the time of the database query. Of course, the replacement of the storage engine on RocksDB does not increase the speed of Ruby and this part remains unchanged. However, even taking all this into account, the speed increment resulting from the database acceleration was impressive.
Here, we present the edge case test results for the 2 Gb virtual machine and the large dataset, and the 8 Gb virtual machine and the giant dataset. We do not consider testing with a high load for the 1 Gb virtual machine due to an extreme shortage of resources.
Operation execution time charts
1) Create issue; 2) Process 10 issues
parallel/load/key/size | myrocks-mysqld-5.6 | mysqld-5.6.31 | mariadb-mysqld-10.1.16 |
---|---|---|---|
1/0/2gb-4cpu/large | 0.018 | 0.024 | 0.024 |
1/0/8gb-4cpu/giant | 0.02 | 0.026 | 0.029 |
4/x4/2gb-4cpu/large | 0.023 | 0.056 | 0.054 |
4/x4/8gb-4cpu/giant | 0.027 | 0.086 | 0.088 |
The bars show the full operation execution time (less is better).
Minimum load: one Redmine process and the absence of external load. Maximum load - four Redmine processes and four full flows of the external disk load.
At low load, Redmine on MyRocks is 15%–25% faster than MySQL and MariaDB with InnoDB. The size of the stored data has little influence on speed for either RocksDB or InnoDB. For all databases, increasing the Redmine issues number 10-fold increases execution time only by about 10%.
At high load (increased number of parallel processes and increased external disk load), the behavior changes completely as the gain of MyRocks rose from 2-fold to almost 4-fold. The size of the stored data also had a noticeable effect on the speed. A 10-fold increase in the Redmine issues number significantly (1.5–2 times) slowed the speed of servers with InnoDB and (less markedly) slowed the RocksDB (0%–15%).
The simultaneous increase in stored data size and high load slowed Redmine work with MyRocks by 1.5 times, whereas Redmine on MySQL and MariaDB became 4 times slower.
We have discovered a nuance in behavior with some Redmine SQL queries with issue parent conditions. This caused speed degradation on some kinds of search in MyRocks. However, it is not a bug in MyRocks, but a small omission in Redmine: the issues table had no index for the parent_id column. We also encountered a little bug that leads to high CPU consumption after conflict transactions in MyRocks.
We are not faced with other problems. According to developers, Facebook has used MyRocks in production for a long time.
You can use MyRocks right now or wait for more extensive testing after the appearance of MyRocks in MariaDB release candidate 10.2 or Percona Server for MySQL.
MyRocks is published in the Jetware software collection as one of the mysqld alternatives and is available in the stack constructors for PHP (LAMP, LEMP), Ruby (RAMP, REMP), or in the application’s constructors, e.g., Redmine.
A few weeks ago, we switched our internal Redmine server to MyRocks and are successfully working on it now.
In this benchmarking, we tested the performance of MyRocks with the Redmine application. In the following, we test the performance of MyRocks with some PHP-applications. Most likely, the first will be Drupal.