Одно хранилище данных. Несколько процессов. Будет ли этот SQL предотвращать условия гонки? - PullRequest
2 голосов
/ 09 сентября 2011

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

Я не выбралхранилища данных пока нет, но я склоняюсь к PostgreSQL просто потому, что это то, к чему я привык.Я видел следующий фрагмент SQL, предложенный как способ избежать состояния гонки, потому что предложение UPDATE предположительно блокирует строку таблицы до того, как произойдет SELECT:

UPDATE jobs
SET status = 'processed'
WHERE id = (
    SELECT id FROM jobs WHERE status = 'pending' LIMIT 1
) RETURNING id, data_to_process;

Но будет ли это действительно работать?Не кажется интуитивно понятным, что Postgres (или любая другая база данных) могли заблокировать строку таблицы перед выполнением SELECT, поскольку необходимо выполнить SELECT, чтобы определить, какую строку таблицы необходимо заблокировать для обновления.Другими словами, меня беспокоит, что этот фрагмент SQL не помешает двум отдельным процессам выбирать и работать в одной строке таблицы.

Я параноик?И есть ли лучшие варианты, чем традиционные РСУБД, для обработки ситуаций параллелизма, подобных этой?

Ответы [ 4 ]

2 голосов
/ 09 сентября 2011

Как вы сказали, используйте очередь.Стандартное решение для этого в PostgreSQL - PgQ .У него есть все эти проблемы параллелизма, разработанные для вас.

0 голосов
/ 25 сентября 2012

Это довольно близко к подходу, который я использовал, когда писал pg_message_queue, простую реализацию очереди для PostgreSQL.В отличие от PgQ, для его использования не требуется никаких компонентов вне PostgreSQL.

Он будет работать просто отлично.MVCC придет на помощь.

0 голосов
/ 09 сентября 2011

Ситуация, которую вы описываете, называется «неповторяемое чтение». Есть два способа решить это.

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

SET TRANSACTION ISOLATION LEVEL REPEATABLE READ

В начале транзакции. Я не могу найти документацию, которая объясняет идиоматический способ сделать это для ruby; вам, возможно, придется испустить этот sql явно.

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

0 голосов
/ 09 сентября 2011

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...