PHP PDO: как делает переподготовку работы влияния заявления

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

Я предполагаю, что главный вопрос: Как делает переподготовку той же работы заявления MySql, PDO волшебно признает заявление (таким образом, я не буду иметь к), и прекратите операцию?

В противном случае я планирую достигнуть, делают это, производя уникальный ключ для каждого различного вопроса и держат подготовленные заявления в частном множестве в объекте базы данных - под его уникальным ключом. Я планирую получить множество, вводят один из следующих путей (ни один из которого мне нравится). В порядке предпочтения:

  • сделайте, чтобы программист передал дополнительное, всегда тот же параметр, называя метод - что-то вроде basename(__FILE__, ".php") . __LINE__ (этот метод работал бы, только если наш метод называют в петле - который имеет место большую часть времени, эта функциональность необходима),
  • сделайте, чтобы программист передал полностью случайную последовательность (скорее всего, произведенный заранее) как дополнительный параметр
  • используйте сам переданный вопрос, чтобы произвести ключ - получение мешанины вопроса или чего-то подобного
  • достигните того же как первой пули (выше), звоня debug_backtrace

У кого-либо есть подобный опыт? Хотя система, на которую я работаю, действительно заслуживает некоторого внимания к оптимизации (это довольно большое и растет каждую неделю), возможно, я не волнуюсь ни о чем и нет никакой исполнительной выгоды в выполнении, что я делаю?

9
задан raveren 25 January 2010 в 13:24
поделиться

4 ответа

[1139723-

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

$ php -v
$ PHP 5.3.0-3 with Suhosin-Patch (cli) (built: Aug 26 2009 08:01:52)
$ ...
$ php benchmark.php
$ PHP hashing: 0.19465494155884 [microtime]
$ MD5 hashing: 0.57781004905701 [microtime]
$ 799994

код:

<?php
error_reporting(E_ALL);

$queries = array("SELECT",
                 "INSERT",
                 "UPDATE",
                 "DELETE",
                 );
$query_length = 256;
$num_queries  = 256;
$iter = 10000;

for ($i = 0; $i < $num_queries; $i++) {
    $q = implode('',
           array_map("chr",
             array_map("rand",
                       array_fill(0, $query_length, ord("a")),
                       array_fill(0, $query_length, ord("z")))));
    $queries[] = $q;
}

echo count($queries), "\n";

$cache = array();
$side_effect1 = 0;
$t = microtime(true);
for ($i = 0; $i < $iter; $i++) {
    foreach ($queries as $q) {
        if (!isset($cache[$q])) {
            $cache[$q] = $q;
        }
        else {
            $side_effect1++;
        }
    }
}
echo microtime(true) - $t, "\n";

$cache = array();
$side_effect2 = 0;
$t = microtime(true);
for ($i = 0; $i < $iter; $i++) {
    foreach ($queries as $q) {
        $md5 = md5($q);
        if (!isset($cache[$md5])) {
            $cache[$md5] = $q;
        }
        else {
            $side_effect2++;
        }
    }
}
echo microtime(true) - $t, "\n";

echo $side_effect1 + $side_effect2, "\n";
1
ответ дан 4 December 2019 в 19:33
поделиться
- [11135275-

MySQL (например, большинство СУБД) будут кэшировать планы выполнения для подготовленных утверждений, поэтому, если пользователь A создает план для:

SELECT * FROM some_table WHERE a_col=:v1 AND b_col=:v2

(где v1 и v2 - связывание vars), затем отправляет Значения, которые должны быть интерполированы СУБД, то пользователь B отправляет тот же запрос (но с разными значениями интерполяции) СУБД не приходится регулировать план. I.e. Это СУБД, который находит соответствующий план - а не PDO.

Однако это означает, что каждая операция в базе данных требуется как минимум 2 круглых поездок (1st, чтобы представить запрос, второй для представления BIND vars), в отличие от одной открытой поездки для запроса с буквальными значениями, то это вводит Дополнительные сети сети. Существует также небольшая стоимость участия в разграничении (и поддержании) кэш запроса / плана.

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

В то время как (по моему опыту) определенно кажется преимуществом производительности, используя подготовленные заявления с Oracle, я не убежден, что то же самое верно для MySQL - однако, многое будет зависеть от структуры вашей базы данных и Сложность запроса (или более конкретно, сколько различных вариантов оптимизатор может найти для разрешения запроса).

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

6
ответ дан 4 December 2019 в 19:33
поделиться

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

После этого я придумал код с кэшированными подготовленными операторами:

function DB($query)
{
    static $db = null;
    static $result = array();

    if (is_null($db) === true)
    {
        $db = new PDO('sqlite:' . $query, null, null, array(PDO::ATTR_ERRMODE => PDO::ERRMODE_WARNING));
    }

    else if (is_a($db, 'PDO') === true)
    {
        $hash = md5($query);

        if (empty($result[$hash]) === true)
        {
            $result[$hash] = $db->prepare($query);
        }

        if (is_a($result[$hash], 'PDOStatement') === true)
        {
            if ($result[$hash]->execute(array_slice(func_get_args(), 1)) === true)
            {
                if (stripos($query, 'INSERT') === 0)
                {
                    return $db->lastInsertId();
                }

                else if (stripos($query, 'SELECT') === 0)
                {
                    return $result[$hash]->fetchAll(PDO::FETCH_ASSOC);
                }

                else if ((stripos($query, 'UPDATE') === 0) || (stripos($query, 'DELETE') === 0))
                {
                    return $result[$hash]->rowCount();
                }

                else if (stripos($query, 'REPLACE') === 0)
                {
                }

                return true;
            }
        }

        return false;
    }
}

Так как мне не нужно беспокоиться о коллизиях в запросах, я в итоге использовал md5() вместо sha1().

4
ответ дан 4 December 2019 в 19:33
поделиться

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

Если вы хотите создать кэш подготовленных запросов, самый простой способ IMHO будет для MD5-HASH CHERE STRING RING и генерируют таблицу поиска.

OTOH: Сколько запросов вы выполняете (в минуту)? Если меньше нескольких сотен, вы только усложняете код, усиление производительности будет незначительным.

1
ответ дан 4 December 2019 в 19:33
поделиться
Другие вопросы по тегам:

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