Одновременный доступ - решение проблемы

Друзья, я решил поделиться с Вами схемой, которая позволяет исключить или свести к минимуму ситуации «заваливания» backend’а одинаковыми «тяжелыми» запросами:

  1. Получаем доступ к кэшу, его срок жизни истёк.
  2. Пытаемся заблокироваться по ключу user cache_lock.
    • Не удалось получить блокировку:
      • ждём снятия блокировки;
      • не дождались: возвращаем старые данные кэша;
      • дождались: выбираем значения ключа заново, возвращаем новые данные (построенный кэш другим процессом).
    • Удалось получить блокировку:
      • строим кэш самостоятельно.

Однако эта схема плоха тем, что не гарантирует 100% корректности, и вот почему.

Блокировки на примере memcached

Рассмотрим два варианта реализации блокировки с помощью memcached.

Семафор

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

Пусть мы хотим заблокироваться по ключу ‘lock’: пытаемся получить значения ключа с помощью операции get. Если ключ не найден, значит блокировки нет, и мы с помощью операции set устанавливаем значение этого ключа, например, в единицу, а время жизни устанавливаем в небольшой интервал времени, который превышает максимальное время жизни блокировки, например, в 10 секунд. Теперь, если frontend завершится аварийно и не снимет блокировку, она автоматически уничтожится через 10 секунд. Итак, с помощью set мы блокировку установили, выполнили все необходимые действия, после этого снимаем блокировку просто удаляя соответствующий ключ командой del. Если на первой операции get мы получили значение ключа, это означает, что блокировка уже установлена другим процессом, наша операция блокировки неуспешна.

Описанный способ обладает недостатком: наличием состояния гонки (race condition). Два процесса могут одновременно сделать get, оба могут получить ответ, что «ключа нет», оба сделают set, и оба будут считать, что установили блокировку успешно. В ситуациях, как одновременное перестроение кэшей, этого может быть допустимо, т.к. здесь цель не исключить все другие процессы, а резко уменьшить количество одновременных запросов к БД, что может обеспечить и этот простой, некорректный вариант.

Мьютекс

Реализуется на стороне сервера (в нашем примере в memcached), вариант корректен, и даже проще первого. Для захвата блокировки достаточно выполнить одну команду: add, указав имя ключа и время жизни (такое же маленькое, как и в первом варианте). Команда add будет успешной только в том случае, если ключа в memcached еще нет, то есть наш процесс и есть тот единственный процесс, которому удалось захватить блокировку. Тогда нам надо выполнить необходимые действия и освободить блокировку командой del. Если add вернет ошибку «такой ключ уже существует», значит, блокировка была захвачена раньше каким-то другим процессом.

Источник: 1

Оцени публикацию:
  • 1,3
Оценили: 1
Теги : алгоритм

Предложения и пожелания:

 

youtube.com/watch?v=7hFivbgIEqk

При полном или частичном использовании материалов данного сайта, ссылка на сайт "yapro.ru" обязательна как на источник информации.
Автоматический импорт материалов и информации с сайта запрещен.
Copyrights © 2007 - 2019 YaPro.Ru

Лебеденко Николай Николаевич
Ошибка в тексте? Выделите её мышкой и нажмите: Ctrl + Enter