предисловие
В последнее время широко упоминаются распределенные проблемы, такие как распределенные транзакции, распределенные фреймворки, ZooKeeper, SpringCloud и т. д. В этой статье сначала рассматривается концепция блокировок, затем рассказывается о распределенных блокировках и о том, как использовать Redis для реализации распределенных блокировок.Заметки об исследовании Redis, далее идет обмен знаниями, связанными с технологиями.
Во-первых, основное понимание блокировки
Во-первых, рассмотрим концепцию замков в нашем рабочем исследовании.
Зачем говорить сначала о блокировках, а потом о распределенных?
Все мы знаем, что роль блокировок заключается в решении проблемы безопасности потоков, вызванной многопоточным доступом к общим ресурсам, но случаев, когда блокировки используются в повседневной жизни, немного. сначала блокировки, а затем вводить распределенные блокировки в глубину.
Через небольшой кейс по продаже билетов
Например, если вы пойдете за билетами дота2 ти9, что произойдет, если вы их не заблокируете? На данный момент код выглядит следующим образом:
Анализ кода:
Вот восемь билетов ti9, настроено 10 потоков (то есть аналоговых 10 человек) для одновременного захвата голосов, если успешный захват показывает успех, захват не отображает отказ. Само собой разумеется, что должно быть 8 человек, чтобы добиться успеха, два человека соревнуются за провал, следующий взгляд на результаты:
Мы обнаружили, что текущие результаты не соответствовали ожидаемой ситуации, и 10 человек действительно купили билеты, то есть возникла проблема безопасности потоков, так что же ее вызвало?
Причина в разнице во времени между несколькими потоками.
Как показано на рисунке, остался только один билет, но оставшееся количество билетов, прочитанных обоими потоками, равно 1, что означает, что поток B успешно захватил билет до того, как поток A изменит инвентарь.
Как это решить? Как всем известно, достаточно добавить ключевое слово synchronized, когда один поток выполняет метод сокращения, другие потоки блокируются в очереди ожидания, чтобы не было конкуренции между несколькими потоками за общие переменные.
Например
Например, когда мы идем в спортзал, чтобы тренироваться, если многие люди используют один и тот же тренажер и одновременно бегают на одной и той же беговой дорожке, возникнет большая проблема, и все будут упорно бороться. Если мы добавим замок в дверь спортзала, только те, у кого есть ключ от замка, смогут войти на тренировку, а остальные будут ждать за дверью, чтобы избежать конкуренции за фитнес-оборудование. код показывает, как показано ниже:
результат операции:
И действительно, в итоге два человека не смогли достать билеты, похоже, наша цель достигнута.
Во-вторых, оптимизация производительности блокировки
2.1 Сокращение времени удержания замка
На самом деле, согласно нашему пониманию повседневной жизни, невозможно, чтобы только один человек тренировался во всем спортзале. Так что нам нужно заблокировать только определенную машину, например, один человек бегает, а другой человек может заниматься другими видами спорта.
Для тикетной системы нам нужно заблокировать только код операции модификации инвентаря, а другие коды еще можно выполнять параллельно, что значительно сократит время удержания блокировки.Код модифицируется следующим образом:
Целью этого является полное использование ресурсов процессора и повышение эффективности выполнения кода.
Здесь мы печатаем время двумя способами:
Разумеется, блокировка только части кода значительно повысит эффективность выполнения кода.
Поэтому после решения проблемы потокобезопасности нам также необходимо учитывать эффективность выполнения кода после блокировки.
2.2 Уменьшите детализацию блокировок
Например, есть два фильма, недавно вышедшие Devil Boy Nezha и Spider-Man.Мы моделируем процесс оплаты покупок, даем методу подождать и добавляем метод ожидания CountDownLatch.Результаты следующие:
Результаты:
Осталось голосов за Нежа: 20
Мы обнаружили, что время покупки билетов, заблокированных повстанцами, влияет на покупку билетов Человека-паука, на самом деле они не зависят друг от друга между двумя фильмами, поэтому нам нужно уменьшить размер блокировки, чтобы блокировать фильм, весь объект становится двумя глобальными переменными блокировки. , измените код следующим образом:
Результаты:
Осталось голосов за Нежа: 20 Оставшиеся голоса Человека-паука: 100
Теперь, когда покупка билетов на два фильма не будет влиять друг на друга, это второй способ оптимизировать блокировку: уменьшить детализацию блокировки. Между прочим, ConcurrentHashMap в параллельном пакете Java должен превратить большую блокировку в 16 маленьких блокировок и обеспечить эффективную защиту параллелизма за счет сегментированных блокировок.
2.3 Разделение замка
Разделение блокировок часто называют разделением чтения и записи. Мы делим блокировки на блокировки чтения и блокировки записи. Блокировки чтения не нужно блокировать, а блокировки записи необходимо учитывать проблемы параллелизма.
Три, тип замка
- Честная блокировка: ReentrantLock
- Несправедливые блокировки: Synchronized, ReentrantLock, cas
- Пессимистическая блокировка: синхронизирована
- оптимистическая блокировка: cas
- Эксклюзивная блокировка: Synchronized, ReentrantLock
- Общий замок: семафор
Здесь не описывается концепция каждого типа замков, вы можете изучить ее самостоятельно, а также замки можно классифицировать по навесным замкам, облегченным замкам и тяжеловесным замкам.
4. Распределенная блокировка Redis
После понимания основных концепций блокировок и оптимизации блокировок сосредоточьтесь на концепции распределенных блокировок.
На приведенном выше рисунке показана распределенная среда, которую мы создали.Есть три элемента покупки билетов, соответствующие одному инвентарю, и каждая система будет иметь несколько потоков.Как и выше, операция изменения инвентаря заблокирована, можем ли мы гарантировать эти 6 Что? про потокобезопасность нити?
Конечно, нет, потому что каждая система тикетов имеет свой собственный процесс JVM, который не зависит друг от друга, поэтому добавление синхронизированного может обеспечить только потокобезопасность одной системы, но не распределенную потокобезопасность.
Следовательно, для решения этой проблемы необходимо промежуточное ПО, общее для трех систем.
Здесь мы выбираем Redis в качестве распределенной блокировки. Несколько систем устанавливают один и тот же ключ в Redis. Только когда ключ не существует, ключ может быть успешно установлен, и ключ будет соответствовать уникальному идентификатору одной из систем. Когда система получает доступ к ресурсам, система завершает работу.После этого удалите ключ для достижения цели снятия блокировки.
4.1 На что следует обратить внимание в распределенных блокировках
1) Взаимное исключение
Только один клиент может получить блокировку в любой момент времени.
Это легко понять, все системы могут иметь только одну систему, удерживающую замок.
2) Анти-тупик
Если клиент аварийно завершает работу, удерживая блокировку, и не снимает блокировку, то другие клиенты не могут получить блокировку, что приведет к тупиковой ситуации, поэтому необходимо убедиться, что клиент снимет блокировку.
В Redis мы можем установить время истечения срока действия блокировки, чтобы гарантировать отсутствие взаимоблокировок.
3) Держатель замка разблокируется
Звонок должен быть подключен, чтобы разблокировать звонок Блокировка и разблокировка должны выполняться одним и тем же клиентом Блокировка, добавленная потоком клиента А, должна быть разблокирована потоком клиента А Клиент не может разблокировать замки других клиентов .
4) реентерабельный
После того, как клиент получает блокировку объекта, клиент может снова получить блокировку объекта.
4.2 Процесс распределенной блокировки Redis
Конкретный процесс распределенной блокировки Redis:
1) Во-первых, используйте природу кеша Redis, чтобы установить пару ключ-значение в Redis в виде ключ-значение.Ключом является имя блокировки, а затем несколько потоков клиента конкурируют за блокировку.Если конкурс прошел успешно, установите значение в качестве уникального идентификатора клиента.Круг изучения Java: 14 201 9 080
2) Клиент, конкурирующий за замок, должен делать две вещи:
- Установите эффективное время блокировки, чтобы предотвратить взаимоблокировку (очень важно)
Необходимо определить продолжительность периода действия в соответствии с потребностями бизнеса и постоянным стресс-тестированием.
- Выделите уникальный идентификатор клиента, цель которого - убедиться, что держатель замка разблокирован (очень важно)
Таким образом, значение здесь задается уникальным идентификатором (например, uuid).
3) Доступ к общим ресурсам
4) Снятие блокировки.Существует два способа снять блокировку.Первый-это автоматическое снятие блокировки по истечении срока действия.Второй-сначала судить имеете ли вы право снимать блокировку по уникальному ID, и снимите блокировку, если идентификатор правильный.
4.3 Блокировка и разблокировка
4.3.1 Блокировка
1) блокировка команды setnx
установить, если не существует Мы будем использовать команду Redis setnx, Смысл setnx в том, что настройка будет успешной, только если блокировка не существует.
2) Установите время действия блокировки, чтобы предотвратить истечение срока блокировки.
Для блокировки требуется два шага.Подумайте, в чем будет проблема?
Что, если клиент внезапно зависнет после того, как мы его заблокируем? Тогда блокировка станет блокировкой без срока действия, и тогда может возникнуть взаимоблокировка. Хотя вероятность того, что это произойдет, очень мала, как только проблема возникнет, она будет очень серьезной, поэтому нам также необходимо объединить эти два шага в один.
К счастью, Redis 3.0 объединил эти две инструкции в новую инструкцию.
Посмотрите исходный код в официальной документации jedis:
Это то, что мы хотим!
4.3.2 Разблокировать
- Проверьте, держите ли вы блокировку сами (судя по уникальному идентификатору);
- Снимите блокировку.
Разблокировка тоже два шага, и атомарность разблокировки тоже должна быть гарантирована, и два шага объединены в один.
Этого нельзя достичь с помощью Redis, и это можно сделать только с помощью скриптов Lua.
Это сценарий Lua, который определяет, удерживает ли он блокировку и освобождает ее.
Почему Lua-скрипты атомарны? Поскольку Lua-скрипт выполняется джедаями с помощью функции eval(), если он будет выполнен, то все будет выполнено.
В-пятых, реализация распределенного кода блокировки Redis.
- Используйте глобальную переменную контекста для записи uuid человека, удерживающего замок.При разблокировке вам необходимо передать uuid в качестве параметра сценарию Lua, чтобы определить, можно ли его разблокировать.
- Чтобы записать текущий поток для достижения повторного входа распределенных блокировок, если текущий поток удерживает блокировку, это также является успешной блокировкой.
- Используйте функцию eval для выполнения скрипта Lua, чтобы обеспечить атомарность при разблокировке.
6. Сравнение распределенных замков
6.1 Распределенные блокировки на основе базы данных
1) Метод реализации
Вставьте часть данных при получении блокировки и удалите данные при разблокировке.
2) Недостатки
- Если база данных зависнет, бизнес-система будет недоступна.
- Невозможно установить время истечения срока действия, это приведет к взаимоблокировке.
6.2 Распределенная блокировка на основе Zookeeper
1) Метод реализации
Создайте новый узел в каталоге указанного узла при блокировке и удалите этот временный узел при снятии блокировки. Из-за наличия обнаружения сердцебиения нет тупиковой ситуации, и это более безопасно.
2) Недостатки
Производительность средняя, не такая эффективная, как у Redis.
так:
- С точки зрения производительности: Redis > zookeeper > база данных.
- С точки зрения надежности (безопасности): zookeeper > Redis > Database
7. Резюме
Начиная с базовой концепции блокировок, в этой статье предлагается проблема безопасности потоков, которая возникает при многопоточном доступе к общим ресурсам, а затем решается проблема безопасности потоков путем добавления блокировок.Этот метод снизит производительность, и необходимо сократить время удержания блокировки. , уменьшить степень детализации блокировки и разделить блокировки для оптимизации блокировки. Сегодняшнее распределение благосостояния: отсортированная частьВопросы интервью Redis.
После этого вводятся четыре характеристики распределенных блокировок:
- взаимная исключительность
- Антитупик
- заблокировать человека разблокировать
- повторный вход
Затем Redis используется для реализации распределенных блокировок, команды Redis используются для блокировки при блокировке, а Lua-скрипты используются для обеспечения атомарности при разблокировке.
Наконец, сравниваются преимущества и недостатки, а также сценарии использования трех распределенных блокировок.
Я надеюсь, что у всех появилось новое понимание распределенных блокировок, и я надеюсь, что все будут больше думать о проблемах с производительностью, думая о решении проблем.