Операция очереди с приоритетом SQL - какие-нибудь подводные камни? - PullRequest
1 голос
/ 13 января 2012

У меня есть таблица очередей с приоритетами, которая может быть упрощенно представлена ​​как:

CREATE TABLE test (
  id int PRIMARY KEY,
  priority int,
  status int
)

, где priority - это приоритет (самый низкий вначале), а status - это состояние элемента (0 означает «ОК» для получения). Задача состоит в том, чтобы получить элемент с наивысшим приоритетом и сбросить его статус, поэтому код выглядит так (псевдокод):

 try = 0;
 while(try++ < max_tries) {
   result = query("SELECT id FROM test WHERE status=0 ORDER BY priority LIMIT 0,1");
   if(result not empty) {
      /* found a row, try to update */
      result2 = query("UPDATE test SET status=1 WHERE id={result[id]} AND status=0");
      if(affected_rows(result2) > 0) {
         /* if update worked fine, we can use this ID */
         return result[id];
      }
      /* otherwise try again */
 }
 return NULL;

Этот код должен запускаться (с соответствующими изменениями, например, с другим синтаксисом LIMIT) в любой базе данных sql (текущее требование - Mysql, Oracle, SQL Server и DB2, но может быть и больше). Насколько я знаю, все необходимые БД поддерживают API "поврежденные строки" для обновлений. Любые потенциальные проблемы или подводные камни с этим подходом? Следует ли использовать SELECT ... FOR UPDATE в приведенном выше утверждении, и если да, то почему?

1 Ответ

1 голос
/ 13 января 2012

Кажется, что указанная комбинация SELECT / UPDATE решает проблему параллелизма с предложением UPDATE WHERE.Однако я бы склонялся к хранимой процедуре, если бы это было приемлемо: в зависимости от разновидности sql, хранимый процесс мог бы избавиться от проблемы параллелизма с атомарным «UPDATE ... RETURNING id INTO ...».

Относительно состояний, если они что-то вроде:

  • 0 "можно получить"
  • 1 "в процессе"
  • 2 "завершено"

тогда состояние действует как замок.Может возникнуть проблема, если процесс прекратит работу после изменения состояния с 0 на 1, но до того, как задание будет фактически обработано: запись может оставаться заблокированной и необработанной до запуска задания очистки.Другой подход заключается в использовании отдельного столбца метки времени для блокировки.Затем запрос включает фильтр «WHERE now () - timeout> NVL (lockTimestamp, начало времени)», и блокировка выполняется установкой lockTimestamp в now () вместо того, чтобы полагаться на установку status = 1.Таким образом, блокировка может автоматически истечь после разумного ожидания (тайм-аута), и элемент снова становится доступным для получения следующим процессором.Работы по очистке не требуются.

Есть ли вероятность голодания, когда работа с низким приоритетом может быть не подобрана?

Нужно ли добавлять дополнительный заказ?Если все элементы имеют одинаковый приоритет, имеет ли значение, что обрабатывается первым (fifo, lifo)?

Полагаю, следует отметить, что если запущено несколько экземпляров этой процедуры выборки, то несколько очередейэлементы могут обрабатываться одновременно, и порядок обработки не обязательно является последовательным (process1 получает item1, process2 получает item2, process2 завершает item2, process1 завершает item1, ...), так что, надеюсь, это нормально, в противном случае блокировка должна проверить, что ничегоеще в процессе.

Я также не вижу особой необходимости в SELECT ... ДЛЯ ОБНОВЛЕНИЯ.

...