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

Блокировки являются неотъемлемой частью нашей профессии, незнание о них чревато большими проблемами. Не смотря, на то, что в интернете много информации по этому вопросу, мне показалось, что стоит посвятить блокировкам еще немно времени. Прочитав о блокировках в книге P of EAA (Мартина Фаулера), мне показалось, что информация в этой книге очень доходчива, но местами чрезмерна, поэтому я решил сократить данную информацию, дабы новичков не пугала тема блокировок.

Предисловие

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

Оптимистическая автономная блокировка (Optimistic Offline Lock)

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

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

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

Варианты реализаций

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

  1. сохранение вместе с каждой записью номера ее версии
  2. сверять значение в столбце отметки времени (с миллисекундами, напомню 1 секунда это 1000 миллисекунд). Стоит заметить, что Мартин Фаулер советует не заменять номер версии временной меткой — системные часы слишком ненадежны, особенно если речь идет о координации работы нескольких пользователей.
  3. сравнение первоначально полученных из базы данных с текущими данными в строке таблицы бд.

Важно помнить, что проверка должна осуществляться в рамках той же системной транзакции, что и сама фиксация изменений.

Пример реализации

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

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

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

Пессимистическая автономная блокировка (Pessimistic Offline Lock)

Предотвращает возникновение конфликтов между параллельными бизнес-транзакциями, предоставляя доступ к данным в конкретный момент времени только одной бизнес-транзакции (Двйвид Райе).

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

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

Возможные реализации

1. Монопольная блокировка записи (exclusive write lock), которая требует наложения блокировки только для редактирования данных. В современных бд данное поведение уже реализовано и используется по-умолчанию. Например: имеем 2 паралельных update-a одной строки, по факту оба update-a попадут в список запросов бд и будут выполнены последовательно.
Это дает возможность избежать конфликта, не позволяя двум бизнес-транзакциям одновременно вносить изменения в одну и ту же строку.

2. Монопольная блокировка чтения (exclusive read lock), используется, когда нужны самые свежие сохраненные данные. В современных бд данное поведение используется по-умолчанию. Например Вы делаете Select, но какой-то Update запрос заблокировал строку, значит Ваш Select будет ждать пока завершится Update (дефолтно бд использует блокировку на чтение данных в строке).

3. Блокировкой чтения/записи (read/write lock) - сочетает в себе первые два типа блокировки, что ведет к жесткому ограничению доступа. Если говорить о запросах в бд, то пример для MySQL будет таким:


BEGIN TRANSACTION;
SELECT * FROM table WHERE row_id = 1 FOR UPDATE;-- заблокировали строку 1: другие потоки не могут читать/изменять строку
UPDATE table SET name = 'bob' WHERE row_id = 1;
COMMIT;

Стоит заметить, что например в MySQL есть инструкция `LOCK IN SHARE MODE`:

BEGIN TRANSACTION;
SELECT * FROM table WHERE row_id = 1 LOCK IN SHARE MODE;-- заблокировали строку 1: другие потоки могут читать строку, но не могут изменять
UPDATE table SET name = 'bob' WHERE row_id = 1;
COMMIT;

Говоря проще, в момент начала работы с записью, открывают системную транзакцию и оставляют ее открытой на все время выполнения бизнес-транзакции (пока запись не будет сохранена).

Блокировка с низкой степенью детализации (Coarse-Grained Lock)

Довольно часто объекты приходится редактировать в составе группы. Предположим, у вас есть объект покупателя и множество его адресов. В этом случае при необходимости блокировки одного из элементов имеет смысл заблокировать и все остальные. Между тем что делать, если группы будут иметь более сложную структуру? Давайте для данной ситуации рассмотрим подходы озвученные выше:

1. Принцип оптимистической автономной блокировки (Optimistic Offline Lock) при большой группе объектов крайне запутывает управление блокировками.

2. Пессимистическая автономная блокировка (Pessimistic Online Lock) большой группы объектов, снижает производительность.

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

Возможный выход: блокировка с низкой степенью детализации. Она накладывает блокировку сразу на группу объектов. Это не только упрощает применение блокировки, но и освобождает от необходимости загружать все члены группы, чтобы заблокировать каждый из них.

Проколы

Любая схема блокирования будет приносить пользу только тогда, когда в ее реализации нет `проколов`. Стоит разработчику забыть вставить какую-нибудь строку кода, относящуюся к применению блокировки, и вся схема блокирования окажется совершенно
бесполезной. Применение блокировки записи вместо блокировки чтения может привести к получению устаревших данных, а неправильное использование номера версии — к нежелательной перезаписи изменений, внесенных кем-то другим.

Общее правило гласит: если элемент может быть заблокирован где-нибудь, он должен быть заблокирован везде.

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

Неявная блокировка (Implicit Lock)

Пожалуй, наиболее очевидное решение проблемы `проколов` состоит в том, чтобы не дать разработчикам совершить перечисленные ошибки. Выполнением наиболее важных процедур блокирования должны заниматься не разработчики, а само приложение. Таким образом, явное блокирование следует заменить неявным (Implicit Lock). Мартин Фаулер предлагает вынести код блокировки, выше кода работы с моделями/сущностями, в так называемую "инфраструктуру кода". Под "инфраструктурой кода" подразумевается совокупностть супертипов слоя, классов инфраструктуры и всех других конструкций, обеспечивающих жизнедеятельность приложения. М. Фаулер признает, что, все воплощения этой идеи, встречавшиеся ему на практике, оказывались довольно слабы, но в простых случаях идея неявной блокировки работает хорошо. Например: если мы решили отредактировать записть, то Маппер, который будет создавать Модель с данными из бд, может сразу применить пессимистическуя автономнуя блокировку (Pessimistic Offline Lock). От себя добавлю, что считаю плохой идеей реализовывать блокировку с низкой степенью детализации (Coarse-Grained Lock) в "инфраструктуре кода", в остальных же случаях, это может "сыграть на руку".

Источник: 1 - 2 - 3 - 4 - 5 - 6 - 7

Оцени публикацию:
  • 0,0
Оценили человек: 0

Похожие статьи:

Справочники и учебники:


Предложения и пожелания:
Ваше имя:
Ваш E-mail:
Сколько будет Οдин + Τри
Главная
X

youtube.com/watch?v=7hFivbgIEqk

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

Главная » Веб-мастеру » MySQL »