2010-05-26 4 views
0

Мы очень расстраиваемся в создании взаимоблокировок в MySQL. Это происходит не из-за превышения таймаута блокировки, поскольку взаимоблокировки происходят мгновенно, когда они происходят. Вот SQL-код, который выполняется на 2 отдельных нитей (с 2-мя отдельными соединениями из пула соединений), что создает тупиковую ситуацию:Получение взаимоблокировок в MySQL

UPDATE Sequences SET Counter = LAST_INSERT_ID(Counter + 1) WHERE Sequence IS NULL 

последовательностях таблица имеет 2 колонки: Последовательность и счетчик

LAST_INSERT_ID позволяет нам до retrieve this updated counter value as per MySQL's recommendation. Это работает идеально для нас, но мы получаем эти тупики! Почему мы получаем их и как мы можем их избежать?

Большое спасибо за любую помощь в этом.

EDIT: все это в транзакции (требуется, поскольку я использую Hibernate), и AUTO_INCREMENT здесь не имеет смысла. Я должен был быть более ясным. Таблица Sequences содержит много последовательностей (в нашем случае около 100 миллионов из них). Мне нужно увеличить счетчик и получить это значение. AUTO_INCREMENT не играет никакой роли во всем этом, это не имеет ничего общего с идентификаторами или PRIMARY KEYs.

+0

Не могли бы вы вывести вывод 'SHOW CREATE TABLE Sequences'? – Quassnoi

+0

CREATE TABLE 'sequences' ( ' Id' BigInt (20) NOT NULL AUTO_INCREMENT, 'Sequence' VARBINARY (1005) По умолчанию NULL, ' Counter' INT (11) По умолчанию '0', PRIMARY KEY ('Id'), UNIQUE KEY 'Sequences_Constraint' (' Sequence' (104)) ) ENGINE = InnoDB AUTO_INCREMENT = 134 DEFAULT CHARSET = utf8 –

ответ

2

Оберните свои транзакции sql в транзакции. Если вы не используете транзакцию, вы получите условие гонки на LAST_INSERT_ID.

Но на самом деле, у вас должны быть поля счетчика auto_increment, поэтому вы разрешите mysql обрабатывать это.

Ваше третье решение заключается в использовании LOCK_TABLES, чтобы заблокировать таблицу последовательностей, чтобы никто другой процесс не мог получить к ней доступ одновременно. Это, вероятно, самое медленное решение, если вы не используете INNODB.

+0

Это транзакция. Я отредактировал мой вопрос выше, чтобы объяснить, что AUTO_INCREMENT здесь не уместен, извините за то, что я не понимаю этого. Столбец таблицы Sequences, вероятно, будет серьезной проблемой для нас. –

+1

Сделки вызывают * более * взаимоблокировки ... они не являются решением проблемы взаимоблокировки. – zombat

1

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

Прежде всего, вы должны прочитать this manual page, чтобы лучше понять, как вы можете их избежать.

Во-вторых, если все, что вы делаете, это обновление счетчика, вы действительно должны действительно использовать столбец AUTO_INCREMENT для Counter, а не полагаться на процесс «выбрать, затем обновить», который, как вы видели, является состояние гонки, которое может создавать взаимоблокировки. По сути, свойство AUTO_INCREMENT вашего столбца таблицы будет действовать как счетчик для вас.

Наконец, я собираюсь предположить, что у вас есть этот оператор обновления внутри транзакции, поскольку это приведет к частым взаимоблокировкам. Если вы хотите увидеть его в действии, попробуйте experiment listed here. Это именно то, что происходит с вашим кодом ... два потока пытаются обновить одни и те же записи одновременно, прежде чем один из них будет зафиксирован. Мгновенный тупик.

Ваше лучшее решение - выяснить, как это сделать без транзакции, и AUTO_INCREMENT позволит вам это сделать.

+0

Интересный эксперимент, связанный с, спасибо. Но я не уверен, что это происходит здесь. Я обновляю только 1 строку. Я прочитаю страницу руководства. Это, конечно, в транзакции. Я отредактировал мой вопрос выше, чтобы объяснить, что AUTO_INCREMENT здесь не уместен, извините за то, что я не понимаю этого. –

+0

@at ​​- вы обновляете только одну строку, но это одна и та же строка в обеих транзакциях, отсюда и тупик. Если одновременно будут совершены две транзакции, оба попытаются обновить одну и ту же строку, так как 'LAST_INSERT_ID()' будет одинаковым для каждого, прежде чем можно будет зафиксировать. – zombat

+1

Почему бы не обновлять операторы, которые обновляют одну и ту же строку, просто выполняются одна за другой? Почему тупик? –

0

Ни один другой SQL не участвует? Мне кажется маловероятным.

«где последовательность равно null», вероятно, вызывает полное сканирование таблицы, в результате чего блокировки считывания должны быть получены на каждой строке/странице/....

Это становится проблемой, если (ваш конкретный движок не использует MVCC и), был INSERT, который предшествовал вашему обновлению в рамках одной и той же транзакции. Этот INSERT приобрел бы исключительную блокировку для некоторого ресурса (строка/страница/...), что приведет к тому, что ожидание блокировки чтения любым другим потоком будет ждать. Таким образом, два соединения могут сначала сделать свою вставку, в результате чего каждая из них будет иметь исключительную блокировку на некоторой небольшой части таблицы, а затем они оба попытаются выполнить ваше обновление, требуя, чтобы каждый из них мог получить блокировку чтения на весь стол.

+0

Не было другого SQL, связанного с тупиками, которые имели какое-либо отношение к этой таблице. Тупики все происходят в вышеупомянутом выражении UPDATE. Интересно, что вы скажете о проблеме NULL. Существует также инструкция UPDATE, где имя последовательности имеет значение, но тупиков там не бывает. Я понял из-за природы нашего приложения, нам нужна нулевая последовательность только намного чаще. Если NULL вызывает полное сканирование таблицы, я мог бы легко изменить это, чтобы найти что-то еще, как пустую строку. У меня есть вставки в той же транзакции, которая вызывает блокировки? –

+0

«Другого SQL не было» и «У меня есть вставки в одной транзакции» ??? Давай. Между транзакциями происходят взаимоблокировки, а не на уровне выписок. И почему вы написали, что «ГДЕ ... НУЛЛ» в первую очередь? Не хотите ли ограничивать свое обновление только той строкой, которая была только что вставлена ​​этой же транзакцией? –

+0

Вы спросили меня, не был ли задействован какой-либо другой SQL, а затем предложил, что проблема может быть выражением insert в той же транзакции. Вот почему я ответил этими фразами. «WHERE Sequence IS NULL» фактически ограничивает мое обновление только строкой, где Sequence имеет значение NULL. Я немного смущен вашим последним вопросом. Вставка происходит только потому, что имя последовательности еще не было в таблице Sequences. –

Смежные вопросы