PDO MySQL: использовать PDO:: ATTR эмулировать готовит или нет?


это то, что я читал до сих пор о PDO::ATTR_EMULATE_PREPARES:

  1. Эмуляция подготовки PDO лучше для производительности, так как собственная подготовка MySQL обходит кэш запросов.
  2. Собственная подготовка MySQL лучше подходит для обеспечения безопасности (предотвращение инъекции SQL).
  3. Собственная подготовка MySQL лучше подходит для отчетов об ошибках.

Я больше не знаю, насколько правдивы все эти утверждения. Моя самая большая забота в выборе MySQL интерфейс предотвращает внедрение SQL. Вторая проблема-это производительность.

мое приложение в настоящее время использует процедурный MySQLi (без подготовленных операторов), и использует кэш запросов совсем немного. Он редко будет повторно использовать подготовленные заявления в одном запросе. Я начал переход к PDO для именованных параметров и безопасности подготовленных операторов.

Я использую MySQL 5.1.61 и PHP 5.3.2

оставить PDO::ATTR_EMULATE_PREPARES включена или нет? Есть ли способ иметь оба производительность кэша запросов и безопасность подготовленных операторов?

5 99

5 ответов:

чтобы ответить на ваши вопросы:

  1. MySQL >= 5.1.17 (или >= 5.1.21 для PREPARE и EXECUTE заявления) можно использовать подготовленные операторы в кэше запросов. Таким образом, ваша версия MySQL+PHP может использовать подготовленные операторы с кэшем запросов. Однако, принять во внимание предостережения для кэширования результатов запроса в документации MySQL. Есть много видов запросов, которые не могут быть кэшированы или которые бесполезны, даже если они кэшируются. В моем опыт кэш запросов не часто очень большой выигрыш в любом случае. Запросы и схемы нуждаются в специальной конструкции, чтобы максимально использовать кэш. Часто кэширование на уровне приложения в конечном итоге оказывается необходимым в любом случае в долгосрочной перспективе.

  2. родной готовит не имеет никакого значения для безопасности. Псевдо-подготовленные операторы все равно будут экранировать значения параметров запроса, это будет просто сделано в библиотеке PDO со строками, а не на сервере MySQL с использованием двоичного кода протокол. Другими словами, один и тот же код PDO будет одинаково уязвим (или не уязвим) для инъекционных атак независимо от вашего EMULATE_PREPARES настройка. Единственное различие заключается в том, где происходит замена параметра--с EMULATE_PREPARES, это происходит в библиотеке PDO; без EMULATE_PREPARES, это происходит на сервере MySQL.

  3. без EMULATE_PREPARES вы можете получить синтаксические ошибки во время подготовки, а не во время выполнения; с EMULATE_PREPARES вы получите только синтаксические ошибки во время выполнения потому что PDO не имеет запроса, чтобы дать MySQL до времени выполнения. Обратите внимание, что это влияет на код вы будете писать! Особенно если вы используете PDO::ERRMODE_EXCEPTION!

еще одно соображение:

  • существует фиксированная стоимость для prepare() (используя собственные подготовленные операторы), поэтому a prepare();execute() С собственными подготовленными операторами может быть немного медленнее, чем выдача простого текстового запроса с использованием эмулированных подготовленных операторов. На многих системы баз данных план запроса для prepare() также кэшируется и может использоваться совместно с несколькими соединениями, но я не думаю, что MySQL делает это. Поэтому, если вы не используете подготовленный объект оператора для нескольких запросов, ваше общее выполнение может быть медленнее.

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

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

/**
 * Return PDO handle for a MySQL connection using supplied settings
 *
 * Tries to do the right thing with different php and mysql versions.
 *
 * @param array $settings with keys: host, port, unix_socket, dbname, charset, user, pass. Some may be omitted or NULL.
 * @return PDO
 * @author Francis Avila
 */
function connect_PDO($settings)
{
    $emulate_prepares_below_version = '5.1.17';

    $dsndefaults = array_fill_keys(array('host', 'port', 'unix_socket', 'dbname', 'charset'), null);
    $dsnarr = array_intersect_key($settings, $dsndefaults);
    $dsnarr += $dsndefaults;

    // connection options I like
    $options = array(
        PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
        PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC
    );

    // connection charset handling for old php versions
    if ($dsnarr['charset'] and version_compare(PHP_VERSION, '5.3.6', '<')) {
        $options[PDO::MYSQL_ATTR_INIT_COMMAND] = 'SET NAMES '.$dsnarr['charset'];
    }
    $dsnpairs = array();
    foreach ($dsnarr as $k => $v) {
        if ($v===null) continue;
        $dsnpairs[] = "{$k}={$v}";
    }

    $dsn = 'mysql:'.implode(';', $dsnpairs);
    $dbh = new PDO($dsn, $settings['user'], $settings['pass'], $options);

    // Set prepared statement emulation depending on server version
    $serverversion = $dbh->getAttribute(PDO::ATTR_SERVER_VERSION);
    $emulate_prepares = (version_compare($serverversion, $emulate_prepares_below_version, '<'));
    $dbh->setAttribute(PDO::ATTR_EMULATE_PREPARES, $emulate_prepares);

    return $dbh;
}

