|
|
|||||||||||||||||||||||||||||
|
Профилирование PHP скриптов на живом сервереИсточник: habrahabr Ents
Наверняка кто-то из вас сталкивался с такой проблемой: медленно работает сайт на реальном сервере. Как это все работает?Сбор данныхPinba отправляет на свой сервер (по UDP, очень быстро) метки начала и конца отрезка времени (далее - таймеры) и складывает данные в MySQL таблицы (легко прочитать). Например
Для построения древовидной структуры мы добавляем 2 дополнительных тега - tree_id (каждый раз уникальный id) и tree_parent_id - это tree_id от того таймера, в который вложен текущий. Например
Таким образом, на сервере можно воспроизвести вложенность таймеров и построить удобочитаемое дерево. Мы разместили во всех интересных местах проекта таймеры, чтобы засекать время (например, при sql запросах, при записи в файлы и т.д.). Подготовка данныхК сожалению, pinba не использует индексы для запросов (кроме PRIMARY), так как используется свой pinba ENGINE (таблицы фактически хранятся в memory, и данные старше N минут удаляются, в нашем случае - 5 минут). Но нельзя сетовать на pinba, так как она предназначена не для запросов по индексам. Для нас индексы важны, потому мы копируем все данные из таблиц pinba в обычные MyISAM таблицы.
Как видно из запросов, у нас система работает в базе pinba, а копия - в базе pinba_cache. Так же для работы нам понадобится ещё одна таблица, в которой будут поля tree_id и tree_parent_id.
Структура таблицы timer_tag_tree приведена ниже. Структура остальных таблиц такая же как в pinba.
Анализ данныхТеперь - самое интересное. Мы собрали данные, сложили их так, как нам необходимо для последующей работы. Далее необходимо написать скрипт, который все эти данные выдаст в удобном виде. Как вывести одно дерево (от одного запроса к сайту) - писать не буду, так как это тривиальная задача. Проблема в том, что для оценки узких мест нужно проанализировать сотни запросов к php, каждый из которых имеет свое дерево вызова функций (таймеров). Нам нужно из этих деревьев собрать одно обобщенное дерево. Алгоритм объединения следующий: Для каждого узла считаем сумму времен выполнения этого узла по всем деревьям. Написав функцию для объединения двух деревьев, можно пройтись циклом по всем и получить сумму. Но тут нас ждет неприятный сюрприз - медленное время работы. Как видим из картинки, сложность объединения 2 деревьев - O(N*N) (внимательные мне подскажут, что можно сделать это за N*log(N), но далее будет более простой метод оптимизации, в 3 строчки), где N - к-во узлов в дереве. Соответственно выгодно объединять маленькие деревья, и очень невыгодно большие. Постараемся эту особенность использовать. Давайте определим дерево выполнения одного скрипта как дерево 1 уровня, сумма двух деревьев первого уровня - дерево второго уровня и т.д. В таких терминах нам нужно объединять побольше деревьев первого уровня, и минимум большого уровня. Делать это будем так: как видим, суммарное к-во объединений было N-1, из которых N/2 - первого уровня, N/4 - второго уровня, N/8 - третьего и т.д. Реализуется эта хитрость крайне просто с помощью рекурсии (при желании её можно разложить в цикл, но для большей понятности я этого делать не буду).
Таким образом, мы сначала объединим изначальные деревья в 2х, а потом уже их будет объединять в деревья побольше. Выигрыш по времени у нас составил в ~10 раз (1000 деревьев). Итого
Подводные камни и минусы
Полезные файлы: Скрипт для отображения дерева: index.php MySQL скрипт для преобразования данных cron.sql PinbaClient.class.php - обертка над pinba для более удобного использования с автоматическим добавлением tree_id, tree_parent_id Так же хочется упомянуть фреймворк onphp, в котором есть нативная поддержка pinba https://github.com/ents/pinba-php-profiler/ - исходные файлы, чтобы поднять все у себя http://pinba.org/ - тут можно скачать pinba Дисклаймер: Данная статья носит популярный характер и не может рассматриваться как руководство к действию. Все действия, описанные ниже не есть истина в последней инстанции, а скорее один из немногих способов сделать визуализацию информации из pinba Ссылки по теме
|
|