MySQL vs Cassandra или как ускорить INSERT в десятки раз

279px-Cassandra_logo.svgНа днях мы начали выбор решения для управления данными, которое бы поддерживало высокую производительность на операциях INSERT. Данная задача достаточно часто встречается в различных информационных системах, например, в системах биллинга с большим количеством пользователей и учетных объектов, в системах обмена мгновенными сообщениями, в системах аналитической обработки данных map-reduce. Все данные задачи характеризуются тем, что в них большое количество операций вставки новых объектов в БД и огромный размер БД, при этом обработка данных может производиться как в онлайн так и в пакетном режиме. Если провести параллель в сторону большинства традиционных сайтов (например, контентных проектов), то в них ситуация диаметрально противоположная — система работает с большим количеством SELECT и малым количеством INSERT/UPDATE. Часто соотношение составляет 10% INSERT-UPDATE/90% SELECT. Надо отметить, что реляционные СУБД достаточно плохо справляются с задачей, которую нам предстояло решить, поэтому мы провели несколько сравнительных тестов системы Cassandra, которая является проверенным решением для задач с интенсивным потоком новых данных. Предлагаем Вашему вниманию результаты тестирования.

Итак, коротко о ключевых особенностях Cassandra:

  • реализована на Java
  • разрабатывается под эгидой Apache Foundation, бесплатная NoSQL СУБД;
  • поддерживает SQL-подобный синтаксис (CQL);
  • не поддерживает внешние ключи;
  • multimaster репликация без ограничений роста;
  • линейное увеличение производительности в зависимости от количества узлов.

MySQL является популярной СУБД для Web и, по сути, стандарт de-facto для многих проектов. О плюсах и минусах MySQL можно говорить достаточно долго. В контексте нашей задачи можно говорить о следующих особенностях:

  • сложное управление реплицируемыми кластерами;
  • плохая масштабируемость на операциях записи;
  • низкая производительность на операциях записи.

Конечно, существуют продукты, которые частично решают вышеуказанные ограничения, например, MySQL Cluster, однако, ситуация с производительностью на запись остается прежней.

Тестовая среда

Мы производили тесты в следующей тестовой среде:

  1. Экземпляр VM: Large 8 cores (Dual E5-2670), 8 GB RAM, 20GB virtio disk
  2. хранилище NFS/SSD RAID5 Adaptec 6405;
  3. Сеть хост-хранилище 10G Intel 350;
  4. Ubuntu 14.04 LVM/Ext4;
  5. Cassandra 2.0.9 с Oracle Java 7 + cassandra-driver (python);
  6. MySQL 5.5 из репозитория (INNODB, MYISAM) с модифицированным конфигурационным файлом.

В принципе, было бы интересно провести тесты и для MariaDB, но это будут тесты сравнения реализаций MySQL, а в данном случае вряд ли мы получим радикальные отличия.

Что мы тестируем

Схема таблицы Cassandra:

CREATE TABLE statdata (
  host int,
  user bigint,
  object text,
  when timestamp,
  value float,
  PRIMARY KEY ((host), user, object, when)
) WITH COMPACT STORAGE AND
  bloom_filter_fp_chance=0.010000 AND
  caching='KEYS_ONLY' AND
  comment='' AND
  dclocal_read_repair_chance=0.100000 AND
  gc_grace_seconds=864000 AND
  index_interval=128 AND
  read_repair_chance=0.000000 AND
  replicate_on_write='true' AND
  populate_io_cache_on_flush='false' AND
  default_time_to_live=0 AND
  speculative_retry='99.0PERCENTILE' AND
  memtable_flush_period_in_ms=0 AND
  compaction={'class': 'SizeTieredCompactionStrategy'} AND
  compression={'sstable_compression': 'LZ4Compressor'};

Схема таблиц MySQL:

create table statdata (
host int,user int, 
`when` int, 
object varchar(32), 
value int) engine=MyISAM;

create unique index statdata_idx on statdata (host,user,object,`when`);

create table statdata2 (
host int,
user int, 
`when` int, 
object varchar(32), 
value int) engine=InnoDB;
create unique index statdata_idx on statdata2 (host,user,object,`when`);

Во всех тестах используется составной уникальный ключ host+user+object+when, который состоит из трех int и одного varchar.

Как мы тестируем

Мы выполняли два вида тестов:

  1. однопоточные тесты;
  2. многопоточные тесты в 4 потока.

Для Cassandra выполнялось два вида тестов:

  1. тест с асинхронным выполнением запросов (не ждем ответа от СУБД);
  2. тест с синхронным выполнением запросов (ждем ответа от СУБД).

