Триггеры и блокировка таблицы в MySQL

сценарий: у меня есть некоторые триггеры, которые отслеживают количество записей одной таблицы вместе с другой полезной информацией. Эти триггеры запускаются при добавлении/удалении / обновлении в этой таблице и заботятся о записи этой информации в другую дополнительную таблицу.

теперь эти триггеры будут работать в многопоточной среде, где, возможно, у меня может быть параллельный доступ к таблицам. Я хотел бы сделать что-то подобное, но оно запрещено (ошибка: код ошибки: 1314. Блокировка не допускается в хранимых процедурах):

DELIMITER $$
DROP TRIGGER IF EXISTS del_alarmCount$$
CREATE TRIGGER del_alarmCount AFTER DELETE ON Alarm
FOR EACH ROW
BEGIN
SET autocommit=0;
LOCK TABLES AlarmCount WRITE, AlarmMembership READ;
  UPDATE AlarmCount SET num = num - 1 
  WHERE RuleId = OLD.RuleId AND
      MemberId = 0 AND
      IsResolved = OLD.IsResolved;

  UPDATE AlarmCount SET num = num - 1 
  WHERE RuleId = OLD.RuleId AND
      IsResolved = OLD.IsResolved AND
      MemberId IN (SELECT MemberId FROM AlarmMembership WHERE AlarmId=OLD.Id);
COMMIT;
UNLOCK TABLES;
END $$
DELIMITER ;

цели для достижения с помощью этих блокировок (или альтернативных конструкций):

  1. избегайте двух триггеров, работающих одновременно, пишите в таблицу AlarmCount и обновляйте связанные записи (я думаю, у меня может быть два триггера, работающих для разных записей таблицы Alarm, обновляющих одну и ту же запись AlarmCount)
  2. убедитесь, что таблица AlarmMembership не изменяется (например, целевой MemberId тем временем удаляется).

любой совет очень приветствуется!

1 ответов


Я думаю, что лучший способ справиться с этим-использовать SELECT ... Для шаблона обновления, описанного здесь:http://dev.mysql.com/doc/refman/5.0/en/innodb-locking-reads.html

Для справки:

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

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

для реализации чтения и увеличивая счетчик, сначала выполните блокировка чтения счетчика с помощью обновления, а затем увеличение счетчик. Например:

 SELECT counter_field FROM child_codes FOR UPDATE; UPDATE child_codes
 SET counter_field = counter_field + 1; 

A ВЫБЕРИТЕ ... Для обновления считывает последние доступные данные, устанавливая эксклюзивные блокировки на каждой строке > он читает. Таким образом, он устанавливает те же блокировки, что и искомое обновление SQL для строк.

. . .

Примечание блокировка строк для обновления с помощью SELECT FOR UPDATE применяется только когда autocommit отключен (либо путем начала транзакции с Запустите транзакцию или установив autocommit в 0. Если autocommit включено, строки, соответствующие спецификации, не заблокированы.

таким образом, в вашем случае вы замените

LOCK TABLES AlarmCount WRITE, AlarmMembership READ;
  UPDATE AlarmCount SET num = num - 1 
  WHERE RuleId = OLD.RuleId AND
      MemberId = 0 AND
      IsResolved = OLD.IsResolved;

С чем-то вроде

SELECT num FROM AlarmCount WHERE RuleId = OLD.RuleId AND
          MemberId = 0 AND
          IsResolved = OLD.IsResolved FOR UPDATE;
UPDATE AlarmCount SET num = num - 1;

Я говорю "что-то вроде", потому что мне не совсем ясно, какое старое.Ruleid и старый.Разрешить ссылается. Также стоит отметить от http://dev.mysql.com/doc/refman/5.0/en/innodb-locking-reads.html это:

предыдущее описание-это просто пример того, как выбрать ... ДЛЯ Обновление работает. В MySQL конкретная задача генерации уникального идентификатор фактически может быть выполнен с использованием только одного доступа к стол:

UPDATE child_codes SET counter_field = LAST_INSERT_ID(counter_field +
1); 
SELECT LAST_INSERT_ID();

инструкция SELECT просто извлекает информацию об идентификаторе (специфичную для текущего соединение.) Это нет доступа к любой таблице.

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

Я должен упомянуть а также, что есть некоторые среды ядра хранения и уровни изоляции транзакций, которые вы хотите рассмотреть. Существует очень, очень хорошая дискуссия на эту тему здесь:когда использовать SELECT ... ДЛЯ ОБНОВЛЕНИЯ?

надеюсь, что это помогает!