У меня есть несколько рабочих, каждый из которых имеет собственное подключение к PostgreSQL. Рабочие манипулируют разными таблицами.Обработка условий гонки в PostgreSQL
Рабочие обрабатывают параллельные запросы извне системы. Одной из таблиц, к которым осуществляется доступ, является таблица пользователей. Когда приходит какая-то информация, я сначала должен убедиться, что в таблице есть запись для пользователя. Если нет записи, я хочу сначала создать ее.
Я использую следующую идиому:
if [user does not exist] then [create user]
Код [user does not exist]
является:
SELECT id FROM myschema.users WHERE userId='xyz'
и я проверить, возвращается ли какая-либо строка.
The (упрощенный) код [create user]
является:
INSERT INTO myschema.users VALUES ('xyz')
Когда моя система обрабатывает параллельные потоки различной информации, касающейся то же пользователь, я часто получаю PostgreSQL ошибки:
Key (id)=(xyz) already exists
It происходит потому, что команда SELECT
не возвращает строк, затем другой рабочий создает пользователя, любой мой рабочий пытается сделать то же самое, что приводит к примерной ошибке параллелизма.
Согласно документации PostgreSQL по умолчанию, когда я неявно запускаю транзакцию, таблица блокируется до тех пор, пока я ее не фиксирую. Я не использую autocommit, и я совершаю транзакцию только в блоках, например. после всего if-else
блок.
Действительно, я мог бы поместить в SQL-файл if-else
, но это не решает мою проблему блокировки вообще. Я предполагал, что парадигма «победит все это» будет работать, и что первый работник, которому удается выполнить команду SELECT
, будет владеть замками, пока не назовет COMMIT
.
Здесь я читал много разных тем, но я до сих пор не уверен, что такое правильное решение. Должен ли я использовать явное блокирование таблиц, потому что неявная блокировка не работает? Как я могу гарантировать, что только один рабочий владеет таблицей вовремя?
Я уверен, что это неправильное решение, но для нас работал следующий подход: 'User.transaction {User.update_all ({userId: user_id}, {userId: user_id}); User.create! (UserId: 'xyz'), если User.exists? (UserId: 'xyz')} '. Первая «фальшивая» команда обновления блокирует строку (если она существует), следующая создает новую строку (если она не существует). Мы также устанавливаем определенный уровень изоляции транзакций, насколько я помню, и мы использовали mysql, а не postgresql. Это все, что я помню. – DNNX
Взгляните на http://stackoverflow.com/questions/17267417/how-do-i-do-an-upsert-merge-insert-on-duplicate-update-in-postgresql и его ссылки. –