Рубрики
Uncategorized

Решение проблем параллелизма с распределенными блокировками

Автор оригинала: David Wong.

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

Решение проблемы, заключающейся в том, что программный блок выполняется одновременно несколькими процессами, заключается в обеспечении того, чтобы только один процесс мог выполнять один и тот же программный блок одновременно. Другие процессы ожидают завершения выполнения текущего процесса, прежде чем они смогут получить вес выполнения программного блока для обновления данных. По аналогии, параллельное выполнение может быть изменено на последовательное выполнение. Чтобы сохранить процесс, который получает право на выполнение, от других помех, необходимо установить тег, который может быть прочитан всеми процессами. Если тег не существует, его можно установить. Когда другие последующие процессы обнаруживают, что тег помечен, они ждут, пока процесс, у которого есть тег, завершит выполнение блока, чтобы отменить тег, прежде чем пытаться установить тег. Этот тег можно понимать как блокировку, и процесс установки тега-это то, что мы обычно называем блокировкой.

Распределенная блокировка С использованием метода Setnx и истечения срока действия для перераспределения

  • setnx()

Значение setnx задано, если оно не существует, которое имеет два основных параметра: setnx (ключ, значение). Этот метод является атомарным. Если ключ не существует, текущий ключ устанавливается успешно и возвращает 1. Если текущий ключ уже существует, текущий ключ завершается ошибкой и возвращает 0.

  • истекает срок действия()

Срок действия устанавливает время истечения срока действия. Следует отметить, что команда setnx не может установить время ожидания ключа, но может установить ключ только через expire ().

  • Конкретные шаги

1. Setnx (ключ блокировки, 1) Если он возвращает 0, размещение завершается неудачно; если он возвращает 1, размещение завершается успешно.

2. Команда expire () устанавливает время ожидания для ключа блокировки, чтобы избежать проблем с взаимоблокировкой.

3. После выполнения бизнес-кода вы можете удалить ключ с помощью команды удалить.

Это решение действительно может удовлетворить потребности повседневной работы, но с точки зрения технических решений могут быть некоторые области, которые можно улучшить. Например, если после успешного выполнения первого шага setnx и до успешного выполнения команды expire () по-прежнему будут возникать проблемы с взаимоблокировкой, поэтому, если мы хотим ее улучшить, мы можем использовать методы redisetnx (), get () и GetSet () для достижения распределенных блокировок.

Распределенная блокировка с использованием Setnx (), get (), Get Set () redis

Предыстория этой схемы в основном заключается в оптимизации схем setnx () и expire () для возможных проблем с взаимоблокировкой.

  • getset()

Эта команда имеет два основных параметра: Get Set (ключ, новое значение). Этот метод является атомарным, устанавливает значение нового значения для ключа и возвращает старое значение ключа. Предполагая, что ключ не существует, многократное выполнение этой команды приведет к следующему эффекту:

  1. GetSet (ключ, “значение 1”) возвращает значение null, а значение ключа равно значению1
  2. GetSet (ключ, “значение 2”) возвращает значение 1, если значение ключа равно Значению2
  3. Аналогия в свою очередь!
  • Используйте шаги
  1. Setnx (ключ блокировки, текущее время + время истечения срока действия), если возвращено 1, блокировка успешно получена; если возвращено 0, блокировка не получена, перейдите к шагу 2.
  2. Get (ключ блокировки) получает значение, которое представлено старым временем истечения срока действия для времени истечения текущего ключа блокировки, и сравнивает старое время истечения срока действия с текущим системным временем. Если считается, что срок действия блокировки истек раньше текущего системного времени, другим запросам может быть разрешено получить ее и перейти к шагу 3, в противном случае решение может быть перезапущено после ожидания указанного времени для возврата к шагу 2.
  3. Вычислите новое время + время истечения срока действия, а затем получите значение Set (клавиша блокировки, newExpireTime), возвращающее старое значение currentExpireTime, установленное перед текущей клавишей блокировки.
  4. Определите, равно ли текущее время истечения и старое время истечения. Если они равны, текущий процесс Get Set успешно устанавливает блокировку и получает блокировку. Если блокировка не равна, что указывает на то, что блокировка была получена другим процессом, то текущий запрос может напрямую вернуться к сбою в соответствии с логикой конкретных требований или вернуться к шагу 2 для продолжения повторной попытки.
  5. После получения блокировки текущий процесс может начать собственную бизнес-обработку. Когда обработка будет завершена, сравните текущее время обработки и время ожидания настроек блокировки. Если время ожидания настроек блокировки меньше, чем время ожидания настроек блокировки, блокировка удаления будет выполнена напрямую. Если время ожидания настроек блокировки больше, чем время ожидания настроек блокировки, блокировка может быть получена другими процессами, то выполнение блокировки удаления приведет к ее освобождению. Блокировка, которую приобрел его процесс, освобождается.

Ниже приведена распределенная блокировка Redis, реализованная с помощью PHP-кода. О красной части, используется псевдокод. Пожалуйста, замените псевдокод объектом подключения Redis в соответствии с вашей собственной ситуацией.

/**
 * Getting Redis Distributed Locks
 *
 * @param $lockKey
 * @return bool
 */
function getRedisDistributedLock(string $lockKey) : bool
{
    LockTimeout = 2000; // Lock timeout time 2000 milliseconds
    $now = intval(microtime(true) * 1000);
    $lockExpireTime = $now + $lockTimeout;
    $lockResult = Redis::setnx($lockKey, $lockExpireTime);

    if ($lockResult) {
        // The current process setup lock succeeded
        return true;
    } else {
        $oldLockExpireTime = Redis::get($lockKey);
        if ($now > $oldLockExpireTime && $oldLockExpireTime == Redis::getset($lockKey, $lockExpireTime)) {
            return true;
        }
    }

    return false;
}

/**
 * Serial Execution Program
 *
 * @param string $lockKey Key for lock
 *@ Param Closure $close gets the closure to be executed by the post-lock process
 * @return mixed
 */
function serialProcessing(string $lockKey, Closure $closure)
{
    if (getRedisDistributedLock($lockKey)) {
        $result = $closure();
        $now = intval(microtime(true) * 1000);
        if ($now < Redis::get($lockKey)) {
            Redis::del($lockKey);   
        }
    } else {
        // Delay 200 milliseconds before execution
        usleep(200 * 1000);
        return serialProcessing($lockKey, $closure);
    }

    return $result;
}

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

Приведенный выше код реализован процессно-ориентированным способом, чтобы просто описать, как настроить распределенные блокировки. Читатели могут внедрять и разрабатывать современный код для своей собственной ситуации. Для крупномасштабных систем, использующих кластерные Redis, шаги по настройке распределенных блокировок более сложны и интересны. Редлок Алгоритмы и рэдиссон Компонент распределенной блокировки Redis.

Оригинал: “https://developpaper.com/solving-concurrency-problems-with-distributed-locks/”