RSS   Twitter   Copiny   Copiny
Нашел ошибку? 

Выдели фрагмент текста с ошибкой или неточностью и нажми Ctrl+Enter!

Время выполнения SQL-запросов в debug информации и место их вызова

расширенная отладка SQL запросов в Joomla

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

Стандартная Joomla-отладка выдаёт только список выполненных SQL-запросов:

  1. SELECT *, RAND() AS ordering 
    FROM jos_banner 
    WHERE showBanner = 1 
    AND (imptotal = 0 OR impmade < imptotal) 
    AND cid = 1 
    AND catid = 33 
    ORDER BY sticky DESC, ordering 
    LIMIT 0, 1
  2. UPDATE jos_banner 
    SET impmade = impmade + 1 
    WHERE bid = 7

Эту ситуацию можно исправить, но придется использовать хак части ядра Joomla.

Добавим в debug информацию о времени выполнения каждого SQL-запроса и место вызова этого запроса. Для этого необходимо отредактировать файл

<корень сайта>/libraries/joomla/database/database/mysql.php : 206

либо для базы данных MySQLi

<корень сайта>/libraries/joomla/database/database/mysqli.php : 222

В оригинале функция выглядит примерно следующим образом:

/**
 * Execute the query
 *
 * @access  public
 * @return mixed A database resource if successful, FALSE if not.
 */
function query()
{
    if (!is_resource($this->_resource)) {
        return false;
    }

    // Take a local copy so that we don't modify the original query and cause issues later
    $sql = $this->_sql;
    if ($this->_limit > 0 || $this->_offset > 0) {
        $sql .= ' LIMIT ' . max($this->_offset, 0) . ', ' . max($this->_limit, 0);
    }
    if ($this->_debug) {
        $this->_ticker++;
        $this->_log[] = $sql;
    }
    $this->_errorNum = 0;
    $this->_errorMsg = '';
    $this->_cursor = mysql_query( $sql, $this->_resource );

    if (!$this->_cursor)
    {
        $this->_errorNum = mysql_error( $this->_resource );
        $this->_errorMsg = mysql_error( $this->_resource )." SQL=$sql";

        if ($this->_debug) {
            JError::raiseError(500, 'JDatabaseMySQL::query: '.$this->_errorNum.' - '.$this->_errorMsg );
        }
        return false;
    }
    return $this->_cursor;
}

Используя функцию debug_backtrace и стандартную функцию microtime() добавим необходимую нам информацию в 

<?php
function query() {
    
    if (!is_resource($this->_resource)) {
        return false;
    }

    // Take a local copy so that we don't modify the original query and cause issues later
    $sql = $this->_sql;
    if ($this->_limit > 0 || $this->_offset > 0) {
        $sql .= ' LIMIT '.$this->_offset.', '.$this->_limit;
    }

    $this->_errorNum = 0;
    $this->_errorMsg = '';
    if ($this->_debug) {
        $sql_start = round(microtime(), 6);
    };
    
    $this->_cursor = mysql_query( $sql, $this->_resource );
    
    if ($this->_debug) {
        $sql_end = round(microtime(), 6);
        $this->_ticker++;
        $backtrace = debug_backtrace();
        $bcktrc_add = '';
        if (is_array($backtrace)) {
            for ($i = 1; $i < 3; $i++) {
                if (isset($backtrace[$i]) and is_array($backtrace[$i])) {
                    if (isset($backtrace[$i]['file']) and isset($backtrace[$i]['line'])) {
                        $bcktrc_add .= str_replace(JPATH_BASE, '', $backtrace[$i]['file']).':'.$backtrace[$i]['line']."\n";
                    }
                }
            }
        }
        $this->_log[] = $bcktrc_add.'Execute time:'.round(($sql_end - $sql_start), 6)." sec.\n".$sql;
    }

    if (!$this->_cursor) {
        $this->_errorNum = mysql_error( $this->_resource );
        $this->_errorMsg = mysql_error( $this->_resource )." SQL=$sql";
        if ($this->_debug) {
            JError::raiseError(500, 'JDatabaseMySQL::query: '.$this->_errorNum.' - '.$this->_errorMsg );
        }
        return false;
    }
    return $this->_cursor;
}

После данных изменений мы уже получим нужную нам информацию:

  1. /components/com_banners/models/banner.php:79
    /modules/mod_banners/helper.php:39
    Execute time:0.000818 sec.
    SELECT *, RAND() AS ordering 
    FROM jos_banner 
    WHERE showBanner = 1 
    AND (imptotal = 0 OR impmade < imptotal) 
    AND cid = 1 
    AND catid = 33 
    ORDER BY sticky DESC, ordering 
    LIMIT 0, 1
  2. /modules/mod_banners/helper.php:40
    /modules/mod_banners/mod_banners.php:23
    Execute time:0.000117 sec.
    UPDATE jos_banner 
    SET impmade = impmade + 1 
    WHERE bid = 7

Замечание: Иногда может потребоваться увеличить глубину вывода точек вызова функций (например Community Builder создаёт свой класс для работы с базой данных, поэтому потребуется заглянуть поглубже), для этого в нашей новой функции в строчке

for ($i = 1; $i < 3; $i++) {


цифру "3" увеличиваем например до "6", тогда будет выведено "5" ("6"-1) точек вызова.

Смотрите также:
Комментарии (1) Добавить комментарий
  • Dimon
    Dimon
    29 Ноября 2012, 16:14
     ↑  +3  ↓     ответ

    А как это же сделать, но для Joomla 2.5? Я не найду ни файлов /libraries/joomla/database/database/mysql.php ни функции query()...

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




* обязательно для заполнения

1 введенный почтовый адрес используется только для обратной связи при ответах в комментариях и сервиса gravatar.com
.