Комментарии 42
Приветствую. Хорошая задача подобрана под ответ. Имхо, лучше оформить "##SELECT * FROM table FOR UPDATE##" без решеток - потребовалось несколько секунд чтобы разобраться, что два начальных не часть SELECT.
А не могли бы Вы рассказать, хотя бы в общих чертах, что за проект?
Часть ERP системы: конструктор «событий», то бишь администратор портала может сам собирать сложные логические «блоки», которые прорадабываются в случае разные событий. Банальный пример — пользователь системы создаёт в CRM модуле новый контакт с набором характеристик. Админ в «конструкторе событий» описывает логику, где проверяются харакретистики и выполняются разные действия, например: спросить подтверждения у менеджера группы, отправить мейл тому-то, присобачить контакт к такой-то компании, создать где-то какую-то линию действий для этого контакта и тд и тп. В итоге получаются довольно сложные схемы. Если учесть что «событий» в даже очень простой ERP системе может генерироваться сотни в минуту, задача по отработке логики оных нетривиальна. Но как показывает практика — нет невозможных задачь ;)
Последний запрос, в котором и должен быть волшебный FOR UPDATE, его собственно не содержит :)
как это не содержит, а как же:
select id from queue where locked = 'false' and execute_at FOR UPDATE
как это не содержит, а как же:
select id from queue where locked = 'false' and execute_at < NOW() LIMIT 1 FOR UPDATE
Салют clops =) а innodDB тюнили как Петя Зайцев учит ???
ну там нереальный InnoDB_pool_size и т.п.???
с самой инной проблем не ощущаем, кстати... подкрутили конечно несколько болтиков как в мане рекомендуют, но особых танцев с бубном пока не было )
блин... у меня primary key - char(36) - InnoDB в таких раскладах ВООБЩЕ не проканает =) ибо там внутре записи так распологаются, а вводить id автоинкрементный везде, пока не получается
execute_at (datetime), executed_at (datetime)

эх, сдается мне, уже было или еще будет проведен не один час в поисках бага. Тем более, что буквы E и D находятся рядом ;)

По теме:
а у вас INSERT в таблицу "queue" не лочится, пока вы пережевываете записи, полученные SELECT ... FOR UPDATE?
У нас вот стояла похожая задача, мы сделали так (местами синтаксис PHP), грубый пример:
0) генерируем какой-нибудь уникальный по времени и по демону $pid;
$pid = $server_uid . ":" . microtime(true);
1) и подминаем под него интересующие нас записи:
db_query("UPDATE tbl SET process_id='$pid', in_process=1 WHERE event_id='$eid'");
2) потом не спеша эти записи обрабатываем:
db_query("BEGIN");
$result = db_query("SELECT * FROM tbl WHERE process_id='$pid' AND in_process=1");
while($row=db_fetch($result){db_query("UPDATE tbl SET value='blablabla' WHERE id='$row->id';");}
db_query("COMMIT;");
полность согласен:
1 - занимаем записи
1.а - проверяем, сколько строк затронул апдейт.
2. - если затронутые ряды есть - не спеша эти записи обрабатываем:

неплохобы еще и организовать демон, которые периодически проверяет - а есть ли записи, залоченные за процесс, но выполняющиеся долго - процесс то мог и вывалиться во время работы.
А всё — элементарно

Столько буков ради того, чтобы сказать миру, что Вы наконец-то удосужились прочесть мануал?
Мать моя женщина! Мускулистые парни - это все делал фокспро на автомате - достаточно было выставить один флаг
Спасибо! Как раз на этой неделе появилась задача перелопачивать большую очередь заданий десятками демонов - тоже никак не мог придумать как метить взятые на обработку...

Чума нашего времени. Айтишные знания очень глубокие. Есть нетривиальная задача и кто-то придумал ей красивое решение, назовем его "for update", об этом решении знает 1 из 100. 49 спецов потратят несколько часов на чтение мануалов и гугление с негарантированным результатом, еще 50 будут три дня писать incredible machine делающую аналог "for update". И только один готов за полсекунды ответить - "юзайте "for update". Вопрос - как за кратчайшее время найти этого одного?
Найдите один раз одного знакомого, который прошёл нормальный курс подготовки по базам данных, только и всего - тот, где среди прочего рассматриваются транзакции, блокировки и способы их реализации в основных СУБД - и использует эти знания на деле. Если совсем лень - запостите в разделе "Проектирование" на форуме SQL.ru.

Если вы сами разработчик - то лучше пройти такой курс самостоятельно.
Не мысли плоско... Таких задач сотни и направлений в айти - не меньше. Искать сотни супергуру себе в знакомые - жизни не хватит.
Задач может и десятки тысяч, а вот направлений - не соглашусь. Базы данных - это один из десятка-двух основных курсов по IT.

Какие супергуру? Это элементарная задача для рядового промышленного разработчика БД.
Реализация транзакций и блокировок в каждой СУБД своя поэтому в любом случае потребуется или время на поиск рещения в документации или на поиск человек который уже работал с данной СУБД.
цитриую себя:
...способы их реализации в основных СУБД...

на SQL.ru все эти СУБД представлены, к тому же отличий там довольно мало
Туплю %) Надо больше спать.

