Включает ли MySQL InnoDB очередь DB триггеров автоматически? - PullRequest
5 голосов
/ 21 августа 2011

Допустим, на наши серверы поступило 1000 запросов на обновление одной таблицы MySQL.Проблемы тупика неизбежно возникают в этой ситуации.Мы попытались опубликовать транзакцию в соответствии с рекомендациями для взаимоблокировок, но они все еще происходят.

Мы подумываем о том, чтобы найти альтернативное решение ниже.

  1. Создать таблицу A, B, C.
  2. Запросы записи, поступающие на сервер для обновления таблицы D, в A, B или C.
  3. Создайте триггер 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, чтобы сделать работу?

Ответы [ 3 ]

1 голос
/ 22 сентября 2011

Хорошо, значит, вы используете одну таблицу и несколько триггеров?

А у вас очень мало транзакций в секунду?

А у вас странные проблемы с блокировкой?

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

99,99%из вероятностей, что ваша проблема вызвана VERY_SLOW_TRIGGERS, я имею в виду, что она очень сильно очень медленная, потому что только 8 в секунду подразумевают время выполнения транзакции 125 мс, которое .. огромно.

Причина блокировки очевидна,вы вызываете триггер для таблицы D.

-> call modification on table D
 -> before mod trigger
 -> modification
 -> after mod trigger
-> modification complete

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

Вы можете:

a) заблокировать меньше строк

b) заблокировать меньше времени -> вставить в другую таблицу, обработать асинхронно оттуда

c) использовать rdbms, которыеправильно поддерживает триггеры

Опция балансировки - это опция hammer-vs-fly, нет причины, по которой вам понадобится более одного сервера для такого низкого количества tps.

Однако вам следует устранить неполадки с производительностью вашего триггера и убедиться, чтовы не сталкиваетесь с перегрузкой ввода-вывода где-то (обычно то, что излишне медленно, имеет тенденцию также чрезмерно использовать драгоценные ресурсы).

Хорошо, вот еще один вариант:

UNLOCK TABLES явно освобождает любую таблицублокировки, удерживаемые текущей сессией.

ЕСЛИ ваше последнее действие заключается в том, что обновление / вставка И ЕСЛИ сбой вашего триггера либо НЕВОЗМОЖЕН, либо НЕ ПРОБЛЕМА

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

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

обновить и вставить в mysql заблокированную и синхронизированную операцию, предположим, что у вас есть 2 запроса от 2 триггера для обновления таблицы D, когда 1 обновляет таблицу D, в очереди ожидается секунда. можно запросить одновременно. Если вы хотите сделать эту транзакцию одновременно, вы должны создать репликацию

0 голосов
/ 30 августа 2011

В этом случае для использования администраторами баз данных MYSQL предусмотрена функция «репликация», которая заключается в разделении одного сервера на несколько серверов, необходимых для балансировки нагрузки. Вы можете сделать это с помощью одного мощного оборудования, разделенного на 2 или более виртуальных сервера, работающих внутри виртуальных устройств с VirtualBox, VirtualPC или вашей разновидностью виртуализации, с включенной функцией репликации MYSQL.

Вы можете настроить один сервер для записи (ваши обновления в этом случае) и другие серверы для запросов, которые считывают данные. См. Документацию по репликации MYSQL здесь

...