Источник этой статьи:
https://www.cnblogs.com/jackyfei/p/12142840.html
Как гарантировать, что метод или часть кода могут выполняться только одним потоком за раз в условиях высокого параллелизма.Монолитными приложениями можно управлять с помощью API-интерфейсов, связанных с параллельной обработкой, но после того, как архитектура монолитного приложения эволюционирует в архитектуру распределенных микросервисов, Например, при развертывании процесса очевидно, что нет возможности контролировать параллелизм с помощью механизма блокировки прикладного уровня. Итак, какие типы замков существуют, для чего используются замки и каковы сценарии использования замков? Сегодня поговорим об использовании блокировок в сценариях с высокой степенью параллелизма.
Категория замка
Разные сценарии приложений предъявляют разные требования к блокировкам.Давайте сначала рассмотрим типы блокировок и различия между этими блокировками.
- Пессимистическая блокировка (синхронизация)
- Тяжеловесная блокировка синхронизируется в Java
- блокировка строки базы данных
- оптимистическая блокировка
- Облегченные блокировки volatile и CAS в Java
- Номер версии базы данных
- Распределенная блокировка (блокировка Redis)
оптимистическая блокировка
Это все равно, что сказать, что вы человек с оптимистичным и позитивным отношением к жизни, всегда думаете о лучшей ситуации, Например, каждый раз, когда вы идете за общими данными, вы думаете, что другие не изменят их, поэтому это будет не быть заблокированным.При обновлении вы будете судить, есть ли кто-либо, кто обновляет данные в этот период.
Сначала используется оптимистическая блокировка, а затем — суждение. Посмотрим на псевдокод:
1reduce()
2{
3 select total_amount from table_1
4 if(total_amount < amount ){
5 return failed.
6 }
7 //其他业务逻辑
8 update total_amount = total_amount - amount where total_amount > amount;
9}
- Номер версии базы данных относится к оптимистической блокировке;
- Алгоритмы CAS, реализованные классом, принадлежат оптимистичным блокировке.
пессимистический замок
Как вы понимаете пессимистическую блокировку? Относительно оптимистичная блокировка — это прямо противоположное, всегда предполагающее наихудший случай, предполагающий, что каждый раз, когда вы берете данные, они будут изменены другими, поэтому вы будете добавлять к ним блокировку каждый раз, когда вы делитесь данными, пока вы не используете их. Затем отпустите замок, а затем использовать данные для других.
Сначала оцениваются пессимистические замки, а затем используются. Давайте также посмотрим на псевдокод:
1reduce()
2{
3 //其他业务逻辑
4 int num = update total_amount = total_amount - amount where total_amount > amount;
5 if(num ==1 ){
6 //业务逻辑.
7 }
8}
- Синхронизация в Java — это тяжеловесная блокировка, пессимистическая блокировка;
- Блокировки строк базы данных являются пессимистичными блокировками;
Случай операции дедукции
Вот очень распространенный пример: вычет из баланса в случае высокой параллелизма, или аналогичный вычет из товарных запасов, или вычет из баланса счета операций с капиталом. Что происходит с операцией дедукции? Легко видеть, что проблема, которая может возникнуть,Перепродано из-за отчислений, то есть вычет становится отрицательным числом.
Например, у меня, например, данные инвентаризации всего 100. В случае параллелизма первый запрос продает 100 единиц, а второй пакет продает 100 юаней, что приводит к отрицательному количеству текущих запасов. Как решить эту ситуацию? Вот четыре варианта.
Сценарий 1: синхронизированная эксклюзивная блокировка
В это время легко придумать самое простое решение:синхронная эксклюзивная блокировка(синхронизировать). А вот недостатки эксклюзивных замков очевидны:
- Одним из недостатков является то, что проблемы с производительностью, вызванные сериализацией потоков, относительно велики по потреблению производительности.
- Другим недостатком является то, что он не может решить проблему межпроцессного взаимодействия в случае распределенного развертывания;
Сценарий 2: Блокировка строк базы данных
Во-вторых, мы можем подумать об использовании блокировок строк базы данных для блокировки этих данных.По сравнению с монопольными блокировками это решение решает проблему перекрестных процессов, но все же имеет недостатки.
- Одним из недостатков является проблема с производительностью, которая будет блокироваться на уровне базы данных до тех пор, пока не будет зафиксирована транзакция, которая также выполняется последовательно;
- Вторым нужно обратить внимание на установку уровня изоляции транзакции Read Committed, иначе в случае параллелизма другие транзакции не смогут увидеть отправленные данные, что все равно приведет к проблемам с перепроданностью;
- Третий недостаток — легко залить соединения с базой данных, если в транзакции есть взаимодействие со сторонним интерфейсом (есть вероятность тайм-аута), то соединение этой транзакции будет все время блокироваться, а база данных соединение будет заполнено.
- Последний недостаток заключается в том, что он склонен к перекрестной взаимоблокировке.Если управление блокировкой нескольких сервисов не является хорошим, произойдет перекрестная взаимоблокировка двух записей AB.
Вариант 3: распределенная блокировка redis
Предыдущая схема по сути использует базу данных как распределенную блокировку, так что точно так же redis и zookeeper эквивалентны своеобразной блокировке базы данных.По сути, при возникновении проблем с блокировкой сам код либо синхронизируется, либо различные блокировки. Его сложнее использовать, поэтому идея состоит в том, чтобы передать проблему согласованности обработки кода профессиональному компоненту, который может помочь вам справиться с проблемами согласованности, такими как базы данных, такие как Redis, такие как zookeeper и т. д.
Здесь мы анализируем преимущества и недостатки распределенных блокировок:
- преимущество:
- МожетИзбегайте большого количества запросов на эксклюзивные блокировки базы данных и повышайте скорость отклика системы.;
- недостаток:
- Атомарность установки блокировок и установки таймаутов;
- Недостаток отсутствия установки периода тайм-аута;
- Время простоя службы или тайм-аут блокировки потока;
- Установка тайм-аута необоснованна;
Атомарность настроек блокировки и срока действия
Команда блокировки redis setnx устанавливает время истечения срока блокировки, а команда разблокировки — del, но в версиях до 2.6.12 команды блокировки и установки срока действия блокировки представляют собой две операции, которые не являются атомарными. Если setnx установил ключ-значение и не успел использовать expire для установки времени истечения, текущий поток зависает или поток блокируется, ключ, установленный текущим потоком, всегда будет действительным, а последующие потоки не могут использовать setnx для нормального получения блокировки, что приводит к смерти Lock.
В ответ на эту проблему в версии Redis выше 2.6.12 добавлены необязательные параметры, которые могут устанавливать время истечения срока действия ключа при блокировке, обеспечивая атомарность операций блокировки и истечения срока действия.
Однако даже если проблема атомарности будет решена, в бизнесе все равно будут возникать некоторые экстремальные проблемы, например, в распределенной среде после того, как A получает блокировку, бизнес-код потока A выполняется слишком долго, что приводит к тайм-ауту. блокировки.Блокировка автоматически отключается. Последующий поток B случайно удерживает блокировку, а затем поток A снова возобновляет выполнение и снимает блокировку непосредственно с помощью команды del, тем самым ошибочно удаляя блокировку того же ключа потока B. Это распространенный сценарий, когда код занимает слишком много времени.Если в вашем коде есть вызов внешнего интерфейса связи, такой сценарий легко может возникнуть.
Установите разумную продолжительность
В случае блокировки тайм-аута нити только что упомянуто, то если вы не устанавливаете продолжительность, конечно, нет. Если сервис внезапно снижается, пока нить удерживает замок, замок никогда не будет недействительным. Существует также вопрос о том, является ли установка времени ожидания времени заблокировки. Если время настройки слишком длинное, это повлияет на производительность. Если время настройки слишком короткое, возможно, что блокировка бизнеса не обрабатывается. Это Можно установить время блокировки разумно?
замок жизни
Это очень сложная проблема для решения, но есть способ решить эту проблему, то есть поддерживающая жизнь блокировка.Мы можем сначала установить период ожидания для блокировки, затем запустить поток демона и позволить потоку демона перезапустите через определенный период времени. Установите время ожидания этой блокировки. Процесс реализации блокировки продолжения заключается в записи потока демона, а затем оценке состояния блокировки объекта. Когда он вот-вот выйдет из строя, повторно заблокируйте его. снова, но надо признать, что объект блокировки тот же.
Аналогично, после того как дело основного потока выполнено, поток демона также необходимо уничтожить, чтобы не тратить ресурсы впустую.Схема использования жизнеобеспечивающей блокировки относительно сложнее.Поэтому, если дело относительно простое, можно установить период ожидания блокировки разумно основанный на опыте и аналогии.
Вариант 4: оптимистическая блокировка базы данных
Один из принципов оптимистической блокировки базы данных состоит в том, чтобы попытаться максимально уменьшить область действия блокировки. Чем больше диапазон блокировок, тем хуже производительность.Блокировки базы данных сводят диапазон блокировок к минимуму. Давайте посмотрим на следующий псевдокод
1reduce()
2{
3 select total_amount from table_1
4 if(total_amount < amount ){
5 return failed.
6 }
7 //其他业务逻辑
8 update total_amount = total_amount - amount;
9}
Мы видим, что код перед модификацией не имеет условия where. После модификации добавьте условие, чтобы судить: общий запас больше запаса, подлежащего вычету.
1update total_amount = total_amount - amount where total_amount > amount
Если количество обновлений возвращается 0, это означает, что вычет был предотвращен другими потоками во время процесса выполнения, и удержание избегает отрицательного числа.
Но это решение также связано с проблемой: если в предыдущем коде обновления и другой бизнес-логике есть какие-то другие операции записи в базу данных, как откатить эту часть данных?
Мое предложение заключается в следующем: вы можете выбрать следующие два способа написания:
- Используйте откат транзакции, чтобы написать:
Сначала мы добавляем транзакцию в бизнес-метод, и метод выдает исключение, когда количество предметов, затронутых вычетом запасов, равно нулю, так что предыдущий бизнес-код также будет откатываться.
1reduce()
2{
3 select total_amount from table_1
4 if(total_amount < amount ){
5 return failed.
6 }
7 //其他业务逻辑
8 int num = update total_amount = total_amount - amount where total_amount > amount;
9 if(num==0) throw Exception;
10}
- Второй способ написания
1reduce()
2{
3 //其他业务逻辑
4 int num = update total_amount = total_amount - amount where total_amount > amount;
5 if(num ==1 ){
6 //业务逻辑.
7 } else{
8 throw Exception;
9 }
10}
Сначала выполните логику обновления Business, а затем выполните операцию логики, если выполнение будет успешно. Это решение является моим относительно рекомендуемым решением. Этот метод может быть использован для совместных операций вычетов ресурсов при одновременных условиях, но здесь необходимо поднять вопрос. Например, что, если бизнес в другой бизнес-логике терпит неудачу по особым причинам? Например, что мне делать, если OOM подается во время процесса вычета?
Могу только сказать, что в такие крайне экстремальные ситуации, как внезапные простои и промежуточная потеря данных, можно вмешиваться вручную только в таких редких случаях, а если учесть все экстремальные ситуации, то это нереально. Основное внимание в нашем обсуждении уделяется тому, как заблокировать работу общих ресурсов в случае параллелизма.
Суммировать
Наконец, позвольте мне подытожить для вас.Если вы можете очень умело решить такого рода проблемы, первое, что приходит вам на ум, это: решение номера версии базы данных или решение распределенной блокировки; но если вы новичок, я верю вам Синхронизация блокировку или блокировку строки базы данных, предусмотренную в Java, следует учитывать в первый раз.
Цель сегодняшнего обсуждения — поместить блокировки в этих сценариях в конкретный сценарий, а затем сравнить и проанализировать их шаг за шагом, чтобы вы могли иметь более полное и систематическое представление о тонкостях использования блокировок. Меня зовут Чжан Фейхун, и я надеюсь, что мой рассказ поможет вам.
●Примечания по синхронизации потоков
●Когда я мигрирую из архитектуры мономеров в микро?
●Куда ушли время и стоимость микросервисов?
●Навигация по обучению микросервисам
●Зачем вам нужен DDD при разработке микросервисов?
●Если бы вы были архитектором, что бы вы сделали
●Позиция микросервисного подразделения
●Построение кластера MongoDB: шардинг + реплика + выборы
●Spring Boot реализует динамическое добавление и удаление задач синхронизации запуска и остановки.
●MongoDB — Пользователи и разрешения
●Сообщество SpringForAll, 10 избранных статей 2019 г.
Эта статья опубликована в блогеOpenWriteвыпуск!