Любой способ выбрать, не вызывая блокировки в MySQL?

Запрос:

SELECT COUNT(online.account_id) cnt from online;

Но онлайн-таблица также модифицируется событием, поэтому часто я вижу блокировку, выполняя show processlist,

Есть ли в MySQL грамматика, которая может делать оператор select, не вызывая блокировки?

И я забыл упомянуть выше, что он находится на ведомой базе данных MySQL.

После того, как я добавил в my.cnf:transaction-isolation = READ-UNCOMMITTEDраб встретит с ошибкой

Ошибка 'Двоичная регистрация невозможна. Сообщение: Уровень транзакции "READ-UNCOMMITTED" в InnoDB небезопасен для режима "STATEMENT" binlog по запросу

Итак, есть ли совместимый способ сделать это?

10 ответов

Нашел статью под названием "MYSQL WITH NOLOCK"

https://web.archive.org/web/20100814144042/http://sqldba.org/articles/22-mysql-with-nolock.aspx

в MS SQL Server вы должны сделать следующее:

SELECT * FROM TABLE_NAME WITH (nolock)

и MYSQL эквивалент

SET SESSION TRANSACTION ISOLATION LEVEL READ UNCOMMITTED ;
SELECT * FROM TABLE_NAME ;
SET SESSION TRANSACTION ISOLATION LEVEL REPEATABLE READ ;

РЕДАКТИРОВАТЬ

Michael Mior предложил следующее (из комментариев)

SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED ;
SELECT * FROM TABLE_NAME ;
COMMIT ;

Если таблица InnoDB, см. http://dev.mysql.com/doc/refman/5.1/en/innodb-consistent-read.html - она ​​использует согласие на чтение (режим без блокировки) для SELECT, "которые делают не указывайте FOR UPDATE или LOCK IN SHARE MODE, если установлена ​​опция innodb_locks_unsafe_for_binlog, а уровень изоляции транзакции не установлен в SERIALIZABLE. Таким образом, для строк, считанных из выбранной таблицы, не установлены блокировки ".

Использование

SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED.

Документы версии 5.0 здесь.

Документы по версии 5.1 здесь.

Вы можете прочитать эту страницу руководства MySQL. То, как таблица заблокирована, зависит от типа таблицы.

MyISAM использует блокировки таблиц для достижения очень высокой скорости чтения, но если у вас есть ожидание оператора UPDATE, то будущие SELECTS будут стоять в очереди за UPDATE.

Таблицы InnoDB используют блокировку на уровне строк, и у вас не будет блокировки всей таблицы за UPDATE. Есть и другие проблемы с блокировками, связанные с InnoDB, но вы можете обнаружить, что они соответствуют вашим потребностям.

Другой способ включить грязное чтение в MySQL - добавить подсказку: LOCK IN SHARE MODE

SELECT * FROM TABLE_NAME LOCK IN SHARE MODE; 

В зависимости от типа таблицы блокировка будет выполняться по-разному, но также будет учитываться значение SELECT. Для таблиц MyISAM простая таблица SELECT count(*) FROM не должна блокировать таблицу, поскольку она обращается к метаданным для получения количества записей. Innodb займёт больше времени, так как он должен захватить таблицу в моментальном снимке для подсчета записей, но это не должно вызывать блокировку.

Вы должны по крайней мере установить concurrent_insert в 1 (по умолчанию). Затем, если в файле данных нет "пробелов" для заполнения таблицы, вставки будут добавлены к файлу, и SELECT и INSERT могут происходить одновременно с таблицами MyISAM. Обратите внимание, что удаление записи создает "пробел" в файле данных, который будет пытаться заполниться будущими вставками и обновлениями.

Если вы редко удаляете записи, вы можете установить concurrent_insert равным 2, и вставки всегда будут добавляться в конец файла данных. Тогда выбор и вставка могут происходить одновременно, но ваш файл данных никогда не станет меньше, независимо от того, сколько записей вы удаляете (кроме всех записей).

Суть в том, что если у вас много обновлений, вставок и выборок в таблицу, вы должны сделать это InnoDB. Вы можете свободно смешивать типы таблиц в системе.

Основываясь на синтаксисе MySQL NOLOCK в этом документе , вы можете установить уровень изоляции для всех подключений глобально наuncommitedчто значит сnolockдля всех операторов select:

       SET GLOBAL TRANSACTION ISOLATION LEVEL READ UNCOMMITTED ;

Это можно получить, запустив:

      SELECT @@global.transaction_isolation;

Из этой ссылки:

Если вы получаете блокировку таблицы явно с помощью LOCK TABLES, вы можете запросить блокировку READ LOCAL вместо блокировки READ, чтобы другие сеансы могли выполнять одновременные вставки, пока у вас заблокирована таблица.

Обычно SELECT не выполняет блокировку, которая вам нужна для таблиц InnoDB. Уровень изоляции транзакции по умолчанию означает, что выбор не блокирует вещи.

Конечно, раздоры все еще случаются.

Вот альтернативное программное решение, которое может работать для других, использующих MyISAM. Если (важно), вам все равно, произошло ли обновление в середине запросов. Как мы знаем, MyISAM может вызывать блокировки на уровне таблицы, особенно если у вас есть ожидающее обновление, которое будет заблокировано, а затем другие запросы выбора, стоящие за этим обновлением, также будут заблокированы.

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

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

Пример написан на Perl с небольшим количеством псевдокода и проходит от высокого к низкому.


# object_id must be an index so it's fast
# First get the range of object_id, as it may not start from 0 to reduce empty queries later on.

my ( $first_id, $last_id ) = $db->db_query_array(
  sql => q{ SELECT MIN(object_id), MAX(object_id) FROM mytable }
);

my $keep_running = 1;
my $step_size    = 1000;
my $next_id      = $last_id;

while( $keep_running ) {

    my $sql = q{ 
SELECT object_id, created, status FROM 
    ( SELECT object_id, created, status FROM mytable AS is1 WHERE is1.object_id <= ? ORDER BY is1.object_id DESC LIMIT ? ) AS is2
WHERE status='live' ORDER BY object_id DESC 
};  

    my $sth = $db->db_query( sql => $sql, args => [ $step_size, $next_id ] );

    while( my ($object_id, $created, $status ) = $sth->fetchrow_array() ) {

        $last_id = $object_id;
        
        ## do your stuff

    }

    if( !$last_id ) {
        $next_id -= $step_size; # There weren't any matched in the range we grabbed
    } else {
        $next_id = $last_id - 1; # There were some, so we'll start from that.
    }

    $keep_running = 0 if $next_id < 1 || $next_id < $first_id;
    
}



Другие вопросы по тегам