InnoDB тупик с режимами блокировки S и X - PullRequest
0 голосов
/ 27 октября 2011

В моем приложении время от времени возникают два запроса (из разных процессов), которые вызывают тупик.

Запрос № 1

UPDATE tblA, tblB SET tblA.varcharfield=tblB.varcharfield WHERE tblA.varcharfield IS NULL AND [a few other conditions];

Запрос № 2

INSERT INTO tmp_tbl SELECT * FROM tblA WHERE [various conditions];

Оба эти запроса занимают значительное время, поскольку в этих таблицах миллионы строк. Когда выполняется запрос № 2, кажется, что tblA заблокирован в режиме S. Похоже, что запрос № 1 требует блокировки X. Поскольку это несовместимо с блокировкой S, запрос № 1 ожидает до 30 секунд, после чего я захожу в тупик:

Ошибка сериализации: 1213 Обнаружен тупик при попытке получить блокировку; попробуйте перезапустить транзакцию

Исходя из того, что я прочитал в документации , я думаю, у меня есть пара вариантов:

  1. Установить индекс для tblA.varcharfield. К сожалению, я думаю, что для этого потребуется очень большой индекс для хранения поля varchar (512). (см. Правку ниже ... это не сработало.)
  2. Отключить блокировку с помощью SET SESSION TRANSACTION ISOLATION LEVEL READ UNCOMMITTED; . Я не понимаю последствий этого и беспокоюсь о поврежденных данных. В настоящее время я не использую явные транзакции в своем приложении, но, возможно, когда-нибудь в будущем.
  3. Разделите мои трудоемкие запросы на маленькие кусочки, чтобы они могли стоять в очереди и запускаться в MySQL без истечения 30-секундного времени ожидания. Это не решило бы суть проблемы, и я обеспокоен тем, что, когда мои серверы баз данных будут заняты, проблема возникнет снова.
  4. Просто повторять запросы снова и снова ... не вариант, на который я надеюсь.

Как мне поступить? Есть ли альтернативные методы, которые я должен рассмотреть?


РЕДАКТИРОВАТЬ: Я попытался установить индекс для varcharfield, но таблица по-прежнему блокируется. Я подозреваю, что блокировка происходит, когда часть UPDATE фактически выполняется. Есть другие предложения, чтобы обойти эту проблему?

Ответы [ 3 ]

1 голос
/ 27 октября 2011

Вы можете индексировать только часть столбца varchar, он все равно будет работать и будет занимать меньше места.Просто укажите размер индекса:

CREATE INDEX someindex ON sometable (varcharcolumn(32))
1 голос
/ 27 октября 2011

A. Если мы предполагаем, что индексирование varcharField занимает много места на диске, и добавление нового столбца не сильно ударит вас, я могу предложить следующий подход:

  1. создать новое поле с типом данных tinyint
  2. индексировать его.
  3. это поле будет хранить 0, если varcharField равно нулю, и 1 - в противном случае.
  4. переписать первый запрос, чтобы выполнить обновление, опираясь на новое поле. В этом случае это не приведет к блокировке всей таблицы.

Надеюсь, это поможет.

0 голосов
/ 05 ноября 2011

Мне удалось решить проблему, добавив явные операторы LOCK TABLE вокруг обоих запросов. Это оказалось лучшим решением, так как каждый запрос затрагивает очень много записей, и что оба они являются фоновыми процессами. Теперь они ждут друг друга.

http://dev.mysql.com/doc/refman/5.0/en/lock-tables.html

Хотя для меня это нормальное решение, оно, очевидно, не для всех. Блокировка с помощью WRITE означает, что вы не можете READ. Только READ блокировка позволит другим READ.

...