Большинство сред баз данных (Hibernate в Java, ActiveRecord и т. Д. В Ruby) имеют форму оптимистической блокировки . Это означает, что вы выполняете каждую операцию в предположении, что она будет работать без конфликтов. В особом случае, когда возникает конфликт, вы проверяете это атомарно в момент выполнения операции с базой данных, генерируете исключение или код возврата ошибки и повторяете операцию в своем клиентском коде после запроса и т. Д.
Обычно это выполняется с использованием номера версии в каждой записи. Когда операция с базой данных выполнена, строка считывается (включая номер версии), код клиента обновляет данные, а затем сохраняет их обратно в базу данных с предложением where
, в котором указывается идентификатор первичного ключа И номер версии совпадает. как это было, когда это было прочитано. Если это отличается - это означает, что другой процесс обновил строку, и операция должна быть повторена. Обычно это означает перечитывание записи и повторное выполнение этой операции с новыми данными из другого процесса.
В случае добавления вам также потребуется уникальный индекс для таблицы, поэтому база данных отклоняет операцию, и вы можете обработать это в том же коде.
Псевдокод будет выглядеть примерно так:
do {
read row from database
if no row {
result_code = insert new row with data
} else {
result_code = update row with data
}
} while result_code != conflict_code
Преимущество этого в том, что вам не требуется сложная синхронизация / блокировка в клиентском коде - каждый поток просто выполняется изолированно и использует базу данных в качестве проверки согласованности (что очень быстро и хорошо). Поскольку вы не блокируете какой-либо общий ресурс для каждой операции, код может выполняться намного быстрее.
Это также означает, что вы можете запускать несколько отдельных процессов операционной системы для распределения нагрузки и / или масштабирования операции на нескольких серверах, а также без каких-либо изменений кода для обработки конфликтов.