Являются ли подготовленные отчеты отходами для обычных запросов? (PHP) - PullRequest
11 голосов
/ 05 декабря 2009

В настоящее время «Подготовленные заявления» кажутся единственным способом, которым кто-либо рекомендует отправлять запросы в базу данных. Я даже вижу рекомендации использовать подготовленные операторы для хранимых процедур. Тем не менее, для дополнительного запроса подготовленные операторы требуют - и короткое время, которое они длятся - я убежден, что они полезны только для строки запросов INSERT / UPDATE.

Я надеюсь, что кто-то может поправить меня в этом, но это похоже на повторение всей CSS-таблицы "Таблицы - зло". Таблицы являются злыми, только если используются для макетов, а не для табличных данных. Использование DIV для табличных данных является нарушением стиля WC3.

Точно так же простой SQL (или сгенерированный из AR), кажется, гораздо более полезен для 80% используемых запросов, которые на большинстве сайтов представляют собой один SELECT, чтобы не повторять эту загрузку страницы (я говорю о скриптовых языках, таких как PHP здесь). Зачем мне заставлять мою БД с завышенными налогами подготовить заявление о том, что его нужно запустить только один раз, прежде чем удалить?

MySQL:

Подготовленное заявление относится к сеанс, в котором он был создан. Если вы прекратите сеанс без освободить ранее подготовленный заявление, сервер освобождает его автоматически.

Таким образом, в конце вашего скрипта PHP автоматически закроет соединение, и вы потеряете подготовленный оператор только для того, чтобы ваш скрипт заново его создал при следующей загрузке.

Я что-то упустил или это просто способ снизить производительность?

: UPDATE:

Меня осенило, что я предполагаю новые соединения для каждого сценария. Я бы предположил, что если постоянное соединение используется, то эти проблемы исчезнут. Это правильно?

: UPDATE2:

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

: Update3:

Большинство людей просто повторяют фразу «подготовленные операторы защищают от SQL-инъекций», которая не в полной мере объясняет проблему. Предоставленный метод escape для каждой библиотеки БД также защищает от внедрения SQL . Но это еще не все:

При отправке запроса обычным способом, клиент (скрипт) конвертирует данные в строки , которые затем передаются сервер БД. Сервер БД затем использует Мощность процессора в преобразовать их обратно в правильный двоичный тип данных. Затем движок базы данных анализирует оператор и ищет синтаксические ошибки.

При использовании подготовленных заявлений ... данные отправляются в собственном двоичном виде, который сохраняет конверсию-использование процессора, и делает передачу данных более эффективный. Очевидно, это также уменьшить использование полосы пропускания, если клиент не расположен рядом с сервером БД.

... Типы переменных предопределены, и, следовательно, MySQL принять во внимание эти персонажи и им не нужны чтобы избежать.

http://www.webdesignforums.net/showthread.php?t=18762

Спасибо OIS за то, что, наконец, поставили меня в тупик по этому вопросу.

Ответы [ 8 ]

6 голосов
/ 05 декабря 2009

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

если вы используете подготовленные операторы в качестве ЕДИНСТВЕННОГО способа поместить предоставленные пользователем данные в запрос, то они являются абсолютно пуленепробиваемыми, когда дело доходит до внедрения SQL.

1 голос
/ 05 декабря 2009

Выигрыш в производительности достигается не за счет меньшего разбора, а за счет необходимости рассчитывать пути доступа один раз, а не повторно. Это очень помогает, когда вы отправляете тысячи запросов.

Учитывая очень простой оптимизатор / планировщик mysql, это может представлять меньшую проблему, чем при использовании более зрелой базы данных с гораздо более сложными оптимизаторами.

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

1 голос
/ 05 декабря 2009

Ответ связан с безопасностью и абстракцией. Все остальные уже упоминали о безопасности, но реальный плюс в том, что ваш ввод полностью абстрагирован от самого запроса. Это учитывает истинный агностицизм базы данных при использовании уровня абстракции, тогда как встраивание входных данных обычно зависит от базы данных. Если вы заботитесь о переносимости, готовые заявления - это путь.

В реальном мире я редко пишу запросы DML. Все мои INSERTS / UPDATES автоматически создаются слоем абстракции и выполняются путем простой передачи входного массива. Для всех намерений и целей действительно нет никакого «снижения производительности» для подготовки запросов и их последующего выполнения (за исключением задержки соединения в начальной ПОДГОТОВКЕ). Но при использовании соединения UDS (Unix Domain Socket) вы не заметите (или даже не сможете провести сравнительный анализ) разницу. Обычно это порядка нескольких микросекунд.

Учитывая недостатки безопасности и абстракции, я бы вряд ли назвал это расточительным.

1 голос
/ 05 декабря 2009

Подготовленные операторы пригодятся в нескольких ситуациях:

  • Отличное разделение данных запроса от ненадежных пользовательских данных.
  • Увеличение производительности при многократном выполнении одного и того же запроса
  • Увеличение производительности при передаче двоичных данных, так как подготовленный оператор может использовать двоичный протокол, тогда как традиционный запрос в конечном итоге будет выполнять кодирование итакие.

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

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

1 голос
/ 05 декабря 2009

Когда вы выполняете оператор SQL в базе данных, анализатор SQL должен предварительно проанализировать его, что в точности совпадает с процессом подготовки.