будьте осторожны при отключении PDO::ATTR_EMULATE_PREPARES (включение родной готовится) когда ваш PHP pdo_mysql не компилируется против mysqlnd.

потому что старый libmysql не полностью совместим с некоторыми функциями, это может привести к странным ошибкам, например:

  1. потеря наиболее значимых битов для 64-битных целых чисел при привязке как PDO::PARAM_INT ( 0x12345678AB будет обрезан до 0x345678AB на 64-битной машине)
  2. невозможность сделать простые запросы, такие как LOCK TABLES ( она бросает SQLSTATE[HY000]: General error: 2030 This command is not supported in the prepared statement protocol yet исключения )
  3. нужно извлечь все строки из результата или закрыть курсор перед следующим запросом ( с mysqlnd или emulated готовит его автоматически делает эту работу для вас и не выходит из синхронизации с сервером mysql)

эти ошибки я выяснил в моем простом проекте при миграции на другой сервер, который использовал libmysql на pdo_mysql модуль. Может быть, есть гораздо больше ошибок, я не знаю. Также я тестировал на свежем 64-битном debian jessie, все перечисленные ошибки возникают, когда я apt-get install php5-mysql, и исчезнет, когда я apt-get install php5-mysqlnd.

, когда PDO::ATTR_EMULATE_PREPARES имеет значение true (по умолчанию) - эти ошибки не происходят в любом случае, потому что PDO вообще не использует подготовленные операторы в этом режиме. Так что, если вы используете pdo_mysql на основе libmysql (подстрока " mysqlnd "не отображается в поле" версия API клиента"pdo_mysql раздел в phpinfo) - вы не должны включить PDO::ATTR_EMULATE_PREPARES выкл.

Я бы отключил эмуляцию подготовок, когда вы запускаете 5.1, что означает, что PDO будет использовать преимущества собственной подготовленной функциональности оператора.

PDO_MYSQL будет использовать собственную поддержку подготовленных операторов, присутствующую в MySQL 4.1 и выше. Если вы используете более старую версию клиентских библиотек mysql, PDO будет эмулировать их для вас.

http://php.net/manual/en/ref.pdo-mysql.php

Я бросил MySQLi для PDO для подготовленных именованных операторов и лучшего API.

однако, чтобы быть сбалансированным, PDO работает незначительно медленнее, чем MySQLi, но это то, что нужно иметь в виду. Я знал это, когда сделал выбор, и решил, что лучший API и использование отраслевого стандарта более важны, чем использование незначительно более быстрой библиотеки, которая привязывает вас к определенному движку. FWIW я думаю, что команда PHP также благоприятно смотрит на PDO над MySQLi в будущем.

Я бы рекомендовал включить реальную базу данных PREPARE вызовы, поскольку эмуляция не ловит все.. например, он будет готовить INSERT;!

var_dump($dbh->prepare('INSERT;'));
$dbh->setAttribute(PDO::ATTR_EMULATE_PREPARES, false);
var_dump($dbh->prepare('INSERT;'));

выход

object(PDOStatement)#2 (1) {
  ["queryString"]=>
  string(7) "INSERT;"
}
bool(false)

Я с удовольствием возьму хит производительности для кода, который действительно работает.

FWIW

версия PHP: PHP 5.4.9-4ubuntu2. 4 (cli)

версия MySQL: 5.5.34-0ubuntu0

зачем переключать эмуляцию на 'false'? Основная причина этого заключается в том, что если компонент database engine выполняет подготовку вместо PDO, то запрос и фактические данные отправляются отдельно, что повышает безопасность. Это означает, что когда параметры передаются в запрос, попытки ввести SQL в них блокируются, так как подготовленные операторы MySQL ограничены одним запросом. Это означает, что истинная подготовленная инструкция не будет выполнена при передаче второго запроса в a параметр.

главным аргументом против использования компонента database engine для prepare vs PDO являются две поездки на сервер-одна для подготовки, а другая для передачи параметров – но я думаю, что добавленная безопасность стоит того. Кроме того, по крайней мере, в случае MySQL, кэширование запросов не было проблемой с версии 5.1.

https://michaelseiler.net/2016/07/04/dont-emulate-prepared-statements-pdo-mysql/