31 марта 2012 в 18:44

MySQLi раскладываем все по полочкам из песочницы

PHP*

Для кого это статья? Первоочередной целью написания статьи было именно «разложить все по полочкам» для тех, кто уже работал с mysqli, но не вникал глубоко, а быстренько написал свои обертки и забыл про оригинальный синтаксис. Я постарался разъяснить нюансы, с которым столкнулся сам, при переносе данных из большой и очень старой БД, спроектированной человеком, не знающим про нормализации, в новую, с сильно изменившейся структурой.

Можно ли читать эту статью людям, которые все еще используют старое расширение mysql и только думающие об перехода на PDO или MySqli? Думаю даже нужно.


MySqli или PDO


Последние годы я писал сайты исключительно на фреймворках, что избавляло меня от работы с БД напрямую. Некоторое время назад начал работу над сайтом на чистом php и задался вопросом, что использовать вместо устаревшего и нерекомендованного к использованию старого расширения PHP MySQL.

Выбирать нужно было между MySqli и PDO. После не очень длительного изучения решил остановиться на MySqli, так как, как мне тогда казалось, он полностью идентичен PDO, за исключением того, что нет возможности отказаться от MySQL в пользу чего-то другого. Как я напишу ниже это не совсем так, минимум одно заметное отличие есть.

MySqli рекомендован к использованию самими разработчиками PHP.[1]

ООП и процедурный интерфейс


MySqli позволяет писать код как в ООП стиле так и в процедурном. Мне ближе ООП как и большинству из хабр сообщества, поэтому в этом статье будет использован именно он.

Три основные класса


MySqli имеет 3 основные класса, которые будут подробно рассмотрены в этой статье
  1. mysqli — необходим для установки соединения с БД и будет полезен, если мы хотим выполнить запросы так, как мы это делали в старом расширении MySQL;
  2. mysqli_stmt — необходим для использования новой возможности MySqli: выполнять запросы по подготовленным выражениям;
  3. mysqli_result — объединяет функции для получения результатов запросов, сделанных с помощью mysqli или mysqli_stmt.

Рассмотрим каждый из них подробнее ниже.

Соединение с БД


Есть два способа.

Способ первый. Если вам нужно просто создать соединение.
$mysqli = new mysqli('localhost', 'my_user', 'my_password', 'my_db');
if ($mysqli->connect_error) {
    die('Connect Error (' . $mysqli->connect_errno . ') ' . $mysqli->connect_error);
}


Способ второй. Если вам нужно использовать опции соединения.
$mysqli = mysqli_init();
if (!$mysqli) {
    die('mysqli_init failed');
}
 
if (!$mysqli->options(MYSQLI_INIT_COMMAND, 'SET AUTOCOMMIT = 0')) {
    die('Setting MYSQLI_INIT_COMMAND failed');
}
 
if (!$mysqli->real_connect('localhost', 'my_user', 'my_password', 'my_db')) {
    die('Connect Error (' . $mysqli->connect_errno . ') ' . $mysqli->connect_error);
}


С помощью $mysqli->connect_errno и $mysqli->connect_error мы получаем описание и код ошибки, возникших при соединении. И new mysqli() и $mysqli->real_connect() при ошибках соединений вызывают ошибку PHP Warning. Поэтому вывод ошибок с помощью выше упомянутых функций имеет смысл, если у вас отключено отображение ошибок PHP, например, на рабочем сервере, либо если вам нужно как-то обработать эти данные. Я упомнил здесь об этом, потому что не все функции MySQLi вызывают PHP Warning в случае ошибки, и для того что бы узнать, что произошла ошибка необходимо обязательно обращаться к специальным функциям, об этом ниже.

Полученный при соединении объект мы присвоили переменной $mysqli, для того чтобы использовать его в дальнейшем. Это очевидно для ООП стиля, но и для процедурного стиля этот объект также необходим, в этом отличие от устаревшего расширения MySQL, где ссылку на соединение необязательно было передавать при каждом использовании mysql функций.

Буферизированные и не буферизированные результаты


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

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

Преимущества:
  • Результат можно начинать читать раньше, сокращается время ожидания;
  • Результат не занимает место в оперативной памяти.

Недостатки:
  • Невозможно узнать, сколько строк получено;
  • Невозможно передвигаться к определенному результату, то есть можно читать данные только с начала и по порядку;
  • Нельзя выполнять других запросов, пока не закрыт этот результат.


Буферизированный результат лишен этих недостатков и соответственно лишен перечисленных преимуществ.

«Классические» запросы


В MySqli оставили возможность «классических» запросов: когда пользователю предлагается самостоятельно заниматься безопасностью передаваемых запросов так, как это было в устаревшем расширении MySQL. Для этого предлагается использовать функцию $mysqli->real_escape_string(), с помощью которой необходимо обрабатывать все данные перед помещением их в запрос.

Так же как и с соединением есть два способа сделать такой запрос короткий и длинный.

Короткий.
$result = $mysqli->query(‘текст запроса’, MYSQLI_USE_RESULT);

Возможные константы:
MYSQLI_STORE_RESULT – вернет буферизированный результат, значение по умолчанию
MYSQLI_USE_RESULT – небуферизированный

Длинный.
$mysqli->real_query('текст запроса');
echo($mysqli->field_count);  // вернет количество столбцов, в результате,
// можно получить до начала получения результата, что дает дополнительную гибкость
// по сравнению c коротким способом, разумеется, вызывать не обязательно
$result = $mysqli->use_result();  // вернет небуферизированный результат
// или
$result = $mysqli->store_result();  // вернет буферизированный результат


Функции $mysqli->use_result() или $mysqli->store_result() так же используются при мульти запросах (запросах состоящих из нескольких запросов). Мульти запросы в этой статье рассмотрены не будут.

И тот и другой синтаксисы вернут результат в виде объекта mysqli_result, который представляет собой удобный интерфейс для работы с результатом, как с буферизированным так и не с небуферизированным.

Как я писал выше, не все функции MySQLi выбрасывают ошибки PHP, описанные выше функции из их числа. В случае если запрос неверный и сервер вернул ошибку, PHP не даст об этом знать. Для проверки используйте функции:
  • $mysqli->error – описание ошибки
  • $mysqli->errno – код ошибки

