Тест производительности движков MySQL, PostgreSQL, FireBird (только SELECT в несколько потоков).
Рассказ о повышении производительности тормозящего запроса. Сравнение движков баз данных: MySQL, FireBird, PostgreSQL, SQLite. Субъективные выводы. Описание теста. Сколько людей делало всяческие benchmark‘и, разных баз данных. Все претендуют на объективность, все такие хорошие, соображающие и опытные тюнингаторы. :-).
Вчера стукнулись, попросили разобраться, что происходит с одним из серверов, некогда настроенным мной. А было там примерно следующее:
1:56PM up 44 days, 17:24, 1 user, load averages: 21.58, 22.19, 22.07
Как выяснилось, всему виной была база данных (неудивительно), базу настраивал лично я, параметры были подобраны нормально, кэширование запросов — включено, казалось бы — все должно быть отлично. Однако за эти пару-тройку месяцев база разрослась и начали возникать первые проблемы.
В продолжении статьи — детали и сравнение производительности БД для разных серверов: MySQL, FireBird, PostgreSQL. И движка SQLite. Все эти движки — опенсорсные и работают на Linux и FreeBSD.
Общие данные о структуре базы данных.
База, состоящая из одной таблицы, представлена следующим образом (MySQL синтаксис):
Аналогичная структура базы данных была воспроизведена и в остальных движках БД: PostgreSQL, FireBird, SQLite. Для тестирования использовался perl (DBD, DBI), использовались модули Time::HiRes и threads.
Использованные серверы баз данных
PostgreSQL 8.0.13 on x86_64-pc-linux-gnu, compiled by GCC x86_64-pc-linux-gnu-gcc (GCC) 4.1.2 (Gentoo 4.1.2)
ISQL Version: LI-V2.0.3.12981 Firebird 2.0 Server version: Firebird/linux AMD64 (access method), version «LI-V2.0.3.12981 Firebird 2.0″ Firebird/linux AMD64 (remote server), version «LI-V2.0.3.12981 Firebird 2.0/tcp (localhost)/P10″ Firebird/linux AMD64 (remote interface), version «LI-V2.0.3.12981 Firebird 2.0/tcp (localhost)/P10″ on disk structure version 11.0
MySQL: mysqladmin Ver 8.41 Distrib 5.0.44, for pc-linux-gnu on x86_64 Server version 5.0.44-log Protocol version 10 Connection Localhost via UNIX socket
Как видно, не для всех серверов версии последние (но для всех достаточно свежие). Везде использовалась 64-битная сборка. ОС — Gentoo Linux. Память: 1024M. Был выполнен следующий тюнинг серверов:
PostgreSQL: процессу передаются такие опции: -N 25 -c work-mem=65536 -c shared-buffers=1000 (не более 25 одновременных подключений, 1000 буферов, разделяемых на эти подключения, рабочая память не более 64Mb).
MySQL: включено кэширование SQL запросов (SET GLOBAL query_cache_limit = 1048576; SET GLOBAL query_cache_type = 1). Настройки для MyISAM дефолтные.
Тюнингом FireBird я никогда ранее не занимался, учиться времени не было, поэтому оставлен конфиг по умолчанию (ОС Gentoo Linux).
По причинам, о которых расскажу позже, привожу данные warm-up запуска (для того, чтобы движок БД считал данные с диска), производимого ОДИН раз, и не перед тестом непосредственно, а после старта системы:
PostgreSQL: четыре процесса (122Mb (6Mb) в сумме). Время разогревочного запроса 0m6.443s. Предположительно связано с огромным размером индекса на диске, я создал два индекса (btree и hash) для колонки place, не зная, какой из них будет использован движком.
MySQL: 202Mb (36Mb). Время разогревочного запроса 0m2.468s.
FireBird: 103Mb (6Mb). Время разогревочного запроса 0m7.836s.
SQLite: Время разогревочного запроса 0m5.108s.
Следующий этап — я останавливаю движки БД, а через 5 минут запускаю их вместе. Еще через 5 минут — выполняю тот же злополучный запрос. В течение этих 5 минут, в совершенно другом каталоге считается размер, занятый файлами.
0m0.841s — PostgreSQL, 0m2.395s — MySQL, 0m4.477s — FireBird, 0m2.896s — SQLite. Как видно, дисковый кэш разогрелся, все движки получили свой прирост производительности. Теперь тестовые данные запуска в такой конфигурации: 20 потоков выполняют по 10 запросов каждый. Выполнение всех 20 потоков начинается одновременно.
Ход тестирования:
Для тестирования использовался самодельный, на скорую руку написанный тест на Perl. Для многопоточности использовался модуль threads.
Multi thread: prepare_pg: Global avg is: 3.13079101085663 Память 124 (6)
Multi thread: prepare_my: Global avg is: 14.5757958674431 Память 484 (52)
Multi thread: prepare_fb: Global avg is: 0.00530530452728272 Память 95 (5)
Multi thread: prepare_s3: Global avg is: 43.2979071068764 Память 675 (111)
Как видно, FireBird успешно справился с кэшированием запроса, — вряд ли хоть один движок БД даже на суперкомпьютере, смог бы выполнить эту выборку за 0,005 секунды. Настройки по умолчанию — действительно отличные! Дальнейшей оптимизации движок подвергать не будем.
Хуже всех, как и ожидалось, в многопоточной среде отработал SQLite. Помимо прочего, во время выполнения скрипта, perl «отжирал» 675Mb (111Mb) памяти, т.е. ясно, что под нагрузкой, в многопоточном окружении, этот движок нежизнеспособен. Впрочем, другого и не ожидалось — не та сфера применения.
Но почему же такие низкие результаты показаны MySQL? MySQL известен по всему интернету, как движок с неимоверно быстрыми SELECT. Даже неверный выбор query plan (который действительно имеет место быть, позже я это покажу, в сравнительном анализе MySQL и PostgreSQL), не мог привести к такой низкой производительности.
Вероятнее всего, проблема кроется в использовании движка MyISAM. Попробуем с InnoDB. Перевод базы в InnoDB осуществлялся следующим образом: mysqldump baron table > table_mysql.sql, в текстовом редакторе vim было заменено ENGINE=MyISAM на ENGINE=InnoDB.
Блокировка в многопоточном режиме должна стать более эффективной, и производительность базы должна возрасти. Даже разогревочный запрос выполнялся всего лишь 0m1.385s.
Как и ожидалось, среднее время запроса уменьшилось (производительность выросла). Но ненамного. Увеличилась скорость выполнения первых запросов (7с.), а затем запросы стали достигать 15с. После еще одного, повторного запуска, производительность, кажется, повысилась еще больше (минимальное время составило 3с.) Этот результат и отражен в таблице.
Multi thread: prepare_my: Global avg is: 12.3769785487652. Память 220 (116).
Увеличение буфера сортировки дало свои результаты (вместо 512Kb по умолчанию, я задействовал 4Mb):
Multi thread: prepare_my: Global avg is: 10.900887594223. Память 235 (127).
Экспериментальным путем было выяснено, что 4 Mb достаточно (увеличение до 6Mb не дает прироста производительности).
Однако у нас в рукаве еще остался козырь: Если FireBird в состоянии закэшировать запрос, значит и MySQL также может это сделать. Надо только понять, в чем причина. А причина, вероятно всего, в большом объеме данных — план выполнения запроса показывает большое количество строк, которые будут проверены — вероятно, они занимают в памяти больше 1Mb, которое отведено под кэширование данных.
Проверим гипотезу. Раз для буфера сортировки оптимальное значение в районе 4 Mb, попробуем установить размер кэшируемых данных также в 4 Mb. Поднимем общий предел закэшированных данных до 20 Mb. (SET GLOBAL query_cache_limit = 4 * 1048576; SET GLOBAL query_cache_size = 20 * 1048576;):
Multi thread: prepare_my: Global avg is: 4.80021322250366. Память 362 (184).
Очевидно, где-то в середине сработало кэширование, и результат получился приемлемым :-). Однако, пробуем еще раз:
Multi thread: prepare_my: Global avg is: 0.000454176664352417
Итак, — мы только что догнали и обогнали FireBird.
Теперь попробуем максимально приблизить к этому результату и PostgreSQL.
Увеличение -c effective_cache_size=1000 не дало нужного результата. Не дало результата и его дальнейшее повышение.
Возможно, в 8.3 версии стало лучше, но сейчас запросы не кэшируются.
Сухая статистика:
Многопоточный тест производительности выборки:
Движок базы данных | 1 запрос | Память |
PostgreSQL | 3,1308 | 6 |
MySQL MyISAM | 14,5758 | 52 |
MySQL InnoDB | 12,377 | 116 |
MySQL InnoDB (sort_buffer) | 10,9009 | 127 |
MySQL InnoDB (sort_buffer, query_cache_limit) | 0,0005 | 184 |
FireBird | 0,0053 | 5 |
SQLite 3 | 43,2979 | 111 |
Разогрев базы данных:
Движок базы данных | 1 запрос |
PostgreSQL | 6,443 |
MySQL MyISAM | 2,468 |
MySQL InnoDB | 1,385 |
FireBird | 7,836 |
SQLite 3 | 5,108 |
Примечания: Нет возможности посмотреть реальную статистику использования памяти PostgreSQL, т.к. для выполнения запросов создаются отдельные процессы. Для SQLite 3 статистика памяти не может быть точной, т.к. приведен расход памяти всего perl скрипта, в него включен расход памяти интерпретатора.
Субъективные выводы
Для этого теста лучше всех в конфигурации «из коробки» проявил себя FireBird. Хотя в целом производительность выборки была продемонстрирована довольно посредственная, но ведь это же конфигурация «из коробки» — ни один параметр не подвергался тюнингу, вероятно, разогревочный запрос можно было увеличить хотя бы до уровня PostgreSQL. В любом случае, FireBird оставляет много места для оптимизации — в частности, можно задать план выполнения запроса (query plan) вручную. Однако «из коробки», запросы выполнялись 1.10 sec — в режиме объяснения query plan.
Чрезвычайно порадовали математики, писавшие алгоритм выбора плана запроса для PostgreSQL. План подобран более качественно, благодаря этому стало возможным троекратное превосходство над MySQL. Наверняка есть и возможности роста — во-первых, таблицы размещались в файле; во-вторых, параметры (кроме количества одновременных подключений и размера памяти) не были затронуты. Вероятно, можно изменить размер буфера сортировки; возможно, можно явно указать более эффективный план запроса вручную. Но в любом случае, 0.375 sec при выполнении одиночного запроса на разогретой базе — очень хороший результат.
MySQL, не позволяющий задать план запроса, оказался в не самом удачном положении. Хотя его спасло наличие кэша результатов — при условии, что база не изменяется, за счет него он способен «порвать» очень многих конкурентов. Без использования кэша, на разогретой базе — время выполнения запросов составляет 1.19 sec.
SQLite был оставлен исключительно для того, чтобы было с чем сравнивать. Подобное применение — совершенно нетипично для этого движка, у него и не было ни малейшего шанса. Однако, при условии однопользовательского окружения, SQLite показывает довольно неплохой результат. 2.57 секунд — это всего лишь в 2 раза медленнее, чем MySQL.
Ни один из упомянутых движков, на мой взгляд, не был достаточно хорош. PostgreSQL был бы близок к термину «достаточно хорош» в моем понимании, если бы поддерживал query cache и «из коробки» смог бы конкурировать с FireBird в этом конкретном случае. FireBird бы был «достаточно хорош», если бы производительность выполнения запроса без кэширования оказалось на хорошем уровне. MySQL достаточно хорош — и его повсеместное применение для некритичных задач это подтверждает. Однако при масштабировании (scale) базы данных, могут возникать проблемы, подобные описанной здесь.
Есть, конечно, и иные точки зрения на этот счет. Пример: недавнее исследование — сравнение производительности Drupal 5.5 с базой данных на PostgreSQL и MySQL. Здесь же приведен всего лишь синтетический тест, одного конкретного медленного запроса.
Впрочем, эти выводы — лишь мое личное мнение
Источник