Практическое применение оптимистической блокировки и пессимистической блокировки

Java задняя часть база данных SQL

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

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

Это отражается в базе данных (упрощенная версия).Когда человек делает ставку, некоторые поля таблицы гандикапов базы данных будут изменены: коэффициенты команды А, сумма ставки команды А, коэффициенты команды Б, сумма ставки команды Б и так далее.

Если вы используете режим транзакций по умолчанию, добавьте аннотацию @Transactional, что вызовет проблему потери обновлений. (Что такое потерянное обновление: то есть обновление одной транзакции покрывает результаты обновления других транзакций. Например, данные, считанные А, представляют собой сумму ставки 1000, и если он рассчитан, данные, считанные В, равны также 1000. A Затем записывает в базу данных вычисленные 1200. Наконец, B записывает в базу данных рассчитанные 1100. Окончательная сумма ставки в таблице составляет всего 1100, и есть потерянное обновление). Если возникает ситуация с высоким параллелизмом и каждую секунду делают ставки десятки или сотни людей, эту проблему необходимо решить.

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

1. Пессимистическое решение блокировки

Ключевой момент: блокировка строки (также называемая блокировкой записи, блокировкой X) добавляется каждый раз при чтении данных, и транзакция освобождается после модификации.

Примечание. Когда mysql использует механизм InonDB, блокировки строк будут добавляться по умолчанию при добавлении, удалении и изменении. Чтение без блокировки строк.

  • Реализация выглядит следующим образом (добавьте для обновления в конце инструкции select):
# 第一步 查的时候加行锁 (注意:InnoDB只有通过索引条件检索数据才使用行级锁,否则,InnoDB将使用表锁, 也就是说,InnoDB的行锁是基于索引的!)
SELECT * FROM table_name WHERE xxx FOR UPDATE;
# 第二步 逻辑处理完后更新数据
UPDATE xxx...
@Query(value = "SELECT * FROM guessing_handicap WHERE handicap_id = ?1 FOR UPDATE", nativeQuery = true)
GuessingHandicap getBet(Integer id);

Это очень просто реализовать, а понимание концепции написано позже.

2. Оптимистичное решение для блокировки

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

  • Механизм номера версии: добавьте поле версии номера версии данных в таблицу данных, которое указывает, сколько раз данные были изменены.При изменении данных значение версии увеличится на 1. При чтении данных также считывается значение версии. При отправке обновления, если только что прочитанное значение версии равно значению версии в текущей базе данных, оно будет обновлено. В противном случае операция обновления будет повторяться до тех пор, пока обновление не будет выполнено. успешно.
int count = 0; // 计数重复次数,暂定10次
while (count < 10) {
    count++;

    // 先读取数据,保存版本号
    GuessingHandicap handicap = guessingHandicapDao.getBet(id);
    Integer version = handicap.getVersion();
    // 进行数据的处理
    // ...

    // 将处理完的结果写回数据库
    Integer rows = guessingHandicapDao.updateHandicap(...);
    if (rows == 0) {
        continue;
    }
    // ...
}
throw new ValidationException("下注失败");
  • Алгоритм CAS: compare and swap (сравнение и обмен) — известный алгоритм без блокировки.

Понятие CAS немного сложное.Вот простая реализация:это та же таблица гандикапов.Когда я читаю данные,я читаю сумму ставки и коэффициенты рекорда,и временно сохраняю данные. Когда логика обработана и записана обратно, обновите ххх, установите шансы = новые шансы, где шансы = исходные шансы

- CAS算法也有缺点,最明显且容易理解的就是,会导致 ABA 问题。

- 如果一个变量V初次读取的时候是A值,并且在准备赋值的时候检查到它仍然是A值,
那我们就能说明它的值没有被其他线程修改过了吗?很明显是不能的,因为在这段时间它的值可能被改为其他值,
然后又改回A,那CAS操作就会误认为它从来没有被修改过。这个问题被称为CAS操作的 "ABA"问题。

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

Два сценария использования блокировки

Из введения двух типов блокировок выше мы знаем, что два типа блокировок имеют свои преимущества и недостатки, и нельзя считать, что один лучше другого.Например, оптимистичные блокировки подходят для меньшего количества операций записи. (сценарии многократного чтения), то есть конфликты.Когда это действительно случается редко, это может сэкономить накладные расходы на блокировки и увеличить общую пропускную способность системы. Однако, если это ситуация с множественной записью, обычно возникают конфликты, из-за которых приложение верхнего уровня будет продолжать повторять попытки, что снизит производительность. сценарии.

Запомните вывод, а именно: оптимистическая блокировка подходит для меньшего количества записей (сценариев многократного чтения), пессимистическая блокировка подходит для сценариев многократной записи.

3. Что такое пессимистическая блокировка и оптимистическая блокировка (понимание концепции)

Оптимистичный замок соответствует оптимистичному человеку, который всегда думает, что все будет развиваться в хорошем направлении, а пессимистический замок соответствует пессимистическому человеку, который всегда думает, что все будет развиваться в плохом направлении. У этих двух типов людей есть свои преимущества и недостатки, и нельзя не сказать, что один лучше другого в зависимости от ситуации.

