Ars Longa, Vita Brevis

Июнь 13, 2008

SqlMon: плагин для анализа SQL-запросов

Рубрика: WordPress
Метки: , , , ,
Vladimir @ 3:17 дп
RSS 2.0

Поиск слабых мест производительности: анализируем SQL-запросы

Меня всегда интересовало, насколько эффективно WordPress работает с базой данных, и насколько хорошо спроектирована база данных.

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

Однажды столкнувшись с ужасной производительностью WordPress и не имея возможности анализировать код десятка поставленных плагинов, я стал решать задачу иначе, в результате чего родился плагин для анализа SQL-запросов.

Принцип работы. Плагин устанавливает обработчики действий, которые позволяют перехватить запрос к базе данных непосредственно перед его выполнением. Если перехваченный запрос — SELECT, UPDATE или DELETE, то скрипт пытается его "объяснить" — выполнить EXPLAIN над запросом. Предвидя возражения, что EXPLAIN работает только с SELECT, но никак не с DELETE и UPDATE, поясняю: запросы DELETE и UPDATE переписываются в SELECT, например:

[-]
View Code MySQL
DELETE `t1`
    FROM `t1`
        INNER JOIN `t2`
            ON `t1`.`id` = `t2`.`id`
    WHERE `t2`.`key` = 'value'
    LIMIT 5

будет переписан в

[-]
View Code MySQL
EXPLAIN SELECT *
    FROM `t1`
        INNER JOIN `t2`
            ON `t1`.`id` = `t2`.`id`
    WHERE `t2`.`key` = 'value'
    LIMIT 5

Возможно, переписывание работает не всегда, но такие случаи мне еще не встречались

Я предполагаю, что Вы понимаете, для чего нужен EXPLAIN и как следует понимать результаты, которые он выдаёт. Если это не так, то очень рекомендую прочитать статью "Optimizing Queries with EXPLAIN", а затем вернуться к данной статье.

Плагин в футере темы (кстати, это относится и к админке) выдаст подробный лог запросов с их объяснением. Это может выглядеть примерно так:

Скачать SqlMon.

Установка. К сожалению, установка данного плагина полностью не автоматизируется: кое-что надо делать вручную. После активации плагина в WordPress нужно выполнить следующие действия:

  1. Добавить в файл /wp-config.php следующие строки:
    [-]
    View Code PHP
    define('SQLMON_ENABLED', true);
    $sqlmon_allowed_ips = array('70.87.222.86', '195.10.218.132', '127.0.0.1');

    Первая строка активирует режим перехвата и анализа запросов, вторая строка содержит массив с IP-адресами, которым разрешен просмотр лога запросов. Естественно, нужно указать свои адреса :-)

  2. Далее предстоит пропатчить один файлик WordPress: /wp-includes/wp-db.php. Сразу объясню, зачем это нужно: WordPress не предоставляет нормальных возможностей отловить запрос до его выполнения. Фильтр query для наших целей не подходит: во-первых, последующий плагин может переписать запрос, во-вторых, я уже насмотрелся на плагины, которые лезут переписывать запрос, если видят SELECT. Проверено, что переписывание EXPLAIN SELECT такими плагинами приводит к синтаксической ошибке.

    Теперь о том, что нужно менять. В файле /wp-includes/wp-db.php есть класс wpdb, в котором есть метод query. В этом методе нужно найти строки

    [-]
    View Code PHP
    // Perform the query via std mysql_query function..
    if (SAVEQUERIES)
        $this->timer_start();
     
    $this->result = @mysql_query($query, $this->dbh);
     
    ++$this->num_queries;

    и изменить их:

    [-]
    View Code PHP
    // Perform the query via std mysql_query function..
    if (SAVEQUERIES)
        $this->timer_start();
     
    if (true == function_exists('do_action')) {
        do_action('before_query', $query, $this->dbh);
    }
     
    $this->result = @mysql_query($query, $this->dbh);
     
    if (true == function_exists('do_action')) {
        do_action('after_query', $query, $this->dbh);
    }
     
    ++$this->num_queries;

    Для тех, кто предпочитает иметь дело с патчами, привожу патч в формате unified diff (внимание: патч проверялся только на WordPress 2.5.1):

    [-]
    View Code Diff
    --- wp-db.old.php       2008-03-21 01:34:32.000000000 +0200 
    +++ wp-db.php   2008-06-13 03:02:27.000000000 +0300 
    @@ -272,7 +272,16 @@ 
                    if (SAVEQUERIES) 
                            $this->timer_start(); 
     
    +               if (true == function_exists('do_action')) { 
    +                       do_action('before_query', $query, $this->dbh); 
    +               } 
    + 
                    $this->result = @mysql_query($query, $this->dbh); 
    +                
    +               if (true == function_exists('do_action')) { 
    +                       do_action('after_query', $query, $this->dbh); 
    +               } 
    + 
                    ++$this->num_queries; 
     
                    if (SAVEQUERIES)

После этого перезагружаем страницу и смотрим :-)

Комментарии к статье "SqlMon: плагин для анализа SQL-запросов" »

К статье "SqlMon: плагин для анализа SQL-запросов" комментариев нет. Может быть, Вы хотите прокомментировать статью?

RSS лента комментариев к этой записи. TrackBack URL

Оставить комментарий к записи "SqlMon: плагин для анализа SQL-запросов"

Подписаться, не комментируя