Все вопросы интервью о распределенных блокировках здесь

распределенный

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

Думая, попивая чай, вы не хотите заниматься проституцией даром! Три раза подряд?

введение

Зачем изучать распределенные блокировки?

Самая простая причина в том, что как программист социального рекрутинга, вы должны пройти собеседование во время собеседования.Видите сколько публичных аккаунтов было раз за разом на тему распределенных блокировок.Видно что это очень важно.В колледже вступительный экзамен, это к вопросу об отправке баллов., не вините жалко.

Свежие выпускники спросят? Это не обязательно, но если сможете, интервьюер обязательно даст вам больше баллов (денег)

В-третьих, распределенные блокировки — обязательный навык в немного крупномасштабной системе. Взгляните серьезно.

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

Распределенная блокировка — важный примитив в распределенной среде, указывающий на то, что разные процессы работают с общими ресурсами взаимоисключающим образом. Распространенным сценарием является внедрение в масштабный проект в виде sdk, который в основном решает два типа задач:

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

Блокировки в Java:

Блокировка является очень распространенным инструментом в процессе разработки.Вы должны быть знакомы с пессимистическими блокировками, оптимистичными блокировками, эксклюзивными блокировками, справедливыми блокировками, нечестными блокировками и т. д. Существует много понятий.Если вы не знакомы с блокировками в Java, вы можете обратитесь к ним.Java-блокировка, о которой нужно сказать(https://tech.meituan.com/2018/11/15/java-lock.html), эта статья очень всеобъемлющая, но для начинающих, которые знают концепцию этих замков, из-за отсутствия практического опыта работы, она может не быть фактическим сценарием использования разблокировки.В Java три ключевых слова Volatile, Synchronized и ReentrantLock могут использоваться для обеспечения безопасности потока.Эта часть знаний обязательно будет задана в первом раунде базовых собеседований (чтобы быть опытным ).

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

1. Интервьюер: Вы когда-нибудь сталкивались со сценарием, в котором вам нужно использовать распределенные блокировки?

анализ проблемы:

Этот вопрос в основном используется в качестве введения. Во-первых, вам нужно понять, в каких сценариях необходимо использовать распределенные блокировки и какие проблемы должны решать распределенные блокировки. Исходя из этого, это поможет вам лучше понять принципы реализации распределенных блокировок.

Сценарии, использующие распределенные блокировки, обычно должны соответствовать следующим сценариям:

  1. Система является распределенной системой, и блокировка java больше не может быть заблокирована.
  2. Управление общими ресурсами, такими как уникальные пользовательские данные в библиотеке.
  3. Синхронный доступ, то есть несколько процессов одновременно работают с общим ресурсом.

Я:

Приведите пример сценария распределенной блокировки, который я использую в своем проекте:

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

Событие А:Взяв в качестве примера обмен баллов на подарки, весь процесс использования баллов просто делится на 3 этапа:

A1: Пользователь выбирает продукт, инициирует обмен и отправляет заказ.

A2: Система считывает оставшиеся баллы пользователя: чтобы определить, достаточно ли текущих баллов пользователя.

A3: Пользовательские баллы будут вычтены.

Событие Б:Система выдачи баллов пользователям также просто делится на 3 шага:

B1: Подсчитайте баллы, которые пользователь заслуживает за день.

B2: Прочитайте исходные точки пользователя

B3: Добавьте баллы, заработанные на этот раз, к исходным баллам.

Итак, вопрос в том, что произойдет, если пользовательские очки потребления и накопленные пользовательские очки появятся в одно и то же время (в то же время, когда пользовательские очки будут работать)?

Предположение: пока пользователь потребляет баллы, автономной задачей является подсчет баллов и выдача баллов пользователю (например, в соответствии с суммой потребления пользователем в день). Эти две вещи выполняются одновременно. логика немного сбивает с толку, пожалуйста, поймите с терпением.

Пользователь U имеет 1000 баллов (данные, записывающие пользовательские баллы, можно понимать какПоделиться ресурсом), это погашение потребует 999 очков.

Без блокировки:Когда программа события A выполняется до второго шага для чтения очков, результат, считанный операцией A: 2, составляет 1000 очков. Считается, что оставшихся очков достаточно для этого выкупа, а затем третий шаг A: 3 выполняется операция по вычету баллов (1000 - 999 = 1), нормальный результат должен быть пользователем или 1 баллом. Но в это времясобытие БОн также выполняется, на этот раз пользователю U будет выдано 100 баллов, а два потока будут выполняться одновременно (Синхронизированный доступ), без блокировки, будет следующая возможность, A:2 -> B:2 -> A:3 -> B:3, до завершения A:3 (вычесть баллы, 1000 - 999) общее количество баллов пользователя U читаются потоком события B,В итоге сумма баллов пользователя U стала 1100 баллов, а еще он обменял подарок в 999 баллов ни за что, что явно не соответствовало ожидаемому результату.

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

(Написание кода — серьезное дело!)

Сама Java предоставляет две встроенные реализации блокировки, одна из которых синхронизируется с помощью JVM, а блокировка предоставляется с помощью JDK, а многие классы атомарных операций являются потокобезопасными. с использованием обоих типов замков.

Однако системы интернет-компаний почти все распределены.В настоящее время синхронизация или блокировка, которые поставляются с Java, больше не могут соответствовать требованиям блокировки в распределенной среде, потому что код будет развернут на нескольких машинах.Чтобы решить Эта проблема возникла с распределенными блокировками типов. Распределенные блокировки характеризуются несколькими процессами, и несколько физических машин не могут совместно использовать память. Распространенное решение основано на вмешательстве уровня памяти. Целевое решение — распределенные блокировки на основе Redis или ZooKeeper. распределенные замки.

(Подробнее разобрать не могу, интервьюера уже не устраивает?)

2. Интервьюер: Какие решения для распространенных распределенных блокировок вы знаете?

Я: Есть три распространенных способа!

  1. Распределенный замок Рейдиса, многие крупные компании будут делать расширенную разработку на основе Рейдиса.
  2. На основе зоопарка
  3. На основе базы данных, такой как Mysql.

3. Интервьюер: Расскажите о методе реализации распределенной блокировки Redis.

анализ проблемы:

В настоящее время существует два основных способа реализации распределенных блокировок: 1. На основе режима Redis Cluster. 2. На основе режима кластера Zookeeper.

Отдайте предпочтение обоим, справиться с интервью в принципе не проблема.

Существует примерно три способа блокировки, а именно распределенная блокировка DB, распределенная блокировка Redis и распределенная блокировка Zookepper.

Я:

1. Распределенная блокировка на основе Redis

Способ 1: Используйте команду setnx для блокировки

public static void wrongGetLock1(Jedis jedis, String lockKey, String requestId, int expireTime) {
  // 第一步:加锁
    Long result = jedis.setnx(lockKey, requestId);
    if (result == 1) {
        // 第二步:设置过期时间
        jedis.expire(lockKey, expireTime);
    }

}

Объяснение кода:

  • Команда setnx означает установить, если не существует. Если lockKey не существует, сохраните ключ в Redis. После успешного сохранения, если результат возвращает 1, это означает, что настройка выполнена успешно. Если это не 1, это означает что он терпит неудачу, и другие потоки уже установили его.
  • expire(), установите время истечения срока действия, чтобы предотвратить взаимоблокировку.Предположим, если блокировка не удаляется после ее установки, тогда блокировка эквивалентна постоянному существованию, что приводит к взаимоблокировке.

(Говоря об этом, я хотел бы подчеркнуть одно «но» у интервьюера)

Думая, где недостаток в моем методе выше? Продолжайте объяснять интервьюеру...

Блокировка разделена на два шага.Первый шаг — jedis.setnx, а второй шаг — jedis.expire для установки времени истечения срока действия.Setnx и expire не являются атомарными операциями.Если программа вышла из строя после первого шага, второй шаг jedis.expire(lockKey, expireTime) не выполняется, что означает, что блокировка не имеет срока действия, и существует вероятность взаимоблокировки. Как улучшить эту проблему?

Улучшать:

public class RedisLockDemo {

    private static final String SET_IF_NOT_EXIST = "NX";
    private static final String SET_WITH_EXPIRE_TIME = "PX";

    /**
     * 获取分布式锁
     * @param jedis Redis客户端
     * @param lockKey 锁
     * @param requestId 请求标识
     * @param expireTime 超期时间
     * @return 是否获取成功
     */
    public static boolean getLock(Jedis jedis, String lockKey, String requestId, int expireTime) {

    // 两步合二为一,一行代码加锁并设置 + 过期时间。
        if (1 == jedis.set(lockKey, requestId, SET_IF_NOT_EXIST, SET_WITH_EXPIRE_TIME, expireTime)) {
            return true;//加锁成功
        }
        return false;//加锁失败

    }

}

Объяснение кода:

Объедините блокировку и установку времени истечения в одну, одну строку кода, атомарную операцию.

(Не дожидаясь, пока интервьюер задаст вопросы, интервьюер был очень доволен)

Интервьюер:Что с операцией разблокировки?

Я:

Снять замок - значит удалить ключ

Разблокировать командой del

public static void unLock(Jedis jedis, String lockKey, String requestId) {
        
    // 第一步: 使用 requestId 判断加锁与解锁是不是同一个客户端
    if (requestId.equals(jedis.get(lockKey))) {
        // 第二步: 若在此时,这把锁突然不是这个客户端的,则会误解锁
        jedis.del(lockKey);
    }
}

Объяснение кода:Судя по requestId, является ли блокировка и разблокировка одним и тем же клиентом, а jedis.del(lockKey) не является атомарной операцией Теоретически будет казаться, что срок действия блокировки истек после первой, если выполняется операция суждения, и она получена другими потоками ., это время для выполнения операции jedis.del(lockKey), что равносильно снятию чужих блокировок, что неразумно. Конечно, это очень крайняя ситуация.Если на первом и втором шагах метода разблокировки нет других бизнес-операций, а приведенный выше код выбрасывается в онлайн, реальной проблемы может и не быть.Первая причина - сумма Параллелизм бизнеса.Если он не высок, этот дефект вообще не будет виден, так что проблема невелика.

Но написание кода — это кропотливая работа, и она должна быть идеальной, если может быть идеальной. В ответ на проблемы в приведенном выше коде предлагаются улучшения.

Улучшения кода:

public class RedisTool {

    private static final Long RELEASE_SUCCESS = 1L;

    /**
     * 释放分布式锁
     * @param jedis Redis客户端
     * @param lockKey 锁
     * @param requestId 请求标识
     * @return 是否释放成功
     */
    public static boolean releaseDistributedLock(Jedis jedis, String lockKey, String requestId) {

        String script = "if redis.call('get', KEYS[1]) == ARGV[1] then return redis.call('del', KEYS[1]) else return 0 end";
        Object result = jedis.eval(script, Collections.singletonList(lockKey), Collections.singletonList(requestId));

        if (RELEASE_SUCCESS.equals(result)) {
            return true;
        }
        return false;

    }

}

Объяснение кода:

С помощью метода eval клиента jedis и сценария сценария одна строка кода используется для решения атомарной проблемы в первом методе.

4. Интервьюер: Расскажите о принципе реализации распределенной блокировки на базе ZooKeeper.

Я:

Или пример потребления и накопления баллов:событие Аисобытие БВ то же время необходимо выполнить операцию модификации интеграла, и две машины выполняются одновременно.В правильной бизнес-логике сначала выполняется одна машина, а затем другая машина. событие A выполняется первым или событие B выполняется первым, чтобы гарантировать, что не будет ситуаций, когда A:2 -> B:2 -> A:3 -> B:3 будет тратить все больше и больше очков (думая что как только такая ошибка появится, босс разозлится, а я могу заплакать).

что делать? Используйте распределенные блокировки zookeeper.

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

Используя функцию последовательных узлов ZooKeeper, если мы создадим 3 узла в каталоге /lock/, кластер ZK создаст узлы в том порядке, в котором они были созданы. /0000000003, Последняя цифра последовательно увеличивается, а имя узла завершается на zk.

В ZK также есть узел, называемый временным узлом. Временный узел создается клиентом. Когда клиент отключается от кластера ZK, узел автоматически удаляется. EPHEMERAL_SEQUENTIAL — временный узел последовательности.

В зависимости от того, существует ли узел в ZK, его можно использовать в качестве состояния блокировки распределенной блокировки для реализации распределенной блокировки.Следующее является базовой логикой распределенной блокировки:

  1. Клиент вызывает метод create() для создания временного последовательного узла с именем «/dlm-locks/lockname/lock-».
  2. Клиент вызывает метод getChildren("lockname"), чтобы получить все созданные дочерние узлы.
  3. После того, как клиент получит пути всех дочерних узлов, если он обнаружит, что узел, созданный на шаге 1, имеет наименьший порядковый номер среди всех узлов, это зависит от того, является ли созданный им порядковый номер первым. считается, что клиент получает блокировку, и никакой другой клиент, предшествующий ему, не получает блокировку.
  4. Если созданный узел не самый маленький среди всех узлов, то отслеживайте самый большой узел с меньшим порядковым номером, чем узел, созданный сам по себе, и вводите ожидание. До следующего изменения отслеживаемого дочернего узла дочерний узел снова захватывается, чтобы определить, следует ли запрашивать блокировку.

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

5. Интервьюер: Каковы преимущества и недостатки ЗК и Рейдов?

Давайте сначала поговорим о Ридсе:

  1. Rdis гарантирует только окончательную согласованность, а репликация данных между репликами выполняется асинхронно (Set — для записи, Get — для чтения, а кластеры Reids обычно имеют архитектуру разделения чтения-записи и задержку синхронизации master-slave). переключатель ведущий-ведомый, некоторые данные могут быть недоступны, копирование прошлого можетпотерянный замокПоэтому не рекомендуется использовать Reids для предприятий, требующих строгой согласованности, и рекомендуется использовать zk.
  2. Время отклика каждого метода кластера Redis самое низкое. С увеличением параллелизма и количества сервисов время отклика значительно возрастет (фактор влияния общедоступного кластера слишком велик), но предел qps может достигать максимума и в принципе не является аномалией.

Скажем ЗК еще раз:

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

как выбрать? (К вашему сведению, основано на моем личном опыте)

Сосредоточьтесь на показателях Redis ZK
Чувствительность ко времени отклика
высокая степень параллелизма
Требуется блокировка чтения-записи
требуется честный замок
Требуется несправедливая блокировка

намекать

Для использования распределенных блокировок должно быть выполнено одно из двух условий:

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

Вне зависимости от ZooKeeper и Redis, в крайних случаях (таких как сбой всего кластера ZK, например сбой Мастера Рейдов и Неполная синхронизация Слейва) будет проблема, что блокируемые ресурсы многократно заблокирован. Вероятность такого рода ненадежности чрезвычайно мала и в основном зависит от кластера Zk и кластера Redis.

6. Как Mysql выполняет распределенные блокировки?

Распределенные блокировки также могут решать проблемы из базы данных.

метод первый:

Используйте таблицу блокировок Mysql, создайте таблицу и установите УНИКАЛЬНЫЙ КЛЮЧ.Этот КЛЮЧ является КЛЮЧОМ, который нужно заблокировать, поэтому один и тот же КЛЮЧ можно вставить в таблицу mysql только один раз, поэтому конкуренция за блокировки передается базе данных, и тот же KEY обрабатывается База данных KEY гарантирует, что только один узел может быть успешно вставлен, а другие узлы не будут вставлены.

Реализация распределенной блокировки БД: блокировка осуществляется по уникальности идентификатора первичного ключа, грубо говоря, форма блокировки заключается в вставке части данных в таблицу, а идентификатор части данных является распределенной блокировкой. , Например, когда вставляется запрос на часть данных с идентификатором 1, другие параллельные запросы, которые хотят вставить данные, должны дождаться выполнения первого запроса для удаления данных с идентификатором 1, прежде чем продолжить вставку. , реализующий функцию распределенной блокировки.

Итак, идея блокировки и разблокировки очень проста, псевдокод:

def lock :
    exec sql: insert into locked—table (xxx) values (xxx)
    if result == true :
        return true
    else :
        return false

def unlock :
    exec sql: delete from lockedOrder where order_id='order_id'

Способ второй:

Использование серийного номера + метки времени для идемпотентной операции можно рассматривать как блокировку, которая не будет снята.

7. Интервьюер: Какие крупные отраслевые компании вы знаете о фреймворках распределенных замков?

Я:Пора показать широту своих познаний, эту Б надо установить

1.Google:Chubby

Chubby — это распределенная система координации, которая использует Paxos для внутренней координации Master и Replicas.

Служба блокировки Chubby используется в GFS, BigTable и других проектах, и ее основной целью является высокая надежность, а не высокая производительность.

Chubby используется в качестве грубой блокировки, например, для выбора мастера. Промежуток времени для удержания блокировки обычно составляет часы или дни, а не секунды.

Chubby предоставляет API, аналогичный файловой системе.Создание пути к файлу в Chubby является операцией блокировки. Chubby использует Delay и SequenceNumber для оптимизации механизма блокировки. Задержка гарантирует, что когда клиент ненормально снимает блокировку, Chubby по-прежнему считает, что клиент удерживает блокировку. Порядковый номер означает, что держатель блокировки запрашивает порядковый номер (включая несколько атрибутов) с сервера Chubby, а затем отправляет порядковый номер на сервер Chubby, когда необходимо использовать замок.Сервер проверяет действительность порядкового номера, включая действителен ли номер и т. д.

2. Цзиндонг ШАРКЛОК

SharkLock — это распределенная блокировка, основанная на Redis. Эксклюзивность блокировки реализуется примитивом SETNX, а принудительное снятие блокировки реализуется с помощью механизма тайм-аута и продления аренды.

3. Распределенный замок Ant Financial SOFAJRaft-RheaKV

RheaKV — это встроенная, распределенная, высокодоступная, строго согласованная библиотека классов хранения KV, основанная на SOFAJRaft и RocksDB.

RheaKV предоставляет внешний интерфейс блокировки.Чтобы оптимизировать чтение и запись данных, он обеспечивает различные характеристики блокировки в соответствии с различными типами хранилища. RheaKV предоставляет планировщик wathcdog для управления механизмом автоматического обновления блокировок, чтобы избежать снятия блокировки заранее до завершения задачи и тупика, вызванного тем, что блокировка никогда не снимается.

4.Netflix: Curator

Curator является клиентской инкапсуляцией ZooKeeper, и реализация его распределенной блокировки полностью выполняется ZooKeeper.

Создано в ZooKeeperEPHEMERAL_SEQUENTIALУзел считается заблокированным, а функция EPHEMERAL узла гарантирует, что владелец блокировки будет вынужден снять блокировку при отключении от ZooKeeper; функция SEQUENTIAL узла позволяет избежать шокового стадного эффекта при наличии большого количества замков.

Суммировать

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

Ссылаться на

Distributed locks with Redis

https://tech.meituan.com/2018/11/15/java-lock.html

свяжитесь со мной

Найдите VX [смените программистов] и ответьте «добавить группу», я втяну вас в техническую группу. Честно говоря, в этой группе, даже если вы не говорите, просто чтение записей чата — это своего рода рост.Старший инженер Ali/Tencent/Baidu, технический мастер Google, инженер IBM и мой Ван Чжуан, Есть большие коровы из всех слоев общества, и если вы что-то не понимаете, пожалуйста, задавайте вопросы в группе.

Наконец, если вы считаете, что статья Ван Чжая хороша, давайте сделаем три ссылки: подписаться, переслать, лайкнуть.