Facebook на базе своего хранилища RocksDB создал storage engine для MySQL - MyRocks. Работающая реализация MyRocks в виде MySQL 5.6 с патчами от Facebook передана в open source и размещается на GitHub - https://github.com/facebook/mysql-5.6. MyRocks является альтернативой InnoDB и дает несколько преимуществ:
Все это в сумме существенно повышает скорость транзакций на HDD и уменьшают изнашиваемость SSD, а также ускоряют репликацию.
MariaDB и Percona уже ведут работы по интеграции MyRocks в свои форки MySQL: Facebook MyRocks at MariaDB, Announcing MyRocks in Percona Server for MySQL. MariaDB объявила, что MyRocks будет доступен в release candidate 10.2 этой зимой. На Jetware в число альтернатив MySQL добавлена оригинальная реализация MyRocks от Facebook на базе MySQL 5.6.
Тестирование производительности на синтетических тестах показывает впечатляющие результаты. В зависимости от типа устройства хранения, выигрыш по скорости составляет от 20% до 10x. Результаты тестирования LinkBench можно посмотреть в публикации Yoshinori Matsunobu MyRocks: A space- and write-optimized MySQL database и в блоге Mark Callaghan, например, MyRocks: use less IO on writes to have more IO for reads. Эти тесты во многом ориентируются на большие объемы баз данных (десятки и сотни гигабайт) и мощные машины.
В дополнение к синтетическим тестам и тестам на больших объемах данных, мы решили протестировать и оценить выигрыш в производительности для типовых веб-приложений и сайтов небольшого размера.
Первым мы тестируем Redmine. Мы знаем как он устроен, активно используем его в разработке и поэтому тестирование имеет для нас также практическую ценность - если результат окажется хорошим, мы переходим на MyRocks.
Мы используем Redmine 3.3.1 с Ruby 2.3.1, в конфигурации по умолчанию, без дополнительных плагинов.
В качестве серверов баз данных используем:
Все бинарники собраны одним компилятором GCC 4.9.3, с рекомендуемыми параметрами сборки и оптимизации.
Операционная система - Ubuntu 14.04 x86_64, Linux kernel 3.13.0. Файловая система - ext4.
Перед выполнением тестов база данных наполняется сгенерированными заранее проектами, пользователями и задачами. Таких вариантов тоже три:
Большинство реальных случаев использования Redmine приходятся на размеры между Малым и Большим. Объемы уровня Гигантского бывают значительно реже.
Объем занятого пространства
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 |
Столбцы показывают объемы занятого пространства для разных баз данных и разных наборов данных (меньше - лучше)
Мы имитируем работу Redmine в условиях, близких работе у облачного провайдера или на офисном сервере. Для этого мы выделяем не весь физический сервер, а размещаем его в виртуальной машине с гораздо меньшим количеством ресурсов и имитируем различную нагрузку на дисковую систему со стороны соседей. В качестве платформы виртуализации используется Xen 4.6, в dom0 - Linux kernel 3.16.7. Устройство хранения разбивается с помощью LVM, обычное linear, без thin provision и снэпшотов. Том размещается посередине HDD.
Использовались три конфигурации виртуальных машин:
Мы проверяем скорость работы наиболее часто используемых в Redmine операций - создание задач, добавление комментариев к задачам, изменение статуса и ответственного лица задачи. Из этих операций мы создали два теста, два рабочих цикла задачи:
Создание задачи
Создание задачи в проекте одним участником проекта, назначение ее на другого участника проекта и добавление в нее 10 комментариев от лица других участников проекта.
Обработка 10 задач
Получение пользователем 10 назначенных на него задач, переведение их всех в In Progress, и затем поочередное переведение задачи в Resolved и назначение ее на создателя задачи.
Тесты проводятся на 1, 2 и 4 параллельных процессах Redmine.
Нагрузка создается с помощью утилиты fio
, которая читает и пишет 50/50 случайные блоки в оставшуюся часть диска. Мы имитировали несколько уровней нагрузки на диск, которые характерны для типовых случаев работы виртуальных машин - у провайдеров публичных облаков и VPS, или при запуске на собственном сервере нескольких виртуальных машин под VMWare, Hyper-V, KVM или XenServer.
Для имитации неполной загрузки мы запускаем fio с ограничением IOPS с помощью ключа --rate_iops
и замеряем утилизацию диска. При 100% однопоточной нагрузке это около 80 IOPS. Утилизация 25% создается нагрузкой 14 IOPS. Большая нагрузка имитируется увеличением числа потоков ключем --iodepth
.
В зависимости от количества соседних виртуальных машин, характера их работы и пиков нагрузки, загруженность диска может серьезно отличаться как у облачных провайдеров и VPS, так и на собственном сервере. Поэтому мы проводили тестирование при отсутствии сторонней нагрузки, при незначительной однопоточной нагрузке (14 IOPS, 25%), так и при и полных сторонних нагрузках в 1, 2 и 4 потока.
Мы измеряем полное время выполнения каждой операции Redmine на большом числе операций и сравниваем среднее время выполнения. Первые 10% результатов игнорируются - на них мы разогреваем систему. Последние 10% результатов игнорируются для того, чтобы исключить хвостовое искажение из-за разного времени завершения параллельных процессов.
Измерения проводятся в разных комбинациях условий:
Время выполнения замеряется для всех трех баз - MyRocks MySQL, MySQL и MariaDB. Мы также вычисляем разницу в скорости MyRocks MySQL по отношению к MySQL и MariaDB. Собранные данные представлены в виде графиков.
Графики времени выполнения операций
1) создание задачи; 2) обработка 10 задач
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 |
Столбцы показывают время выполнения операции (меньше — лучше). Линии показывают во сколько раз сервер MySQL или MariaDB был медленнее сервера MyRocks MySQL.
Минимальная нагрузка - один процесс Redmine и отсутствует сторонняя нагрузка. Максимальная нагрузка - 4 процесса Redmine и 4 полных потока сторонней нагрузки на диск.
Мы видим, что время создания задачи для MyRocks при увеличении нагрузки до максимума меняется незначительно, и возрастает от 0.018 сек до 0.023 сек, на 23%. Для MySQL и MariaDB минимальное время создания задачи составляет 0.022 сек и возрастает в десять раз до 0.23 сек при максимальной нагрузке. При минимальной нагрузке MySQL и MariaDB оказываются медленнее на 24%, чем MyRocks; при максимальной нагрузке они оказываются медленнее в 9.5 раз.
Время обработки задач для MyRocks вырастает от 0.245 сек при минимальной нагрузке до 0.327 сек при максимальной, на 33%. Для MySQL и MariaDB минимальное время обработки задач возрастает примерно в 7 раз - с 0.283 сек при минимальной нагрузке до 2.245 сек при максимальной.
Объема оперативной памяти не хватает для эффективного кэширования чтения, и это очень сказывается на скорости InnoDB.
Графики времени выполнения операций
1) создание задачи; 2) обработка 10 задач
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 |
Столбцы показывают время выполнения операции (меньше — лучше). Линии показывают во сколько раз сервер MySQL или MariaDB был медленнее сервера MyRocks MySQL.
Минимальная нагрузка - один процесс Redmine и отсутствует сторонняя нагрузка. Максимальная нагрузка - 4 процесса Redmine и 4 полных потока сторонней нагрузки на диск.
В данной конфигурации ресурсы виртуальной машины лучше соответствуют объему данных и нагрузке. Для MyRocks время создания задачи остается прежним - от 0.018 сек до 0.023 сек, вырастая на 23%. Для MySQL и MariaDB минимальное время становится чуть больше - 0.023 сек и вырастает только в два раза - до 0.056 сек при максимальной нагрузке. Они оказываются медленнее, чем MyRocks, на 30% при минимальной нагрузке, и в 2.3 раза при максимальной.
Для обработки задач ситуация похожая. Время выполнения у MyRocks при увеличении нагрузки слабо растет от 0.248 сек до 0.331 сек. Для MySQL и MariaDB минимальное время уже на 10% больше, чем для Малого набора данных и составляет 0.296 сек. При максимуме нагрузки время увеличивается почти в два раза - до 0.595 сек. MySQL и MariaDB оказываются медленнее, чем MyRocks, на 18% при минимальной нагрузке и на 80% при максимальной.
Графики времени выполнения операций
1) создание задачи; 2) обработка 10 задач
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 |
Столбцы показывают время выполнения операции (меньше — лучше). Линии показывают во сколько раз сервер MySQL или MariaDB был медленнее сервера MyRocks MySQL.
Минимальная нагрузка - один процесс Redmine и отсутствует сторонняя нагрузка. Максимальная нагрузка - 4 процесса Redmine и 4 полных потока сторонней нагрузки на диск.
Десятикратное увеличение объема данных немного увеличило время создания задачи для всех баз: 0.020 сек для MyRocks, 0.026-0.029 для MySQL и MariaDB. Увеличение нагрузки замедляет MyRocks на 35% до 0.027 сек. Для MySQL и MariaDB рост нагрузки сказывается на скорости больше - при максимальной нагрузке время увеличивается в 3 раза - до 0.088 сек, и они оказываются медленнее, чем MyRocks, в 3.2 раза.
При обработке задач время выполнения MyRocks увеличивается на 32%, от 0.255 до 0.33 сек. У MySQL и MariaDB время увеличивается в 4 раза - с 0.309 до 1.242 сек. И они отстают от MyRocks в 3.8 раз.
Объем данных уже вырос до таких размеров, что начинают сказываться задержки случайной записи при обновлении индексов InnoDB и разница в скорости между RocksDB и InnoDB при максимальных нагрузках снова выросла.
Для работы Redmine объем 1 Gb является минимальным рекомендуемым. Для эффективного кэширования данных в page cache размер памяти уже недостаточен, поэтому скорость работы очень чуствительна к нагрузке на диск. Задержки возникают уже на SELECT-запросах, так как для них приходится считывать данные с диска. Меньший объем хранимых данных у RocksDB привел к более эффективному кэшированию чтения, чем для InnoDB. Поэтому даже при большой нагрузке скорость операций у MyRocks изменилась совсем незначительно.
При увеличении памяти до 2 Gb основные используемые данные уже вмещаются в page cache и серверу БД уже не нужно постоянно их считывать с диска. В этом случае диск является узким горлышком только при изменениях в базе. Транзакции записываются на диск без writeback cache и интенсивная дисковая нагрузка увеличивает время ожидания завершения записи.
Организация хранения данных в RocksDB, способствующая линейной записи, и уменьшенный объем записываемых данных, уменьшают количество операций записи. Поэтому мы наблюдаем, что даже при высокой дисковой нагрузке, скорость выполнения транзакций в RocksDB лишь немного уменьшается и значительно превышает скорость при использовании InnoDB.
Исходя из принципа работы RocksDB, мы ожидали ускорения при выполнении транзакций. На синтетических тестах производительности разработчики получали 10-кратный прирост скорости в работе СУБД. Для приложений, таких как Redmine, время выполнения операции состоит из времени выполнения Ruby-скрипта и времени выполнения запроса в БД. Разумеется, замена storage engine на RocksDB никак не увеличит скорость работы Ruby, и эта составляющая остается неизменной. Но и с учетом этого прирост в скорости за счет ускорения БД оказался впечатляющим.
Здесь мы приводим краевые результаты тестирования для виртуальной машины 2 Gb и Большого набора данных, и для виртуальной машины 8 Gb и Гигантского набора данных. Мы не учитываем здесь тестирование на высокой нагрузке для виртуальной машины 1 Gb, так как это случай экстремальной нехватки ресурсов.
Графики времени выполнения операций
1) создание задачи; 2) обработка 10 задач
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 |
Столбцы показывают время выполнения операции (меньше — лучше)
Минимальная (1 процесс Redmine без сторонней дисковой нагрузки) и максимальная нагрузка (4 процесса Redmine и 4 потока полной сторонней нагрузки на диск)
При низкой нагрузке Redmine на MyRocks оказался на 15%-25%, быстрее, чем на MySQL и MariaDB. Размер хранимых данных мало влияет на эту скорость и у RocksDB, и у InnoDB - увеличение числа задач Redmine в 10 раз увеличило время выполнения примерно на 10%.
При высокой нагрузке (увеличении числа параллельных процессов и повышении сторонней дисковой нагрузки) поведение полностью меняется. Отрыв MyRocks стал больше - от 2-кратного до почти 4-кратного. Размер хранимых данных также стал существенно влиять на скорость - 10-кратное увеличение числа задач Redmine заметно (в 1.5-2 раза) замедлило скорость выполнения на серверах с InnoDB, и менее заметно замедлило выполнение на RocksDB (0-15%).
Одновременное увеличение объема данных и высокая нагрузка замедлили работу Redmine с MyRocks в 1.5 раза, в то время как Redmine на MySQL и MariaDB стал медленнее в 4 раза.
При тестировании мы обнаружили нюанс поведения одного из SQL-запросов Redmine при поиске с учетом parent issues. Из-за него некоторые виды поиска оказывались медленнее в MyRocks. Но это небольшое упущение со стороны Redmine - parent_id
не имел в таблице индекс. Также мы столкнулись с небольшим багом, приводящим к расходу CPU после некоторых конфликтных транзакций в MyRocks.
Мы не сталкивались с другими проблемами. По словам разработчиков, Facebook использует MyRocks в production уже давно.
Вы можете использовать MyRocks уже сейчас или дождаться более широкого тестирования после появления MyRocks в MariaDB release candidate 10.2 или в Percona Server for MySQL.
MyRocks размещена в репозитории программ Jetware как одна из альтернатив mysqld и доступна в конструкторах стеков PHP (LAMP, LEMP), Ruby (RAMP, REMP) или в конструкторах приложений, например, Redmine.
Несколько недель назад мы перевели свой внутренний Redmine-сервер на MyRocks и успешно в нем работаем.
В данном тесте мы проверили производительность MyRocks для приложения Redmine. В следующих тестах мы собираемся проверить производительность MyRocks с PHP-приложениями. Скорее всего, первым будет Drupal.