Поиск слабых мест производительности: анализируем 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):

    [-]
    --- 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)

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

Update: для WordPress 2.6 патч будет выглядеть следующим образом:

[-]
--- wp-db.old.php       2008-07-11 04:49:06.000000000 +0300
+++ wp-db.php   2008-08-03 10:39:14.000000000 +0300
@@ -605,7 +605,16 @@
                if ( defined('SAVEQUERIES') && 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 ( defined('SAVEQUERIES') && SAVEQUERIES )
Добавить в закладки
  • del.ici.ous
  • Digg
  • Furl
  • Google
  • Simpy
  • Spurl
  • Y! MyWeb
  • БобрДобр
  • Мистер Вонг
  • Yandex.Закладки
  • Текст 2.0
  • News2
  • AddScoop
  • RuSpace
  • RUmarkz
  • Memori
  • Google Bookmarks
  • Писали
  • СМИ 2
  • Моё Место
  • 100 Закладок
  • Ваау!
  • Technorati
  • RuCity
  • LinkStore
  • NewsLand
  • Lopas
  • Закладки - IN.UA
  • Connotea
  • Bibsonomy
  • Trucking Bookmarks
  • Communizm
  • UCA
  • Slashdot
  • Magnolia
  • Blogmarks
  • Current
  • Meneame
  • Oknotizie
  • Diigo
  • Funp
  • Hugg
  • Dealspl.us
  • N4G
  • Mister Wong
  • Faves
  • Yigg
  • Fresqui
  • Care2
  • Kirtsy
  • Sphinn
  • SaveThis.ru

Связанные записи

13
Июнь
2008

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

  1. [...] “13 Тэгов, которые следует удалить из вашей темы” SqlMon: плагин для анализа SQL-запросов » Июнь 12, [...]

  2. [...] был получен при помощи плагина SQL Monitor (в целях повышения удобочитаемости, я изменил [...]

  3. Vladimir says:

    Для WordPress 2.6.1 можно использовать тот же самый патч, что и для WordPress 2.6.

  4. igrok54 says:

    Млять, написал такой длинный коммент и не сохранил, как обычно делаю. Получил в результате облом по проверочному коду - время утекло…
    Плагин архиполезный, однозначно.
    Ставил на WP 2.3.3.
    1. В моей теме не хочет выводится, в админке ОК. При переключении на дефолтную - все ОК. Может где-то надо путь к теме указать?
    2. Очень бы пригодился небольшой ФАК по работе с плагином. В частности интересует, что выводится в графе Results - количество запросов к базе?
    Сейчас пытаюсь разобраться, как мне увидеть, что запрос, например:
    SELECT option_value FROM wp_options WHERE option_name = ‘kubrick_header_image’ LIMIT 1
    потребовал столько-то соединений с базой данных, столько-то выделенной памяти и столько-то времени на выполнение…
    Сорри за ламерские вопросы.
    Кстати, возвращаясь к началу своего коммента - при защите от спама, описанной на моем блоге (URL в адресе) капча не нужна, обломов (как у меня произошло) не бывает, и защита от спама 100% - проверено уже годами. Рекомендую.

    • Vladimir says:

      Сорри, как-то я пропустил комментарий

      1. В теме нет вызова wp_footer().
      2. Query — запрос, ErrCode — код ошибки (если запрос неверный) или ноль, Results — количество строк в результате (для SELECT), либо количество затронутых строк (для INSERT/UPDATE/DELETE/REPLACE и т.п.), Time — время, затраченное на выполнение запроса

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

      Используется одно соединение :-)

      столько-то времени на выполнение

      Графа Time.

      столько-то выделенной памяти

      С этим сложнее — данные выбираются уже после того, как отработает функция.

      На мой взгляд, самым полезным является результат в таблице под запросом (EXPLAIN запроса).
      Очень рекомендую прочитать Optimizing Queries with EXPLAIN

Подписаться на RSS-ленту комментариев к статье «SqlMon: плагин для анализа SQL-запросов» Trackback URL: http://blog.sjinks.org.ua/wordpress/plugins/192-sqlmon-plugin-for-sql-query-analysis/trackback/

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

Вы можете использовать данные тэги: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>

Оставляя комментарий, Вы выражаете своё согласие с Правилами комментирования.

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