Рекомендации по многопоточной обработке записей базы данных

У меня есть один процесс, который запрашивает таблицу для записей, где PROCESS_IND = 'N', выполняет некоторую обработку, а затем обновляет PROCESS_IND до 'Y'.

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

С чего начать?


person Mike Sickler    schedule 18.02.2009    source источник
comment
может ли PROCESS_IND быть любым другим значением, например «L»?   -  person sfossen    schedule 18.02.2009
comment
Конечно, он используется только этим процессом, так что это может быть что угодно.   -  person Mike Sickler    schedule 18.02.2009
comment
Публикация размера страницы, структуры таблицы, #rows и индексов может помочь.   -  person Vincent    schedule 20.02.2009


Ответы (5)


Шаблон, который я бы использовал, выглядит следующим образом:

  • Создайте столбцы «lockedby» и «locktime», которые представляют собой идентификатор потока/процесса/машины и отметку времени соответственно (вам понадобится идентификатор машины, когда вы разделяете обработку между несколькими машинами).
  • Каждая задача будет выполнять запрос, например:

    ОБНОВЛЕНИЕ таблицы задач SETlockedby=(мой идентификатор), locktime=now() ГДЕ Lockedby IS NULL ORDER BY ID LIMIT 10

Где 10 - "размер партии".

  • Затем каждая задача выполняет SELECT, чтобы узнать, какие строки она «заблокировала» для обработки, и обрабатывает эти строки.
  • После завершения каждой строки вы устанавливаете lockby и locktime обратно в NULL.
  • Все это делается в цикле для всех партий, которые существуют.
  • Задание cron или запланированное задание периодически сбрасывает «заблокировано» для любой строки, время блокировки которой истекло слишком давно, поскольку они предположительно были выполнены задачей, которая зависла или аварийно завершилась. Кто-то другой потом их подберет

LIMIT 10 специфичен для MySQL, но другие базы данных имеют эквиваленты. ORDER BY импортируется, чтобы запрос не был недетерминированным.

person MarkR    schedule 20.02.2009
comment
+1 за очень хорошую идею. Я просто не знаю, почему ORDER BY был бы важен, если бы не было порядка, в котором должны обрабатываться строки. Если это так, то в любом случае не может быть и речи о нескольких процессах. - person mghie; 20.02.2009
comment
В Sybase вы можете использовать set rowcount {#rows | @variable} для размера пакета, эквивалентного LIMIT. - person Vincent; 20.02.2009
comment
ORDER BY важен только в том случае, если вам нужно, чтобы ваши операторы SQL были детерминированными - в MySQL это важно для воспроизведения двоичного журнала в режиме операторов, что жизненно важно для работы репликации и восстановления на момент времени. В других базах данных это менее важно. - person MarkR; 20.02.2009
comment
Вы должны прочитать свою документацию, чтобы узнать, как получить идентификатор процесса/потока. Я уверен, что это просто. - person MarkR; 25.05.2010
comment
Что происходит, когда две задачи выдают и фиксируют блокировку одновременно? Если такое поведение является критическим, вы можете захотеть, чтобы одна задача диспетчера выполняла всю маркировку для блокировок. В этот момент рабочие задачи должны беспокоиться только о том, что принадлежит мне? - person pojo-guy; 19.03.2016

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

Чтобы действительно правильно ответить на этот вопрос, потребуется больше информации о структуре таблицы и задействованных индексах, но для дальнейшего объяснения.

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

Сначала протестируйте свой подход без перемещения на APL (блокировка всех страниц «по умолчанию»), а затем, если возникнут проблемы, перейдите на DOL (сначала страница данных, а затем строка данных). Имейте в виду, что когда вы переключаете таблицу на DOL, все ответы на этой таблице становятся немного хуже, таблица занимает больше места и становится более склонной к фрагментации, что требует регулярного обслуживания.

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

person Vincent    schedule 19.02.2009

Вы должны включить row level locking в таблице с помощью:

CREATE TABLE mytable (...) LOCK DATAROWS

Затем вы:

  • Начать транзакцию
  • Выберите свою строку с опцией FOR UPDATE (которая заблокирует ее)
  • Делай что хочешь.

Никакой другой процесс не может ничего сделать с этой строкой, пока транзакция не завершится.

П. S. Некоторые упоминают о накладных расходах, которые могут возникнуть в результате использования LOCK DATAROWS.

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

Но если вы переключитесь на DATAPAGES, вы сможете заблокировать только одну строку на PAGE (по умолчанию 2k), и процессы, строки которых находятся на одной странице, не смогут выполняться одновременно.

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

Параллелизм процессов имеет гораздо большее значение для такого дизайна.

person Quassnoi    schedule 18.02.2009
comment
Мы не знаем достаточно информации, чтобы сделать вызов «подобной таблице», мы не знаем размер страницы, количество строк на странице, размер таблицы или структуры или какие другие запросы/процессы выполняются над таблицей. Я видел, как приложения доводились до их нужд, благоразумно внедряя блокировки на уровне строк. - person Vincent; 20.02.2009
comment
Конечно, мы ничего не знаем, но давайте проведем преждевременную оптимизацию на всякий случай :) - person Quassnoi; 20.02.2009
comment
Я не уверен, что ты имеешь в виду? Немедленный переход к блокировке на уровне строк не является преждевременной оптимизацией. Его следует использовать очень экономно и обычно только тогда, когда код не может быть изменен и существуют взаимоблокировки, которые нельзя разрешить другими способами. - person Vincent; 20.02.2009

Самый очевидный способ — это блокировка. Если в вашей базе данных нет блокировок, вы можете реализовать ее самостоятельно, добавив поле «Заблокировано».

Одним из способов упрощения параллелизма является рандомизация доступа к необработанным элементам, поэтому вместо конкуренции за первый элемент они распределяют доступ случайным образом.

person Osama Al-Maadeed    schedule 19.02.2009

Преобразуйте процедуру в один оператор SQL и обработайте несколько строк как один пакет. Вот как должны работать базы данных.

person David Aldridge    schedule 19.02.2009
comment
Однако существует множество процессов, которые нельзя запустить в БД. - person you786; 21.03.2016