PS У нас читали только общие положения. Разбора конкретных СУБД не производилось.
Кроме того, это решение не является красивым, потому как:
1) Да, изолируется обновление строк между процессами.
2) Но тот процесс I, который попал на чтение ещё не обновлённой, но заблокированной на запись выборки процессом II, ждёт окончания обновления I-м.

Просто поскольку обновление похоже занимает меньше времени, чем обработка, то это даёт выигрыш по сравнению с блокировкой записей на время обработки (полная транзакция).
увы, решение, конечно, не блещет, тут вы — правы! да, вы правы — выигрышь в производительности тут исключительно в том, что обновение занимает меньше времени чем обработка :)

однако а) с своей задачей справляется великолепно б) было введено в действие менее чем за сутки (это включая тесты на производительность!)
В Вашем примере где в конце LIMIT 1 FOR UPDATE не будет использоваться построчная блокировка.
Если пустить в другом коннекте такой же запрос до того как в первом сказан COMMIT будет стандартное ожидание освобождения лока.

Если уж дошли в мануале до селекта, посмотрите в каких случаях происходит построчная блокировка.
Как это "Трансакции не дают должного результата"?! Как раз классическое решение этой задачи - в обертывании select и update в одну транзакцию. Другой вопрос, что mysql... не очень транзакционная СУБД ;) Поэтому ей такие подсказки необходимы, чтобы нормально работать.
Не поможет обертывание в транзакцию, если SELECT в ней не for update.
Две транзакции могут сделать SELECT до того, как в первой из них пройдет commit.
Согласен, написал не вникнув в задачу. На самом деле достаточно к условию UPDATE дописать WHERE locked = 'false' - и смотреть на количество затронутых рядов. Тогда и транзакции ни к чему - атомарность выполнения update обеспечит отсутствие коллизий.
НЛО прилетело и опубликовало эту надпись здесь
А я не заморачивался по поводу FOR UPDATE, так как на моем мыскале нет InnoDB...
Я взял следующую выборку:
SELECT * FROM tbl WHERE id%N=n LIMIT скока надо;
где N - количество демонов, обслуживающих очередь,
n - номер демона (присваивается при старте демона и не повторяется у остальных демонов)
В результате не нужно рукоблудие с локами. Правда появилось рукоблудие с демонами, но оно решается гораздо легче.
И рациональное предложение - а не завести ли Вам на каждой физической машине по очереди (MySQL таблица) и паре..тройке демонов, работающих с этой локальной очередью ?
Тоже иногда использую такое распараллеливание, только как правило не по id. Минусы для автора очевидны - возможно такое построение задач, при котором большая часть нагузки уйдёт в один из потоков.
демонов сложно менеджить когда их много. более того, они «поднимаются» автоматически при необходимости!
Меня спасают pid файлики и расфоркивающиеся демоны.
Удалил файлики - демоны померли.
Запустил отца - детки поднялись, посмотрели pid-файлики и померли, если предыдущий ребенок еще работает...
а в SELECT вашем индексы-то не используются. и производительность сильно падает.
Мега-прокачанный проект открывает для себя транзакции. Смешно
простое применение транзакций в данном случае проблемы не решает, смешно, что не прочитали внимательно
FOR UPDATE - это частный случай блокировки, а блокировка - это и есть одна из изюминок транзакций :)
А по-моему смешно, что Вы утверждаете, что FOR UPDATE помогает демонам "просто не видеть ряд". Ибо это бред. И, как я уже до этого написал, всё равно вышеприведённые запросы НЕ ПОЗВОЛЯЮТ распараллеливать задачи!
Не бред, а заблуждение или описка )

Они позволяют распараллелить обработку, при этом подтормаживая иногда на коллизиях попыток чтения заблокированных строк.
К этому надо очень аккуратно подходить. Просчитывать все до мельчайших деталей. Делать нагрузочные тестирования. Мы уже с этим напоролись, такая блокировка создалась на Oracle от SELECT`a FOR UPDATE. Бизнес стоял.
Только полноправные пользователи могут оставлять комментарии. Войдите, пожалуйста.