Потокобезопасность и ограничение внешнего ключа - PullRequest
0 голосов
/ 01 августа 2011

Фон

Принимая во внимание:

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

Предлагаемое решение

Класс регистра, который отслеживает идентификаторы объектов, которые были записаны. Интерфейс класса register имеет открытые методы, поэтому (представлен в Java):

public interface Register
{
    synchronized boolean requestObjectLock(int id);
    synchronized boolean saveFinalized(int id);
    synchronized boolean checkSaved(int id);
}

Метод requestObjectLock проверяет, заблокирован ли еще объект другим потоком, и возвращает ложное значение. В противном случае он блокирует этот идентификатор и возвращает true. В этом случае вызывающий поток несет ответственность за вызов saveFinalized, когда он был успешно записан в базу данных, и обязанность всех других потоков проверить, записано ли это уже с checkSaved перед записью объекта, который ссылается на это. Другими словами, существует три состояния, в которых может находиться объект: незарегистрированный, заблокированный (зарегистрированный, но незаписанный) и сохраненный (зарегистрированный и записанный).

Рассуждения

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

Вопросы

Можно ли гарантировать последовательность выполнения запросов, выполняемых разными потоками? И, следовательно, является ли это решение слишком сильным? Есть ли более простое решение? С другой стороны, это безопасно?

1 Ответ

1 голос
/ 06 августа 2011

Термины, которые необходимо исследовать на стороне базы данных, это «уровень изоляции транзакций» и «контроль параллелизма».Поддержка платформы СУБД варьируется.Некоторые платформы реализуют подмножество уровней изоляции, определенных в стандартах SQL.(Стандарты SQL позволяют это. Они написаны с точки зрения того, какое поведение недопустимо.) И разные платформы по-разному подходят к управлению параллелизмом.

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

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

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

грязное чтение : транзакция A может считывать данные, записанные параллельной незафиксированной транзакцией B.

неповторяемое чтение : транзакция A считывает данные дважды.Параллельная транзакция, B, фиксирует между двумя чтениями.Транзакция данных, прочитанная первой, отличается от данных, которые она прочитала второй, из-за транзакции B. (Некоторые люди описывают транзакцию A как «одинаковые строки, разные значения столбцов».)

фантомное чтение : транзакция A читает данные дважды.Параллельная транзакция, B, фиксирует между двумя чтениями.Два чтения транзакции A возвращают два разных набора строк, поскольку транзакция B повлияла на оценку предложения WHERE транзакции A.(Некоторые люди описывают транзакцию A как «одинаковые значения столбца, разные строки».)

Вы управляете поведением транзакции в SQL с помощью SET TRANSACTION.Таким образом, SET TRANSACTION SERIALIZABLE означает, что грязное чтение, неповторяемое чтение и фантомное чтение невозможны.SET TRANSACTION REPEATABLE READ разрешает фантомное чтение, но грязное чтение и повторение невозможно.

Вам нужно будет проверить документацию вашей платформы, чтобы узнать, что она поддерживает.Например, PostgreSQL синтаксически поддерживает все четыре уровня изоляции.Но внутренне он имеет только два уровня: чтение зафиксировано и сериализуемо.Это означает, что вы можете SET TRANSACTION READ UNCOMMITTED, но вы получите поведение «прочитано зафиксировано».

Важно для вас : эффект сериализуемого уровня изоляции состоит в том, чтобы гарантировать, что транзакции, по-видимому, имеютвыдан по одному клиенту.Но это не совсем то же самое, что сказать, что если транзакция A начинается до транзакции B, она будет зафиксирована до транзакции B. Если они не влияют друг на друга, dbms разрешается сначала зафиксировать транзакцию B, не нарушая семантику сериализуемого уровня изоляции..

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

...