Применяется ли блокировка строк в подчиненной базе данных к основной базе данных?

Например, в транзакции

START TRANSACTION;
SELECT count(*) as count FROM `order` WHERE user_id = 25286 LOCK IN SHARE MODE;
INSERT INTO `order` (`id`, `user_id`, `product_id`) VALUES (NULL, '25286', '36296');

мы выполняем SELECT LOCK IN SHARE MODE; SELECT запрос будет выполнен на ведомой базе данных и заблокирует строку.

Ли SELECT LOCK IN SHARE MODE также создать блокировку строки в базе данных master, чтобы запрос INSERT не выполнялся в базе данных master?

1 ответ

НЕТ - Блокировка не проникает через Сервер (ы). Таким образом, явная блокировка на подчиненном сервере не блокирует соответствующие таблицы на главном сервере, и наоборот.

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

Я сделал подобный вид фильтрации в коде PHP. Для направления запросов на соответствующие серверы (чтение запросов на ведомый и запись запросов на ведущий) пишется пользовательская функция для определения типа запроса.

Следует отметить, что запросы в операции Transaction/Lock/Unlock всегда считаются запросами на запись.

Также для Set, только SET AUTOCOMMIT а также SET TRANSACTION Есть команды записи.

Ниже приведена в значительной степени урезанная версия фактического кода, который мы используем:

/*
* All the WRITE operation commands
*/
$write_commands = array(
    'create', 
    'alter', 
    'drop', 
    'truncate',
    'comment', 
    'rename', 
    'insert', 
    'update',
    'delete', 
    'merge', 
    'call', 
    'lock', 
    'unlock',
    'start', 
    'commit', 
    'rollback', 
    'savepoint',
    'set', 
    'replace' 
);


/*
* method to determine whether Read or Write
* @param $sql String (SQL query string)
* @return: void
*/
function determineReadOrWrite(string $sql): void {

    $dml_query = false;

    $words = str_word_count(strtolower(trim($sql)), 1);
    $first_word = isset($words[0]) ? $words[0] : '';
    $second_word = isset($words[1]) ? $words[1] : '';

    if (in_array($first_word, $this->write_commands)) {
        /* if it is not "set" then we set to master link */
        if ($first_word !== 'set'
        || ($first_word === 'set' && $second_word === 'autocommit')
        || ($first_word === 'set' && $second_word === 'transaction')
        ) {
            $dml_query = true;

            /* If we Lock tables or Begin a Transaction, we should run on Write servers only */
            /* till we Commit/Rollback or Unlock Tables */
            if(($first_word === 'start' && $second_word === 'transaction') 
            || $first_word === 'lock'){

                /* Set whether the current query is starting a Transaction / Lock etc */
                $this->wait_for_commit_rollback = true;
            }

            /* We are doing Commit/Rollback or Unlock Tables */
            if ($first_word === 'commit' 
            || $first_word === 'rollback' 
            || $first_word === 'unlock') {
                $this->wait_for_commit_rollback = false;
            }
        }
    }

    /* It's a insert/update/delete/etc query - to be run on Write Db only */
    if ($dml_query || $this->wait_for_commit_rollback) { 
        $this->setActiveConnectionToWrite(true);            

    } else {
        $this->setActiveConnectionToRead();
    }

}
Другие вопросы по тегам