Действительно ли подготовленные Операторы являются отходами для нормальных запросов? (PHP)

Еще больше отражения...

catch (TargetInvocationException tiex)
{
    // Get the _remoteStackTraceString of the Exception class
    FieldInfo remoteStackTraceString = typeof(Exception)
        .GetField("_remoteStackTraceString",
            BindingFlags.Instance | BindingFlags.NonPublic); // MS.Net

    if (remoteStackTraceString == null)
        remoteStackTraceString = typeof(Exception)
        .GetField("remote_stack_trace",
            BindingFlags.Instance | BindingFlags.NonPublic); // Mono

    // Set the InnerException._remoteStackTraceString
    // to the current InnerException.StackTrace
    remoteStackTraceString.SetValue(tiex.InnerException,
        tiex.InnerException.StackTrace + Environment.NewLine);

    // Throw the new exception
    throw tiex.InnerException;
}

Имеют в виду, что это может повредиться в любое время, поскольку частные поля не являются частью API. Посмотрите дальнейшее обсуждение Моно bugzilla.

11
задан Xeoncross 5 December 2009 в 01:17
поделиться

8 ответов

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

если вы используете подготовленные операторы как ЕДИНСТВЕННЫЙ способ поместить данные, предоставленные пользователем, в запрос, тогда они абсолютно пуленепробиваемые, когда дело касается SQL-инъекций.

6
ответ дан 3 December 2019 в 10:04
поделиться

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

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

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

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

1
ответ дан 3 December 2019 в 10:04
поделиться

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

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

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

1
ответ дан 3 December 2019 в 10:04
поделиться

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

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

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

1
ответ дан 3 December 2019 в 10:04
поделиться

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

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

0
ответ дан 3 December 2019 в 10:04
поделиться

Похоже, я не нахожу никаких хороших преимуществ в использовании постоянных соединений или подготовленных операторов для этой материи. Посмотрите на эти числа - для 6000 операторов select (чего никогда не произойдет в запросе страницы!) Вы едва заметите разницу. Большинство моих страниц используют менее 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
ответ дан 3 December 2019 в 10:04
поделиться

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

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

0
ответ дан 3 December 2019 в 10:04
поделиться

Когда вы выполняете оператор 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, какой код вам больше всего понравится? ; -)

1
ответ дан 3 December 2019 в 10:04
поделиться
Другие вопросы по тегам:

Похожие вопросы: