Неблокирующие выборки в MySQL
Материал из Eludia.
Как известно (или, во всяком случае, всегда следует иметь в виду), за скорость элементарных операций, которой славится классический MySQL, приходится платить ощутимыми неудобствами, проявляющимися в многопользовательском режиме.
Самый яркий тому пример: на время исполнения любого SELECT все входящие в него MyISAM-таблицы целиком блокируются на запись. В частности, любой запрос, связанный с полным проходом по таблице log, подвешивает на время своего исполнения все действия активных пользователей. При использовании InnoDB и прочих storage backend'ов с блокировкой на уровне записей данная проблема стоит не столь остро, однако простор для оптимизации всё же остаётся.
А именно, при работе с большими (миллионными) таблицами всегда стоит иметь в виду местную MySQL'скую альтернативу SELECT: HANDLER. Эта инструкция позволяет заглядывать в таблицу напрямую, без каких-либо блокировок и буферизации. При этом нельзя использовать JOIN и естественным образом возникает риск получить неконсистентные данные, однако в ряде ситуаций эти ограничения вполне окупаются.
Типичный пример тому: проход всё той же таблицы log для восстановления данных, связанных с некоторыми событиями. Пример из жизни: вы добавили к таблице deps поле id_log_create и хотите, чтобы для всех ранее введённых строк deps этот указатель был установлен на запись log, соответствующую созданию записи. Задача решается скриптом
sql_do ('HANDLER log OPEN');
while (1) {
my $log = sql_select_hash ("handler log read NEXT WHERE type='deps' AND action='create'");
$log -> {id} or last;
sql_do ('UPDATE deps SET id_log_create = ? WHERE id = ?', $log -> {id}, $log -> {id_object});
}
sql_do ('HANDLER log CLOSE');
который можно спокойно запускать в разгар рабочего дня: он никому не помешает.