$city = $mysqli->real_escape_string($city);
$mysqli->query(«SELECT * FROM `city` WHERE `id` = '$city);
if ($mysqli->errno) {
die('Select Error (' . $mysqli->errno . ') ' . $mysqli->error);
}


Преимущества «классического» синтаксиса запросов:
  1. Он значительно более компактен, чем подготовленные выражения (класс mysqli_stmt);
  2. Он позволяет получить небуферизированный результат в виде mysqli_result. Подготовленные выражения позволяют получить только буферизированный результат mysqli_result, а с небуферизированным придется работать средствами mysqli_stmt, который значительно менее гибкий, чем mysqli_result.


Практическое применение «классического» синтаксиса запросов я вижу:
  1. В не буферизированных запросах, если вам хочется использовать преимущества работы с mysqli_result;
  2. В запросах INSERT, UPDATE, REPLACE или DELETE, если для вас предпочтителен их более короткий синтаксис.

Для таких запросов будут полезны свойства:
  • $mysqli->affected_rows – количество затронутых строк предыдущим запросом не на выборку
  • $mysqli->insert_id – автоматически сгенерированный ID для последнего запроса вставки.

Подготовленные запросы


Преимущества подготовленных запросов над «классическими»:
  • При серии запросов, в которых меняются лишь данные, запрос передается на сервер только один раз, в дальнейшем посылаются только изменяющиеся данные;
  • Защита от SQL-инъекций.

За работу с подготовленными запросами в MySQLi отвечает класс mysqli_stmt.

Два способа создания подготовленного запроса.
// первый способ - используя объект mysqli
$mysqli->prepare(«SELECT * FROM `sk2_articles` WHERE `id` = ?»);
if ($mysqli->errno) {
die('Select Error (' . $mysqli->errno . ') ' . $mysqli->error);
}
 
// второй способ - используя объект mysqli_stmt
$stmt = $mysqli->stmt_init();
$stmt->prepare(«SELECT * FROM `sk2_articles` WHERE `id` = ?»);
if ($stmt->errno) {
die('Select Error (' . $stmt->errno . ') ' . $stmt->error);
}


Различия в том, для какого объекта вызываются функции получения информации об ошибке. Мне второй способ кажется удобнее, потому что проверки на ошибки можно объединить в один блок if c другими функциями mysqli_stmt. Как это сделать будет видно в примерах ниже.

$id_min = 81;
$id_max = 88;
$stmt = $mysqli->stmt_init();
if(
// подготовливаем запрос, там куда будут вствлятся данные отмечаем символом ? (плейсхолдоры)
($stmt->prepare(«SELECT title FROM sk2_articles WHERE id > ? and id < ?») ===FALSE)
// привязываем переменные к плейсхолдорам
or ($stmt->bind_param('ii', $id_min, $id_max) === FALSE)
// отрправляем даные, которые на данный момент находятся в привязанных переменных
or ($stmt->execute() === FALSE)
// привязывем переменую для получения в нее результата
or ($stmt->bind_result($title) === FALSE)
// делаем запрос буферизированным, 
// если бы этой строки не было, запрос был бы небуферезированым
or ($stmt->store_result() === FALSE)
// получение результата в привязанную переменную
or ($stmt->fetch() === FALSE)
// закрываем подготовленный запрос
or ($stmt->close() === FALSE)
) {
die('Select Error (' . $stmt->errno . ') ' . $stmt->error);
}
echo $title;


Несколько пояснений к выше написанному коду.
  1. В $stmt->bind_param() первым параметром указываем тип привязываемых данных (i – integer, d — double, s – строка). Вторым и последующим указываются переменные, которые будет привязаны к соответствующим плейсхолдорам, объявленным в $stmt->prepare() символами "?" в том порядке, в каком они указаны в $stmt->prepare(). Это заметное отличие от PDO, где плесхолдоры могут быть именованными, а значит не зависят от порядка объявления.
  2. $stmt->bind_result по-моему самое слабое место класса mysqli_stmt, так как оно обязывает знать, какие именно данные будут получены из запроса и каком порядке они объявлены в запросе. Для случая “SELECT * FROM …” он вряд ли применим. Гораздо более гибкий подход в работе с результатом предлагает класс mysqli_result. Чтобы быть окончательно объективным, стоит упомянуть, что можно воспользоваться функцией $stmt->result_metadata() получить mysqli_result содержащий только о метаданные без самих данных, и посмотреть что за столбцы получены, но этот подход мне кажется излишне сложным, и о нем я упомянул только, чтобы избежать обвинений в неточной информации.
  3. $stmt->store_result(), как я написал в комментарии к строке, необязательный и нужен для того, чтобы можно было использовать такие функции как:
    • $mysqli_stmt->num_rows – количество полученных строк к результате,
    • $stmt->data_seek – перемещение внутреннего указателя на заданную строку результата,
    • $stmt->free_result() – и не забудьте вызвать освобождение памяти если вы использовали $stmt->store_result().

  4. $stmt->fetch() возвращает TRUE/FALSE если данные были получены удачно/неудачно, и NULL если данных больше нет.
  5. Не все функции, объединенные в блок if, в случае ошибки помещают ее в $stmt->error, некоторые вызывают PHP Warning, но все из них в случае неудачи возвращают FALSE, поэтому мне кажется эта конструкция удачной. Если вам нужно выполнить несколько раз $stmt->execute() или $stmt->fetch(), то так сделать не получится.
  6. Не зависимо от того, был ли запрос буферизированный или нет, доступны следующие свойства:
    • $stmt->field_count — количество столбцов в результате,
    • $stmt->affected_rows – количество затронутых строк предыдущим запросом не на выборку,
    • $stmt->insert_id – автоматически сгенерированный id предыдущей вставки.


Изменим код так, чтобы получить результат в виде экземпляра объекта mysqli_result.
$id_min = 81;
$id_max = 88;
$stmt = $mysqli->stmt_init();
if(
($stmt->prepare(«SELECT title FROM sx2_links WHERE id > ? and id < ?») === FALSE)
or ($stmt->bind_param('ii', $id_min, $id_max) === FALSE)
or ($stmt->execute() === FALSE)
// получение буферизированного результата в виде mysqli_result,
// небуферизированный результат получить нельзя, о чем я писал в недостатках
or (($result = $stmt->get_result()) === FALSE)
or ($stmt->close() === FALSE)
) {
die('Select Error (' . $stmt->errno . ') ' . $stmt->error);
}
$row = $result->fetch_row();
echo $row[0];


Класс mysqli_result и работа с результатом с помощью него


Как было показано выше, объект mysqli_result вы могли получить как с помощью «классического» запроса с помощью класса mysqli, тогда он может быть как буферизированный так и небуферизированный, так и с помощью класса mysqli_stmt, тогда он буферизированный. От того какой результат вы получили, зависит работа функций этого класса, поэтому нужно хорошо понимать, что если ваш запрос небуферизированный вы не располагаете всем результатом и соответственно не можете знать сколько строк в результате, и читать его можно только по-порядку строка за строкой.

Набор функций в этом классе покажется знакомым по-старому расширения:
  • $result->fetch_row() – получает текущий ряд результата в виде нумерованного массива,
  • $result->fetch_assoc() – в виде ассоциативного массива,
  • $result->fetch_array() – тип массива задается константой,
    1. MYSQLI_ASSOC – ассоциативный,
    2. MYSQLI_NUM – нумерованный,
    3. MYSQLI_BOTH – оба,

  • $result->fetch_object() – строка результата в виде объекта.

Про $result->fetch_object() хотелось бы поговорить отдельно. У этой функции есть два параметра, оба необязательные:
  • class_name – имя класса, на основе которого будет создан объект,
  • params – массив параметров, которые будут переданы конструктору при создании объекта.

Как видите, передать конструктору класса предположим ассоциативный массив одной строки результата с помощью этой функции не получится. Она сама за вас присвоит свойствам класса, совпадающие с названиями полей результаты. Если свойства класса не будет найдено, оно создается динамически, с областью видимости public.
class Book
{
private $some1;
public $some2;
protected $id;
 
function __construct($param1, $param2) {
$this->some1 = $param1;
$this->some2 = $param2;
}
}
$book = $result->fetch_object('Book', array(1, 2));
var_dump( $book);

Выведет, если в результате было только одно поле id
object(Book)[4]
  private 'some1'  => int 1
  public 'some2'  => int 2
  protected 'id'  => int 382

Другие полезные функции и свойства рассматриваемого класса:
  • $result->fetch_all() — получение всех рядов в виде массива нумерованного / ассоциативного / обоих типов ,
  • $result->data_seek() — установка внутреннего указателя на заданный ряд результата,
  • $result->num_rows – количество строк в результате.

Ссылки:
Страница MySQLi в официальной документации на php.net
Больше примеров по MySQLi
Список функций MySQLi на русском языке
Андрей Чаленко @ChAk
карма
8,0
рейтинг 0,0
Самое читаемое Разработка

Комментарии (63)

  • +2
    Прощу прощения. Вам времени не жалко своего?
    • +2
      В чем проблема-то?
      • –10
        Просто я считаю работу с БД таким образом — просто потерей времени. Есть достаточное количество ORM на PHP, которые действительно способны сократить время разработки.
        • +5
          Бесспорно. Но значит ли это что можно не уметь работать с БД без них. Я вот тоже пользовался возможностями фрейморков и уже забыл как работать с БД напрямую, а тут понадобилось переносить базу данных, где некоторые таблицы сотни мегабайт. Вы можете сказать про сервера с десятками гигабайт оперативки, но в моем случае не было таких серверов, а был средний впс, а делать нужно.
          • –4
            Придет время, когда наравне с обычными таблицами будут здоровенные view'хи. Тогда sql вспоминается на раз и узнается о нем много нового. А составлять запросы, когда через ORM это будет быстрее, секурнее и ООП-шнее — не стоит.
            • 0
              >> А составлять запросы, когда через ORM это будет быстрее, секурнее и ООП-шнее — не стоит.
              Птому что это не круто? ;-)
        • 0
          Угу, а потом DBA за голову хватаются, когда видят, что ORM нагенерил.

          ORM не отменяет необходимости писать некоторые запросы вручную.
          • 0
            Много вы DBA знаете в веб-разработке?
            Много вы DBA знаете, которые не умеют работать с ORM?
            Много ли DBA вы знаете?
            • 0
              1. да
              2. им пофигу на ОRМ, вообще. DBA сказал, как написать запрос — разработчик пишет, если ОRМ не позволяет, значит, выбросить ОRМ.
              3. да
            • +1
              По-вашему, веб-разработка это только сайтики что ли? Мы занимаемся облачным докуменооборотом, там основной клиент — браузер. Хитов дикое количество, без оптимизации запросов не обойтись. И да, у нас есть DBA и не один.
    • +2
      Если статья окажется кому-то полезной, то жалеть будет не о чем. Статей по mysqli не русском совсем не много, внес свой посильный вклад.
      • +8
        По статье видно что труд приложили, но…
        а) В начале была затравка про сравнение с PDO, но его так и невидно
        б) Сама статья по сути рерайт мануала по php, ни сравнений (даже с mysql), ни особенностей, ни нюансов, ни специфических функций (тех же асинхронных запросов).
        в) Код сам по себе тоже любопытен. Получение результата селекта начиная с выполнения запроса — 16 строк кода с комментами и в случае ошибки die, как-то это не очень в плане демонстрации «как надо делать».
        • 0
          а) Извиняюсь, если кого ввел в заблуждение, не хотел, в будущем буду осторожнее
          б) Не соглашусь. Я понимаю, что могло сложиться такое впечатление из за справочного стиля изложения. В мануал я заглядывал только для того что бы посмотреть какие примеры там используются. Текст писал исключительно из головы. Нюансы особенности и специфичные функции которые мне показались интересными я осветил, если это не заметно, видимо не получилось.
          В) Примеры слабые не спорю. Основной упор делал на текст.
      • +7
        Ещё внесу свои 5 рублей в казну — код ужасен. Если вы начинающий — ещё не страшно (пока), но пора смотреть в сторону идеализации кода до удобочитаемого уровня. Тем более, что это примеры. Ваши читатели будут использовать конструкцию с шестью(!!!) «or», а потом их уволят нахрен.
        • 0
          Было бы не плохо, если бы кто нибудь написал алтернативный более правильный красивый код этого фрагмента с 6 OR.
          • 0
            Цепочка обязанностей
            Такое делается именно через этот паттерн.
          • 0
            Очень примерно:

            1. class Result
            2. {
            3.  private $stmt = null;
            4.  private $result = null;
            5.  public function __construct($min, $max)
            6.  {
            7.      $stmt = $mysqli->stmt_init();
            8.      try {
            9.          $this->prepare();
            10.          $this->bind($min, $max);
            11.          $this->execute();
            12.          $this->attachResult();
            13.          $this->store();
            14.          $this->fetch();
            15.          $this->close();
            16.     } catch (Exception $e) {
            17.      Debug.log('Select Error (' . $stmt->errno . ') ' . $stmt->error);
            18.     }
            19.  }
            20.  
            21.  // подготовливаем запрос, там куда будут вствлятся данные отмечаем символом ? (плейсхолдоры)
            22.  private function prepare()
            23.  {
            24.     $this->stmt->prepare("SELECT title FROM sk2_articles WHERE id > ? and id < ?")
            25.  }
            26.  
            27.  // привязываем переменные к плейсхолдорам
            28.  private function bind($min, $max)
            29.  {
            30.     $this->stmt->bind_param('ii', $min, $max);
            31.  }
            32.  
            33.  // отрправляем даные, которые на данный момент находятся в привязанных переменных
            34.  private function execute()
            35.  {
            36.     $this->stmt->execute();
            37.  } 
            38.  
            39.  // привязывем переменую для получения в нее результата
            40.  private function attachResult()
            41.  {
            42.     $this->stmt->bind_result($this->result);
            43.  }
            44.  
            45.  // делаем запрос буферизированным,
            46.  // если бы этой строки не было, запрос был бы небуферезированым 
            47.  private function store()
            48.  {
            49.     $this->stmt->store_result();
            50.  }
            51.  
            52.  // получение результата в привязанную переменную
            53.  private function fetch()
            54.  {
            55.     $this->stmt->fetch();
            56.  }
            57.  
            58.  private function close()
            59.  {
            60.     $stmt->close();
            61.  }
            62.  
            63.  public function getResult()
            64.  {
            65.     return $this->result;
            66.  }
            67. }
            68.  
            69. $id_min = 81;
            70. $id_max = 88;
            71. $result = new Result($id_min, $id_max);
            72. print $result->getResult();
            * This source code was highlighted with Source Code Highlighter.
            • +1
              Спасибо за пример.
              Во время написания статьи думаешь не сколько о применимости кода, сколько об его наглядности и краткости. Ни кто не хочет разбираться в большом коде. К тому же мне кажется любому очевидно, что в статьях подобно этой выкладывают не готовые решения, которые можно использовать в продакшин.
            • +1
              Ну и смысл тянуть эту портянку в демо-статью — показать, что GoF прочитал? Замечания на будущее: комментарии — на английском, в формате phpdoc (а не коменнтариями, как сейчас). И self-described члены класса обычно не требуют description.
              • 0
                скорее Фаулера по рефакторингу я прочитал
  • +2
    Ожидал увидеть асинхронные запросы.
    • 0
      Вы наверное о небуферизированный запросах. Я старался раскрыть тему, что бы было понятно как их делать. С примерами, к сожалению действительно как то плохо. Боялся, что будет слишком объемно, видимо зря.
      • 0
        Нет, именно о асинхронных.
        • 0
          Можно пример?
          • +1
            Пример из мануала:
            ru2.php.net/mysqli.poll#refsect1-mysqli.poll-examples

            Асинхронные запросы доступны если расширение скомпилировано с использованием mysqlnd (используется по умолчанию начиная с PHP 5.4).
  • +5
    Так в чем преимущество в сравнении с PDO?
    • 0
      Про недостаток я написал. Про преимущество ни чего не скажу, так как не работал с PDO.
      • 0
        В век серверов по 50 евро с 24 Гб памяти эти недостатки как-то теряются на фоне.
        • +3
          Видимо вы не о том недостатке. Я о том, что в MySQLi плейсхолдоры не могут быть именованными, и соответственно мы зависим от порядка их объявления.
          • 0
            Тогда вообще: возьмите на вооружение Doctrine ORM или Propel ORM. Про низкоуровневые слои типа PDO или mysqli — забудьте. Вспоминайте про них только тогда, когда это будет ну очень нужно. Разницы нет, честно.
            • 0
              Ждал этого коммента) Написали бы это с самого начала… +1
          • 0
            Как только я увидел что у mysqli неименованные плейсхолдеры, это полностью исключило этот бакенд в качестве использования… PDO, только PDO, к тому же позволит максимально легко сменить базу данных в будущем.
        • 0
          Значит, закон 94-ФЗ вас не касается. Повезло.
    • +2
  • –4
    real_connect()
    real_query()
    real_escape_string()

    Я понимаю, разработчики MySQL долбанутые, у них самые часто используемые функции начинаются с «real_».

    Я понимаю, разработчики расширения mysql тупо один-в-один скопировали API MySQL со всеми идиотскими префиксами.

    Но вот на кой ИКС разработчики MySQLi вытворили то же самое? У разработчиков PHP какая-то болезненная любовь к «безопасным» префиксам?

    А если бы разработчики MySQL назвали функции mysql_secure_escape_string_with_support_for_character_encoding, разработчики PHP его точно так же скопировали бы? Так ведь это время не за горами, для MySQL придумают новую версию, старые оставят для обратной совместимости и пометят устаревшими.
    • –1
      Если судить по минусам, то всем очень удобно перед каждой функцией писать «real_».

      Неожиданно.
      • 0
        Не всем. Мне удобнее «unreal_».
  • 0
    Последние годы я писал сайты исключительно на фреймворках, что избавляло меня от работы с БД напрямую. Некоторое время назад начал работу над сайтом на чистом php

    Зачем понадобился чистый PHP, к слову?
    • –3
      а это нормальный период. Многие через него проходят и не редко есть идея: «Создать свой фреймворк». Ну или подобное что-то.
    • 0
      Жалко денег на дорогой сервер. Сайт приносит мало денег, а посетителей много, хотелось что бы ни чего лишнего небыло, тогда можно обходиться средним vpsом
      • 0
        средним vps'ом можно обходиться если правильно продумать кэширование ;)
        • 0
          Вот хочет заказчик видеть реальное число просмотров поста в блоге, чтоб на каждое F5 цифра была новая, и запрос к БД уже надо делать. А главное PHP вызывать нужно.
          • 0
            (a) чтоб на каждое F5 цифра была новая, и (b) запрос к БД уже надо делать

            Из а не следует b. Что мешает обновить счетчик в кеше при регистрации просмотра?
            • 0
              Смотря о каком кеше речь идёт. Одно дело о кеше страницы для анонимусов, другое даже для тысячи зареганных пользователей, у каждого из которых своя страница, большая часть из которых могут эту страницу и не открыть. Третье — кеш запроса к БД.
              • 0
                О кеше количества просмотров поста в блоге. В данном случае не вижу разницы между анонимусами/зареганными пользователями — один инкремент счетчика в мемкеше/редисе решает вопросы с рил-тайм апдейтами для любого типа пользователей.
                • 0
                  Это будет кеш запроса к БД, а не кеш страницы. И есть подозрение, что большого выигрыша кеширование в мемкеше запроса вроде SELECT * FROM posts WHERE id = ? не даст, поскольку СУБД тоже кешировать умеет.
                  • 0
                    MySQL из рук вон плохо кеширует — при любом изменении таблицы сбрасываются кеши всех запросов, в которых эта таблица участвовала, даже если изменилась запись, которая в резалт-сетах не встречалась. Так что память лучше потратить на buffer pool (key cache для MyISAM) и кешировать данные вне базы (разумеется более интеллектуально).
                    • 0
                      Ладно вам уже, у любого решения есть критерий разумной применимости. Если заказчик хочет realtime счетчик и готов за это платить железом — флаг ему в руки =) Если не готов, но может раскошелиться на бОльший срок разработки — аналогично. Флаг в руки, только другой расцветки. Можно хоть аккумулировать эти счетчики в redis и скидывать в БД по времени или кол-ву хитов.

                      А если заказчик хочет и рыбку съесть и удовольствие получить — то нужен ли вам такой заказчик? ;)
                      • 0
                        Ну вся эта ветка началась с предположения, что железо сильно ограниченно, от того и пляшем.
  • 0
    Есть ли в MySQLi аналог mysql_pconnect()?
    • 0
      А вот про это планировал написать, но забыл. Для создания постоянного подключения необходимо перед именем хоста при создании подключения добавить «p:». Изначально этой возможности в mysqli не было, но с версии PHP 5.3 ее вернули.
      • 0
        Отлично. Я не знал об этом синтаксисе и «отсутсвие» pconnect в MySQLi было для меня сдерживающим переход на MySQLi фактором
        • НЛО прилетело и опубликовало эту надпись здесь
          • +1
            зато медленнее.
            • +1
              Там вон выше ОРМ обсуждают, а вы тут про скорость плейсхолдеров :)
            • 0
              Зато не только mysql
  • +2
    От статьи ожидал чего-то большего.
    • 0
      денег например :(
  • +1
    И чем это отличается от официальной документации? То же самое другими словами. Никаких собственных мыслей не привнесено.
  • 0
    Кстати забавный аргумент про возможность «легко» перескочить на другую базу, вот сколько не было проектов ну не разу не пригодилось.

    И еще один забавный момент — бывают проекты где вся логика зашита в базе, и данные получаются посредством процедур, там вообще пофиг что использовать, хоть ORM, хоть PDO, хоть mysqli, правда, если не изменяет память кто то из последних двух не умеет принимать множественный результат.
  • 0
    PDO выйграл.

    /**
    * @var PDO
    */
    $db;

    $statement = $db->prepare($sql);

    $statement = $db->execute(array($param));

    $result = $statement->fetchAll();

    // Ещё короче.
    $statement = $db->query($sql, array($param));

    $result = $statement->fetchAll();
    • 0
      Неправда, выйграл войн.
      • –1
        Вам сюда, глазам станет немного легче.

Только зарегистрированные пользователи могут оставлять комментарии. Войдите, пожалуйста.