Таким образом, сравнение выполнения SQL-операторов напрямую с подготовкой и выполнением не имеет недостатков, но имеет некоторые преимущества:

  • Прежде всего, как уже указывалось в longneck, передача пользовательского ввода в подготовленный оператор автоматически ускользает от ввода. Как будто база данных подготовила фильтры для значений и пропускает только те значения, которые подходят.

  • Во-вторых, если тщательно использовать подготовленные операторы, и вы попадаете в ситуацию, когда вам нужно выполнить его несколько раз, вам не нужно переписывать код для подготовки и выполнения, но вы просто выполняете его.

  • В-третьих: код становится более читабельным, если все сделано правильно:


$sql = 'SELECT u.id, u.user, u.email, sum(r.points)
        FROM users u
        LEFT JOIN reputation r on (u.id=r.user_id)
        LEFT JOIN badge b on (u.id=b.user_id and badge=:badge)
        WHERE group=:group';

$params = array(
    ':group' => $group, 
    ':badge' => $_GET['badge']
);

$stmt = $pdo->prepare($sql);
$result = $stmt->execute($params);

вместо


$sql = 'SELECT u.id, u.user, u.email, sum(r.points)
        FROM users u
        LEFT JOIN reputation r on (u.id=r.user_id)
        LEFT JOIN badge b on (u.id=b.user_id and badge="'.mysql_real_escape_string($_GET['badge']).'")
        WHERE group="'.mysql_real_escape_string($group).'"';

$result = mysql_query($sql);

Представьте, что вам пришлось изменить оператор SQL, какой код был бы вашим любимым? ; -)

0 голосов
/ 05 декабря 2009

Кэсси права. Если вы не подготовите / скомпилируете его, dbms в любом случае должен будет его запустить.

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

0 голосов
/ 05 декабря 2009

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

ОБНОВЛЕНО Я только что пересмотрел свой тест на включают 4k SELECT и 4k INSERT заявления! Запустите его сами и дайте мне знать, есть ли какие-либо ошибки проектирования.

Возможно, разница будет больше, если мой сервер MySQL не будет работать на той же машине, что и Apache.

Persistent: TRUE
Prepare: TRUE
2.3399310112 seconds

Persistent: FALSE
Prepare: TRUE
2.3265211582184 seconds

Persistent: TRUE
Prepare: FALSE
2.3666892051697 seconds

Persistent: FALSE
Prepare: FALSE
2.3496441841125 seconds

Вот мой тестовый код:

$hostname = 'localhost';
$username = 'root';
$password = '';
$dbname = 'db_name';

$persistent = FALSE;
$prepare = FALSE;

try 
{

    // Force PDO to use exceptions for all errors
    $attrs = array(PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION);

    if($persistent) 
    { 
        // Make the connection persistent
        $attrs[PDO::ATTR_PERSISTENT] = TRUE;
    }

    $db = new PDO("mysql:host=$hostname;dbname=$dbname", $username, $password, $attrs);

    // What type of connection?
    print 'Persistent: '.($db->getAttribute(PDO::ATTR_PERSISTENT) ? 'TRUE' : 'FALSE').'<br />';
    print 'Prepare: '.($prepare ? 'TRUE' : 'FALSE').'<br />';

    //Clean table from last run
    $db->exec('TRUNCATE TABLE `pdo_insert`');

}
catch(PDOException $e)
{
    echo $e->getMessage();
}

$start = microtime(TRUE);

$name = 'Jack';
$body = 'This is the text "body"';

if( $prepare ) {

    // Select
    $select = $db->prepare('SELECT * FROM pdo_insert WHERE id = :id');
    $select->bindParam(':id', $x);

    // Insert
    $insert = $db->prepare('INSERT INTO pdo_insert (`name`, `body`, `author_id`) 
    VALUES (:name, :body, :author_id)');
    $insert->bindParam(':name', $name);
    $insert->bindParam(':body', $body);
    $insert->bindParam(':author_id', $x);


    $run = 0;
    for($x=0;$x<4000;++$x) 
    {
        if( $insert->execute() && $select->execute() ) 
        {
            $run++;
        }
    }

}
else
{

    $run = 0;
    for($x=0;$x<4000;++$x) {

        // Insert
        if( $db->query('INSERT INTO pdo_insert (`name`, `body`, `author_id`) 
        VALUES ('.$db->quote($name).', '. $db->quote($body).', '. $db->quote($x).')') 

        AND

        // Select
        $db->query('SELECT * FROM pdo_insert WHERE id = '. $db->quote($x)) )
        {
            $run++;
        }

    }

}





print (microtime(true) - $start).' seconds and '.($run * 2).' queries';
0 голосов
/ 05 декабря 2009

При использовании SQL-запросов, таких как SELECT x,y,z FROM foo WHERE c='mary had a little lamb', сервер должен анализировать SQL-оператор, включающий данные +, вы должны очистить часть "mary had ..." (вызов mysql_real_escape () или аналогичный для каждого параметра). Используя подготовленные операторы, сервер также должен анализировать оператор, но без данных, и отправляет обратно только идентификатор для оператора (крошечный крошечный пакет данных). Затем вы отправляете фактические данные без предварительной очистки. Я не вижу здесь накладных расходов, хотя я свободно признаю, что никогда не проверял это. У тебя есть? ;-)

edit: И использование подготовленных операторов может избавить от необходимости преобразовывать каждый параметр (вход / выход) в строки. Возможно, даже больше, если ваша версия php использует mysqlnd (вместо "старой" клиентской библиотеки libmysql). Также не проверял аспект производительности.

...