Для MySQL так же выполнялось два вида тестов:

  1. тест с таблицей в MyISAM (statdata);
  2. тест с таблицей в INNODB (statdata2).

Перед выполнением теста все данные очищаются  через TRUNCATE.

Сначала представляем результаты тестирования в один поток (вставка 900K записей):

single-thread

По горизонтальной оси — количество вставленных строк. По вертикальной — время в секундах, которое потрачено на вставку. Как можно видеть Cassandra с асинхронными запросами оставляет остальных участников далеко позади. Cassandra с синхронными запросами ведет себя хуже чем INNODB, но обратите внимание, оба графика для Cassandra линейные. Мы специально продолжали вставки до 10 млн строк и Cassandra продолжала вести себя все так же линейно, в то время как у MySQL начиналась деградация и загибание графиков вверх. Линейность может быть объяснена способом хранения, используемым Cassandra, который позволяет обеспечить рост БД без деградации.

Внизу статьи можно будет найти архив с результатами тестирования, пока же хочется дополнительно отметить, что происходило с процессором и дисковой подсистемой:

  1. Cassandra async: Iowait ~ 0.25%, IOPS ~ 3, python client — 100% одного ядра;
  2. Cassandra sync: Iowait ~ 0.01%, IOPS ~ 1, python client — 30% одного ядра;
  3. MySQL INNODB: Iowait ~ 7%, IOPS ~ 1000, python client — 20-30% одного ядра;
  4. MySQL MyISAM: Iowait ~ 17%, IOPS ~ 1000, python client — 20-30% одного ядра;

iops60-single

Что можно сказать по этим данным… В случае Cassandra первый тест упирается в скорость клиента и не может получить большую производительность, многопоточный тест позволит получить реалистичный результат. Второй тест для Cassandra упирается в ожидание результата операции от Cassandra. При этом оба теста генерируют мизерную нагрузку на дисковую подсистему.

В случае MySQL мы видим огромное количество IOPS на диск и значительный IOWAIT. Скорее всего упираемся в ограничение MySQL и/или дисковой подсистемы виртуальной машины (хотя IOWAIT не слишком большой, скорее всего именно MySQL). Маловероятно многопоточный тест даст лучшие результаты для MyISAM, возможно что для INNODB будет улучшение.

В случае выполнения любых тестов IOWAIT хранилища был ~0%, что свидетельствует об отсутствии узких мест в системе хранения, которые бы негативно влияли на MySQL.

Представляем результаты тестирования в четыре потока:

cassandra-vs-mysql-4

cassandra-8

Данные тесты выполняются одновременно четырьмя клиентами, а тест CASSANDRA-SYNC-8 одновременно 8 клиентами. На первом изображении можно увидеть, что в районе 300000×4 (1200000) записей INNODB начинает проигрывать CASSANDRA-SYNC-4, дальше разрыв только увеличивается. При этом INNODB генерирует ~1500 IOPS и загружает систему на 15%, а у Cassandra загрузка и IOPS остаются на мизерном уровне. Для демонстрации возможностей приведен тест CASSANDRA-SYNC-8, в котором в хранилище пишут одновременно 8 клиентов. Как видно, он не слишком отличается от CASSANDRA-SYNC-4.

Наше предположение в том, что можно увеличивать количество клиентов, пока мы не сравнимся с CASSANDRA-ASYNC-4, потому что на этом тесте Cassandra и клиенты полностью утилизировали  8 ядер CPU.

Все журналы тестирования, конфигурационные файлы и тесты на python и shell находятся в архиве: suite

В конце можно добавить, что данные тесты покрывают только случай, когда осуществляется 100% операций вставки в базу, для понимания производительности необходимо так же провести тесты, которые комбинируют чтение и запись одновременно.

Масштабируемость

Модель хранения данных Cassandra подразумевает линейную масштабируемость системы, то есть, увеличив количество узлов в 2 раза, Вы увеличите производительность системы так же в два раза. Для обеспечения отказоустойчивости Cassandra хранит в кластерной системе обычно 3 реплики. Кроме того, линейное увеличение производительности позволяет достаточно просто планировать будущие затраты на вычислительные мощности.

Вместо заключения

Конечно, цель данной статьи не состоит в том, чтобы сказать «выкиньте MySQL», для различных приложений существуют различные решения и реляционные СУБД не зря доминируют на рынке систем управления данными уже 30 лет, это лишь подтверждает, что РСУБД — удачный и удобный инструмент, подходящий для многих задач, однако в свете меняющегося мира, когда объем данных постоянно растет, часто требуются новые инструменты, которые могут уступать гибкости и функциональности реляционным СУБД, но позволяют обрабатывать значительно большие нагрузки и адаптироваться к меняющимся требованиям к производительности.