Атомный SELECT и ОБНОВЛЕНИЕ - PullRequest
4 голосов
/ 13 апреля 2011

Если мои данные выглядят так:

ID STATUS     DATE_ADDED
== ========== ==========
 1 Processing 2011-04-01
 2 New        2011-04-02 
 3 New        2011-04-03
 4 Processing 2011-04-03
 5 Done       2011-04-06
 6 New        2011-04-06
 7 New        2011-04-14
 8 Done       2011-04-14
 ...

... каков рекомендуемый способ выбрать 10 самых старых записей со статусом «Новый» и установить для них статус «Обработка», гарантируя, что любые другиепараллельный процесс не может сделать то же самое с теми же записями?

Это веб-приложение, работающее на PHP / 5.2.6 под Windows Server 2003, которое подключается к удаленному серверу Oracle 10g через ODBC (драйвер Oracle, а не Microsoft).

Ответы [ 5 ]

3 голосов
/ 13 апреля 2011

Это трудно сделать в Oracle 10g.В 11g синтаксис SELECT FOR UPDATE ... SKIP LOCKED делает это простым.

Простой оператор UPDATE будет сериализован.Как будет SELECT FOR UPDATE.Конечно, два конкурирующих процесса никогда не получат одинаковые строки;проблема в том, что в лучшем случае они будут сериализованы, а в худшем - заблокированы.

Рекомендуется использовать Oracle Advanced Queuing (или реализацию очередей по вашему выбору), чтобы поставить в очередь идентификаторы, которые будутобрабатываются и позволяют реализации очередей управлять конфликтом значений.

-

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

select * from my_table
 where rowid in 
       (select row_id 
          from (select rowid as row_id, rownum as rn 
                  from mytable where some_condition 
                 order by deterministic_sort_order)
         where rn between :low_rn and :hi_rn
       )
 for update nowait;

Выражение сортировки должно быть детерминированным (просто включите первичный ключ какконец выражения сортировки) для предотвращения столкновений.

2 голосов
/ 13 апреля 2011

Используйте транзакцию для этого. Использование уровня изоляции " serializable " для транзакции не позволит любому другому процессу получить доступ / изменить строки, пока ваша транзакция работает с ними.

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

Вы можете использовать:

set transaction isolation level serializable;
1 голос
/ 14 апреля 2011

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

lock your_table in exclusive mode

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

1 голос
/ 14 апреля 2011

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

create table Lock_Table (
  app_catagory  varchar2(20) primary key,
  usage_ts      timestamp(6)
 );

Вставьте одну строку: insert into Lock_Table (app_category) values 'APP1' и подтвердите. Это одноразовая вставка.

Затем, чтобы заблокировать другие сеансы: update Lock_Table set usage_ts = current_timestamp where app_category = 'CAT1'

Вам не нужен столбец use_ts, вы можете использовать select for update.

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

0 голосов
/ 10 мая 2013

Только для заметки

также, Оптимистическая блокировка Стратегия может быть применена для решения этой проблемы

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