Как работают подготовленные заявления?
Вопрос
Я пишу несколько процедур БД и использую подготовленные операторы.Моя среда — PDO с PHP5.
Я понимаю, что подготовленные операторы в первую очередь обеспечивают выигрыш в производительности, а также некоторые вспомогательные бонусы, такие как отсутствие необходимости вручную экранировать входные данные SQL.
Мой вопрос касается производительности.
Ниже приведены две реализации функции getPrice, которая принимает идентификатор продукта и возвращает его цену.
getPrice_A повторно использует один и тот же объект PDOStatement при последующих вызовах в рамках одного и того же выполнения сценария.Это необходимо или рекомендуется?Если да, то есть ли способ избежать дублирования этого дополнительного кода для каждого метода get*() в каждой отдельной модели?
getPrice_B создает новый объект PDOStatement при каждом вызове.Сможет ли СУБД признать, что этот оператор уже подготовлен, и при этом сможет пропустить некоторую работу?Другими словами, правильно ли эта реализация использует преимущества производительности подготовленных операторов?
Написав все это и прочитав, я полагаю, что getPrice_B подойдет, а getPrice_A дает незначительную выгоду, которая может стоить или не стоит дополнительных усложнений.
Хотя хотелось бы услышать наверняка кого-нибудь более знающего.
Предположим, что $pdo
является допустимым подключенным объектом PDO в примерах ниже.
<?php
class Product {
static function &getPrice_A($id) {
static $stmt;
if (!$stmt) {
$stmt = $pdo->prepare('SELECT price FROM products WHERE id = ?');
}
$stmt->execute(array($id));
return $stmt->fetchColumn(0);
}
static function &getPrice_B($id) {
$stmt = $pdo->prepare('SELECT price FROM products WHERE id = ?');
$stmt->execute(array($id));
return $stmt->fetchColumn(0);
}
}
// example usage:
$price = Product::getPrice(4982);
echo "Product 4982 costs $price\n";
Решение
Насколько я понимаю, подготовленные операторы будут повторно использовать сгенерированный план SQL, если это один и тот же оператор, поэтому база данных будет видеть тот же подготовленный оператор, и ей не придется выполнять работу, чтобы выяснить, как выполнить запрос к базе данных.Я бы сказал, что дополнительная работа по сохранению подготовленного оператора в Product::getPrice_A
обычно это не очень полезно, скорее потому, что это может скрыть код, а не проблема с производительностью.Когда речь идет о производительности, я считаю, что всегда лучше сосредоточиться на ясности кода, а затем на производительности, когда у вас есть реальная статистика, указывающая на проблему.
Я бы сказал: «Да, дополнительная работа не нужна» (независимо от того, действительно ли она повышает производительность).Кроме того, я не очень большой эксперт по БД, но о приросте производительности подготовленных операторов я слышал от других, и он происходит на уровне базы данных, а не на уровне кода (поэтому, если код на самом деле вызывает параметризованный оператор в фактическая БД, тогда БД может кэшировать эти планы выполнения...хотя в зависимости от базы данных вы можете получить выгоду даже без параметризованного оператора).
В любом случае, если вы действительно обеспокоены (и видите) проблемы с производительностью базы данных, вам следует рассмотреть решение для кэширования...из которых я очень рекомендую кэширование памяти.С помощью такого решения вы можете кэшировать результаты запросов и даже не обращаться к базе данных для вещей, к которым вы часто обращаетесь.