пессимистический замок

Всегда предполагайте наихудший случай.Каждый раз, когда вы идете за данными, вы думаете, что другие изменят их, поэтому каждый раз, когда вы получаете данные, вы блокируете их, так что другие, которые хотят получить данные, будут блокироваться до тех пор, пока они не будут получает блокировку (общую). Ресурсы используются только одним потоком за раз, другие потоки блокируются, а ресурсы передаются другим потокам после их использования). Многие такие механизмы блокировки используются в традиционных реляционных базах данных, например блокировки строк, таблиц и т. д., блокировки чтения, записи и т. д., которые блокируются перед выполнением операций. Эксклюзивные блокировки, такие как synchronized и ReentrantLock в Java, являются реализацией идеи пессимистической блокировки.

оптимистическая блокировка

Всегда предполагайте наилучшую ситуацию. Каждый раз, когда вы идете за данными, вы думаете, что другие не изменят их, поэтому они не будут заблокированы. Вы можете использовать механизм номера версии и реализацию алгоритма CAS. Оптимистическая блокировка подходит для типов приложений с множественным чтением, что может повысить пропускную способность.Как и механизм write_condition, предоставляемый базой данных, на самом деле это оптимистическая блокировка. Класс атомарной переменной в пакете java.util.concurrent.atomic в Java реализован с использованием CAS, реализации оптимистической блокировки.

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

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

  • Блокировки таблицы делятся на блокировки чтения таблицы и блокировки записи таблицы.

  • Блокировка строки делится наобщий замокиэксклюзивный замок, а разделяемые блокировки и исключения имеют другие псевдонимы, которые на самом деле являются просто другими именами.

    • общий замок--блокировка чтения--S-образный замок
    • эксклюзивный замок--блокировка записи--Х замок
  • Чтобы разрешить сосуществование блокировок строк и таблиц, реализуйте механизм блокировок с множественной грануляцией., InnoDB также имеет два внутреннихблокировка намерения(блокировка намерения), обе эти блокировки намеренияблокировка стола:

    • Преднамеренная общая блокировка (IS): транзакция намеревается добавить общую блокировку строки к строке данных.Транзакция должна сначала получить блокировку IS таблицы, прежде чем добавлять общую блокировку к строке данных.
    • Преднамеренная монопольная блокировка (IX): транзакция намеревается добавить монопольную блокировку строки к строке данных.Транзакция должна получить блокировку IX таблицы, прежде чем добавлять монопольную блокировку к строке данных.
    • После тщательного рассмотрения концепция становится совершенно ясной, и блокировка намерения также неявно выполняется базой данных за нас, и нам не нужно об этом заботиться!

В-четвертых, понимание транзакций только для чтения

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

@Transactional(readOnly = true)

Примечания для транзакций только для чтения:

  • В транзакции только для чтения содержимое не может быть добавлено, изменено или удалено, в противном случае сообщается оператор Cannot execute в транзакции ТОЛЬКО ДЛЯ ЧТЕНИЯ.
  • В транзакции только для чтения может быть прочитано только содержимое до момента времени выполнения, а содержимое, измененное в течение периода, не может быть прочитано.
  • Транзакции только для чтения используются в качестве секретного сигнала для платформы ORM, чтобы оптимизировать выполнение, например отказаться от блокировки или никогда не очищать.
  • Транзакции только для чтения также имеют недостатки: использование транзакций будет динамически генерировать прокси-классы, увеличивая накладные расходы.

Краткое описание сценария приложения:

  1. Если за раз выполняется один оператор запроса, нет необходимости включать поддержку транзакций, и база данных по умолчанию поддерживает согласованность чтения во время выполнения SQL;
  2. Если одновременно выполняется несколько операторов запроса, таких как статистический запрос и запрос отчета, в этом сценарии множественный SQL-запрос должен обеспечивать общую согласованность чтения.В противном случае после предыдущего SQL-запроса и до следующего SQL-запроса данные будут при смене пользователя общий статистический запрос будет иметь противоречивые данные чтения. В это время должна быть включена поддержка транзакций.
  3. Если вы хотите красиво написать код, вы можете добавить транзакции только для чтения в соответствии с первыми двумя пунктами.Если вы считаете это хлопотным, вы также можете добавить ко всем им комментарии (прикрой свое лицо.jpg)

Лунпэн
Инженер-разработчик Reed Technology Java

Reed Technology-Guangzhou Professional Software Outsourcing Service Company

Предоставить апплет WeChat, исследования и разработки приложений APP, дизайн пользовательского интерфейса и другие профессиональные услуги, уделяя особое внимание консультированию по интернет-продуктам, дизайну бренда, исследованиям и разработкам в области технологий и другим областям,

Посетите www.talkmoney.cn, чтобы узнать больше

Универсальное руководство | Утренний дневник Lite | Bump Wallpaper | Yancai