Триггеры и блокировка таблицы в 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 ;
цели для достижения с помощью этих блокировок (или альтернативных конструкций):
- избегайте двух триггеров, работающих одновременно, пишите в таблицу AlarmCount и обновляйте связанные записи (я думаю, у меня может быть два триггера, работающих для разных записей таблицы Alarm, обновляющих одну и ту же запись AlarmCount)
- убедитесь, что таблица 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 ... ДЛЯ ОБНОВЛЕНИЯ?
надеюсь, что это помогает!