Подтвердить что ты не робот

Оптимистическая блокировка по конкретному (Java) примеру

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

Я понимаю, что оптимистическая блокировка включает в себя добавление столбца для отслеживания "версии" записи и что этот столбец может быть меткой времени, счетчиком или любой другой конструкцией отслеживания версий. Но я до сих пор не понимаю, как это гарантирует целостность WRITE (это означает, что если несколько процессов одновременно обновляют один и тот же объект, то впоследствии объект правильно отражает истинное состояние, в котором он должен быть).

Может ли кто-нибудь предоставить пример конкретного, простого для понимания того, как оптимистическая блокировка может использоваться в Java (против, возможно, базы данных MySQL). Скажем, у нас есть объект Person:

public class Person {
    private String firstName;
    private String lastName;
    private int age;
    private Color favoriteColor;
}

И что экземпляры Person сохраняются в таблице people MySQL:

CREATE TABLE people (
    person_id PRIMARY KEY AUTO_INCREMENT,
    first_name VARCHAR(100) NOT NULL,
    last_name VARCHAR(100) NOT NULL,        # } I realize these column defs are not valid but this is just pseudo-code
    age INT NOT NULL,
    color_id FOREIGN KEY (colors) NOT NULL  # Say we also have a colors table and people has a 1:1 relationship with it
);

Теперь скажем, что есть 2 программные системы или 1 система с двумя потоками, которые пытаются одновременно обновить один и тот же объект Person:

  • Программное обеспечение /Thread # 1 пытается сохранить изменение фамилии (от "John Smith" до "John Doe" ).
  • Программное обеспечение /Thread # 2 пытается сохранить изменение в любимом цвете (от RED до GREEN)

Мои вопросы:

  • Как оптимизировать блокировку в таблицах people и/или colors? (Ищете конкретный пример DDL)
  • Как вы могли бы использовать эту оптимистичную блокировку на уровне приложения /Java? (Поиск конкретного примера кода)
  • Может ли кто-нибудь запустить меня через сценарий, в котором изменяется DDL/код (от # 1 и # 2 выше), в моем сценарии (или любом другом сценарии) и будет "оптимистично блокировать" people/colors таблицы правильно? В принципе, я ищу оптимистичную блокировку в действии, с легким для понимания объяснением того, почему она работает.
4b9b3361

Ответ 1

Обычно, когда вы смотрите на оптимистичную блокировку, вы также используете библиотеку, например Hibernate или другую реализацию JPA с @Version.

Пример может выглядеть следующим образом:

public class Person {
    private String firstName;
    private String lastName;
    private int age;
    private Color favoriteColor;
    @Version
    private Long version;
}

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

Тогда DDL может быть

CREATE TABLE people (
    person_id PRIMARY KEY AUTO_INCREMENT,
    first_name VARCHAR(100) NOT NULL,
    last_name VARCHAR(100) NOT NULL,        # } I realize these column defs are not valid but this is just pseudo-code
    age INT NOT NULL,
    color_id FOREIGN KEY (colors) NOT NULL,  # Say we also have a colors table and people has a 1:1 relationship with it
    version BIGINT NOT NULL
);

Что происходит с версией?

  • Каждый раз, прежде чем хранить объект, вы проверяете, сохранилась ли версия, хранящаяся в базе данных, той версии, которую вы знаете.
  • Если это так, сохраните свои данные с версией, увеличенной на один

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

UPDATE Person SET lastName = 'married', version=2 WHERE person_id = 42 AND version = 1;

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

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

Так что никакого столкновения:

  • Процесс A читает личность
  • Процесс A пишет Person, тем самым увеличивая версию
  • Процесс B читает Person
  • Процесс B пишет Person, тем самым увеличивая версию

Collision:

  • Процесс A читает личность
  • Процесс B читает Person
  • Процесс A пишет Person, тем самым увеличивая версию
  • Процесс B получает исключение при попытке сохранить с изменением версии после того, как Person был прочитан.

Если Color - это другой объект, вы должны поместить туда ту же схему.

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

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

Какой тип столбца использовать в качестве версии?

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

version TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP;

таким образом приложения, реализующие оптимистичную блокировку, заметят изменения в немых приложениях.

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

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

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

В качестве последнего средства вы можете использовать значение всех полей в качестве версии. Хотя это будет самый дорогой подход, в большинстве случаев это способ получить аналогичные результаты без изменения структуры таблицы. Если вы используете Hibernate, существует @OptimisticLocking -замена, чтобы обеспечить соблюдение этого поведения. Используйте @OptimisticLocking(type = OptimisticLockType.ALL) для класса сущности для отказа, если какая-либо строка изменилась с тех пор, как вы прочитали объект или @OptimisticLocking(type = OptimisticLockType.DIRTY), чтобы просто сбой, когда другой процесс также изменил поля, которые вы изменили.

Ответ 2

@TheConstructor: Отличное объяснение того, что это такое и нет. Когда вы сказали: "Оптимистичная блокировка - это не волшебство, чтобы объединить конфликтующие изменения", я хотел прокомментировать. Я использовал управление DataFlex, которое позволяло пользователям редактировать записи в форме. Когда они нажали кнопку "Сохранить", приложение будет делать так называемое " многопользовательское перечитывание" данных - вытягивание текущих значений - и сравнение с тем, что пользователь изменил. Если поля, измененные пользователем, не были изменены в то же время, тогда только эти поля будут записаны обратно в запись (которая была заблокирована только во время операции повторного чтения + записи), и поэтому 2 пользователя могли прозрачно изменять разные поля на та же самая запись без проблем. Он не нуждался в марках версий, просто знание о том, какие поля были изменены.

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