Решил изложить некоторые мысли на эту тему: не претендую ни на полноту обзора, ни на абсолютность своего мнения.
Итак, когда речь заходит о тестировании производительности, обычно выделяют:
- нагрузочный тест (load test) - насколько хорошо мы можем выдержать плановую загрузку (сохранив при этому заданные показатели производительности)
- стресс-тест (stress test) - под какой нагрузкой мы сломаемся и что при этом произойдет
Тестирование производительности обычно проводят для каждой версии приложения и наблюдют не только и не столько за абсолютными цыфрами (хотя важны и они), сколько за динамикой - "мы внесли изменения в архитектуру и нам удалось улучшить производительность на X%".
Теперь разберемся что есть что применительно к базам данных. Во-первых, при попытке произвести тестирование производительности базы данных, мы неизбежно сталкиваемся с набором довольно специфических трудностей:
- настройки: база данных не является самодостаточной сущностью с точки зрения приложения - она работает под управлением сервера БД, от характеристик и настроек которого сильно зависит то, с какой скоростью наше приложение сможет считывать/записывать данные
- непрямое взаимодействие: пользователь приложения в подавляющем большинстве случаев не взаимодействует с базой данных напрямую, поэтому при неизменном с точки зрения пользователя функционале, разные версии приложения могут генерировать существенно разную нагрузку
- профиль нагрузки: производительность сильно зависит от того, как именно используется приложение (есть более "тяжелые" и более "легкие" операции), впрочем, эта проблема характерна для приложения вцелом
- масштабируемость нагрузки: производительность существенным (и нелинейным) образом зависит от объемов данных в базе и количества подключений (одновременно работающих пользователей)
- единицы измерения: сложно определить в каких "попугаях" мерять производительность базы данных - ведь не существует "единых универсальных запросов к базе данных"
Попробуем разобраться с этими вопросами.
Настройки. Тут особенно сказать нечего: да, от настроек (равно как и от оборудования) очень многое зависит. Нужно постараться воспроизвести настройки целевого окружения (production environment) и стараться не менять их от теста к тесту. С оборудованием сложнее. Идеальный вариант - тестировать на точной копии целевых серверов, но практически мало кто может себе такое позволить. Однако запуск тестов на одном и том же оборудовании с одним и тем же набором настроек позволит получить общую картини движения: ничего не поменялось, улучшилос, ухудшилось и т.п.
Непрямое взаимодействие. По большому счету у нас здесь есть два пути:
- Нагружать базу данных через приложение. При этом мы фактически тестируем еще и приложение. Плюс здесь в том, что мы будет эмулировать работу пользователя (по-идее) - т.е. все будет так же, как и в реальных условиях. Минус в том, что сложнее измерять (т.к. все измерения будут относиться к системе в целом), требуется больше ресурсов (как программных, так и аппаратных) и сложнее масштабировать (увеличиваем число взаимодействующих подсистем - увеличиваем число мест, которые могут стать бутылочным горлышком).
- Нагружать базу данных напрямую. Здесь мы может свести к минимуму влияние других подсистем приложения, но можем столкнуться с необходиомостью реализации чуть ли не самого приложения с нуля (для эмуляции активности пользователя). Альтернативой является запись активности приложения с последующим его воспроизведением в несколько потоков.
Первый из этих способов более "правильный" и позволяет генерировать нагрузку, в наибольшей степени приближенную к реальности. Второй - проще реализовать, (в общем случае) проще масштабировать, да и к ресурсам он менее требователен.
Профиль нагрузки. Имеет смысл завести несколько "пакетов", в которые войдут разные наборы сценариев использования приложения. Можно составить некоторый обобщенный сценарий поведения пользователя (вроде: вошел в систему, почитал новости, зашел в раздел такой-то, потом туда-то, сдела то-то, вышел) и тестировать именно его. Несколько подобных профилей, составленных с учетом статистики использования реальной системы, позволят сгенерировать реалистичную нагрузку, в максимальной степени похожую на ту, которую генерируют реальные пользователи.
Масштабируемость нагрузки. Здесь все зависит от целей, которые мы преследуем. Как уже было сказано, масштабировать нужно объем базы данных и количество подключений (одновременно работающих пользователей). Очевидное общее правило: чем больше данных, тем меньше пользователей сможет комфортно работать. Существуют утилиты, которые могу нагенерировать случайные данные для БД любой структуры. Но не всегда их можно применить: денормализованность схемы, неполное описание ограничений на уровне схемы, "хитрая" логика обработки данных, хранящаяся в хранимых процедурах или коде приложения - все это значительно затрудняет генерацию. Хорошим подспорьем будет какая-нибудь реальная БД с пользовательскими данными.
Единицы измерения. Вот здесь, пожалуй, одна из самых больших проблем. Мы можем измерять нагрузку на сервер, на котором работает наша СУБД с тем, чтобы понять, справляется он или нет с заданной нагрузкой и что меняется от версии к версии. В этой связи полезно измерять:
- среднюю загрузку процессора (CPU, %) - показатель в 60-70% считается нормальным (допустимым), если больше - сервер не справляется
- очередь ввода/вывода (Average disk queue length) - даже значение в несколько единиц свидетельствует о проблемах с подсистемой ввода/вывода (должно быть 0)
- количество запросов на блокировки в нашей базе (locks/sec) - чем меньше, чем лучше, важен прогресс от теста к тесту и изменение интенсивности этой величины в течении теста; всплески позволят идентифицировать наиболее тяжелые действия, которые нуждаются в оптимизации в первую очередь
- среднее время ожидания блокировки (msec) - показывает сколько нам приходится ждать окончания работы других потоков (чем меньше - тем лучше)
В ходе тестирования всегда стоит запустить какой-нибудь инструмент мониторинга запросов к БД с тем, чтобы потом можно было наложить полученный трэйс на данные о производительности и понять, как они соотносятся, найти наболее "тяжелые" операции (запросы) и уделить внимание их оптимизации.