понедельник, 22 декабря 2008 г.

Тестирование производительности базы данных

Решил изложить некоторые мысли на эту тему: не претендую ни на полноту обзора, ни на абсолютность своего мнения.

Итак, когда речь заходит о тестировании производительности, обычно выделяют:

  • нагрузочный тест (load test) - насколько хорошо мы можем выдержать плановую загрузку (сохранив при этому заданные показатели производительности)
  • стресс-тест (stress test) - под какой нагрузкой мы сломаемся и что при этом произойдет

Тестирование производительности обычно проводят для каждой версии приложения и наблюдют не только и не столько за абсолютными цыфрами (хотя важны и они), сколько за динамикой - "мы внесли изменения в архитектуру и нам удалось улучшить производительность на X%".

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

  • настройки: база данных не является самодостаточной сущностью с точки зрения приложения - она работает под управлением сервера БД, от характеристик и настроек которого сильно зависит то, с какой скоростью наше приложение сможет считывать/записывать данные
  • непрямое взаимодействие: пользователь приложения в подавляющем большинстве случаев не взаимодействует с базой данных напрямую, поэтому при неизменном с точки зрения пользователя функционале, разные версии приложения могут генерировать существенно разную нагрузку
  • профиль нагрузки: производительность сильно зависит от того, как именно используется приложение (есть более "тяжелые" и более "легкие" операции), впрочем, эта проблема характерна для приложения вцелом
  • масштабируемость нагрузки: производительность существенным (и нелинейным) образом зависит от объемов данных в базе и количества подключений (одновременно работающих пользователей)
  • единицы измерения: сложно определить в каких "попугаях" мерять производительность базы данных - ведь не существует "единых универсальных запросов к базе данных"

Попробуем разобраться с этими вопросами.

Настройки. Тут особенно сказать нечего: да, от настроек (равно как и от оборудования) очень многое зависит. Нужно постараться воспроизвести настройки целевого окружения (production environment) и стараться не менять их от теста к тесту. С оборудованием сложнее. Идеальный вариант - тестировать на точной копии целевых серверов, но практически мало кто может себе такое позволить. Однако запуск тестов на одном и том же оборудовании с одним и тем же набором настроек позволит получить общую картини движения: ничего не поменялось, улучшилос, ухудшилось и т.п.

Непрямое взаимодействие. По большому счету у нас здесь есть два пути:

  1. Нагружать базу данных через приложение. При этом мы фактически тестируем еще и приложение. Плюс здесь в том, что мы будет эмулировать работу пользователя (по-идее) - т.е. все будет так же, как и в реальных условиях. Минус в том, что сложнее измерять (т.к. все измерения будут относиться к системе в целом), требуется больше ресурсов (как программных, так и аппаратных) и сложнее масштабировать (увеличиваем число взаимодействующих подсистем - увеличиваем число мест, которые могут стать бутылочным горлышком).
  2. Нагружать базу данных напрямую. Здесь мы может свести к минимуму влияние других подсистем приложения, но можем столкнуться с необходиомостью реализации чуть ли не самого приложения с нуля (для эмуляции активности пользователя). Альтернативой является запись активности приложения с последующим его воспроизведением в несколько потоков.

Первый из этих способов более "правильный" и позволяет генерировать нагрузку, в наибольшей степени приближенную к реальности. Второй - проще реализовать, (в общем случае) проще масштабировать, да и к ресурсам он менее требователен.

Профиль нагрузки. Имеет смысл завести несколько "пакетов", в которые войдут разные наборы сценариев использования приложения. Можно составить некоторый обобщенный сценарий поведения пользователя (вроде: вошел в систему, почитал новости, зашел в раздел такой-то, потом туда-то, сдела то-то, вышел) и тестировать именно его. Несколько подобных профилей, составленных с учетом статистики использования реальной системы, позволят сгенерировать реалистичную нагрузку, в максимальной степени похожую на ту, которую генерируют реальные пользователи.

Масштабируемость нагрузки. Здесь все зависит от целей, которые мы преследуем. Как уже было сказано, масштабировать нужно объем базы данных и количество подключений (одновременно работающих пользователей). Очевидное общее правило: чем больше данных, тем меньше пользователей сможет комфортно работать. Существуют утилиты, которые могу нагенерировать случайные данные для БД любой структуры. Но не всегда их можно применить: денормализованность схемы, неполное описание ограничений на уровне схемы, "хитрая" логика обработки данных, хранящаяся в хранимых процедурах или коде приложения - все это значительно затрудняет генерацию. Хорошим подспорьем будет какая-нибудь реальная БД с пользовательскими данными.

Единицы измерения. Вот здесь, пожалуй, одна из самых больших проблем. Мы можем измерять нагрузку на сервер, на котором работает наша СУБД с тем, чтобы понять, справляется он или нет с заданной нагрузкой и что меняется от версии к версии. В этой связи полезно измерять:

  1. среднюю загрузку процессора (CPU, %) - показатель в 60-70% считается нормальным (допустимым), если больше - сервер не справляется
  2. очередь ввода/вывода (Average disk queue length) - даже значение в несколько единиц свидетельствует о проблемах с подсистемой ввода/вывода (должно быть 0)
  3. количество запросов на блокировки в нашей базе (locks/sec) - чем меньше, чем лучше, важен прогресс от теста к тесту и изменение интенсивности этой величины в течении теста; всплески позволят идентифицировать наиболее тяжелые действия, которые нуждаются в оптимизации в первую очередь
  4. среднее время ожидания блокировки (msec) - показывает сколько нам приходится ждать окончания работы других потоков (чем меньше - тем лучше)

В ходе тестирования всегда стоит запустить какой-нибудь инструмент мониторинга запросов к БД с тем, чтобы потом можно было наложить полученный трэйс на данные о производительности и понять, как они соотносятся, найти наболее "тяжелые" операции (запросы) и уделить внимание их оптимизации.

Комментариев нет:

Отправить комментарий