Допустим, на наши серверы поступило 1000 запросов на обновление одной таблицы MySQL.Проблемы тупика неизбежно возникают в этой ситуации.Мы попытались опубликовать транзакцию в соответствии с рекомендациями для взаимоблокировок, но они все еще происходят.
Мы подумываем о том, чтобы найти альтернативное решение ниже.
- Создать таблицу A, B, C.
- Запросы записи, поступающие на сервер для обновления таблицы D, в A, B или C.
- Создайте триггер INSERT для таблиц A, B и C соответственно, который, в свою очередь, запишет данные вТаблицу D, вместо того, чтобы непосредственно подвергать Таблицу D 1000 запросам, поступающим на сервер.
Поэтому мы задаемся вопросом, когда это происходит, и несколько строк записываются в Таблицы A, B и C, лежащие в основе Триггеры в Таблицах.A, B и C могут запускаться одновременно для обновления таблицы D.
Механизм MySQL InnoDB автоматически ставит в очередь эти триггеры или мы должны обрабатывать это в нашем коде?
Любая помощьвысоко ценится.
Таблица D, которая теперь обновляется непосредственно всеми этими запросами и где возникает тупик, выглядит следующим образом.
v_user_email varchar(60) NO PRI
v_device_IMEI varchar(40) NO PRI
i_adid int(11) NO PRI
i_impressions int(4) YES 0
dt_pulllogdttm datetime NO
c_created_by char(15) NO
dt_created_on datetime NO
c_modified_by char(15) YES
dt_modified_on datetime YES
PHP thaВставка / обновление строк в этой таблице выглядит следующим образом.Вы увидите, что мы пытаемся опубликовать транзакцию 3 раза, если она завершится неудачно из-за взаимоблокировки, но есть транзакции, которые проваливаются даже тогда, и журнал сообщает об этом из-за тупика.
$updateQuery = "UPDATE tb_ad_pull_log SET i_impressions = (i_impressions + 1), dt_pulllogdttm = SYSDATE(), c_modified_by = '$createdBy', dt_modified_on = SYSDATE() WHERE v_user_email = '$email' AND i_adid = $adId";
if(ExecuteDeadLockQuery($updateQuery, "UPDATE", __LINE__) == 0) // If there is no record for this ad for the user, insert a new record
{
$insertQuery = "INSERT INTO tb_ad_pull_log VALUES('$email', '$device_IMEI', $adId, 1, SYSDATE(), '$createdBy', SYSDATE(), NULL, NULL)";
ExecuteDeadLockQuery($insertQuery, "INSERT", __LINE__);
}
Функция ExecuteDeadLockQuery выглядит следующим образом -
function ExecuteDeadLockQuery($query, $activity, $lineNumber)
{
global $errorLoggingPath;
$maxAttempts = 3;
$currentTry = 1;
$noOfAffectedRows = -1;
while($currentTry <= $maxAttempts)
{
$currentTry++;
mysql_query($query);
if( mysql_errno() <> 0 ) // If error occured
{
continue;
}
else
{
$noOfAffectedRows = mysql_affected_rows();
break;
}
}
if($noOfAffectedRows == -1) // Query never executed successfully
{
LogError($activity . " failed in tb_ad_pull_log: " . mysql_error(), __FILE__, $lineNumber , $errorLoggingPath);
}
return $noOfAffectedRows;
}
Есть ли более чистый способ избежать этого тупика?Вот некоторые журналы, которые у нас есть.
ERROR: 08-21-2011 14:09:57 UPDATE failed in tb_ad_pull_log: Deadlock found when trying to get lock; try restarting transaction LINE 83
ERROR: 08-21-2011 14:09:57 INSERT failed in tb_ad_pull_log: Deadlock found when trying to get lock; try restarting transaction LINE 86
ERROR: 08-21-2011 14:09:57 INSERT failed in tb_ad_pull_log: Deadlock found when trying to get lock; try restarting transaction LINE 86
ERROR: 08-21-2011 14:09:57 UPDATE failed in tb_ad_pull_log: Deadlock found when trying to get lock; try restarting transaction LINE 83
ERROR: 08-21-2011 14:09:57 INSERT failed in tb_ad_pull_log: Deadlock found when trying to get lock; try restarting transaction LINE 86
ERROR: 08-21-2011 14:09:57 UPDATE failed in tb_ad_pull_log: Deadlock found when trying to get lock; try restarting transaction LINE 83
ERROR: 08-21-2011 14:09:59 UPDATE failed in tb_ad_pull_log: Deadlock found when trying to get lock; try restarting transaction LINE 83
ERROR: 08-21-2011 14:09:59 UPDATE failed in tb_ad_pull_log: Deadlock found when trying to get lock; try restarting transaction LINE 83
ERROR: 08-21-2011 14:10:01 UPDATE failed in tb_ad_pull_log: Deadlock found when trying to get lock; try restarting transaction LINE 83
ERROR: 08-21-2011 14:10:01 INSERT failed in tb_ad_pull_log: Deadlock found when trying to get lock; try restarting transaction LINE 86
Строка 83 - это оператор UPDATE в PHP, а 86 - INSERT.Помните, что эти данные могут быть записаны в эту таблицу со скоростью 5-8 транзакций в секунду.
Дополнительная информация
При каждом ВСТАВЛЕНИИ и ОБНОВЛЕНИИ в ТАБЛИЦУ D запускается триггер, который обновляет ТАБЛИЦУ X и ТАБЛИЦУ Y. Является ли это причиной того, что Таблица D остается заблокированной, и, следовательно, входящие запросы получают взаимоблокировку?
Наконец-то появилась проблема, но я не уверен, как ее решить.Триггеры AFTER INSERT и AFTER UPDATE на ТАБЛИЦЕ D блокируют таблицу, когда они запускаются, и, следовательно, блокируют входящие запросы.Почему я так уверен в этом, потому что после того, как я отбросил эти триггеры, журнал прекратил регистрацию сообщений о взаимоблокировках, зарегистрированных в противном случае
Фрагмент кода триггера.
CREATE DEFINER=CURRENT_USER TRIGGER tuadmin.t_update_CPM_updateBalance
AFTER UPDATE
ON tb_ad_pull_log
FOR EACH ROW
BEGIN
DECLARE `cpm_value` decimal(10,4);
DECLARE `clientid` int(4);
/* Execute the below block if the requested ad is not the default ad */
IF NEW.i_adid <> 1 THEN
SET SESSION TRANSACTION ISOLATION LEVEL READ COMMITTED;
//do updates to TABLE X and Y logic
END
Thisя не понимаю, почему эти триггеры удерживают блокировку таблицы D и не позволяют одновременной вставке / обновлению.
Это позволит избежать всех проблем, если мы отбросим триггеры и простовызвать SP из PHP, чтобы сделать работу?