Подготовленные заявления являются отходами для обычных запросов? (PHP)

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

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

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

MySQL:

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

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

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

: UPDATE:

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

: UPDATE2:

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

: Update3:

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

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

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

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

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

Спасибо OIS за то, что он окончательно дал мне ссылку на эту проблему.

Ответ 1

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

если вы используете подготовленные операторы как ТОЛЬКО способ поместить данные, предоставленные пользователем в запрос, тогда они абсолютно пуленепробиваемы, когда дело доходит до SQL-инъекции.

Ответ 2

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

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

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

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

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


$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, какой код будет вашим любимым?; -)

Ответ 3

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

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

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

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

Ответ 4

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

В реальном мире я редко когда-либо пишу DML-запросы. Все мои INSERTS/UPDATES автоматически создаются слоем абстракции и выполняются простым передачей входного массива. Во всех смыслах и целях для подготовки запросов и последующего их выполнения действительно не существует "производительности" (за исключением латентности подключения в исходном PREPARE). Но при использовании соединения UDS (Unix Domain Socket) вы не будете замечать (или даже сравнивать). Обычно он составляет порядка нескольких микросекунд.

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

Ответ 5

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

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

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

Ответ 6

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

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

Ответ 7

Кажется, я не нахожу никаких хороших преимуществ для использования постоянных подключений - или подготовленных операторов для этого матера. Посмотрите на эти цифры - для 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';

Ответ 8

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

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