Mysql запрет за считывание до выполнения определенных действий

123
Dreammaker
На сайте с 20.04.2006
Offline
570
#11

Aisamiery, я тоже хотел написать, что здесь, mutex или что-то подобное должно быть, но все так уверенно начали советовать транзакции, что я начал верить в этом сам :)

И по-хорошему, нужно бы дополнительную информацию от ТС, что должен получить второй пользователь, который полез "невовремя". В первом посте об этом не говорится, а тут могут быть нюансы.

Aisamiery
На сайте с 12.04.2015
Offline
293
#12
Dreammaker:
Aisamiery, я тоже хотел написать, что здесь, mutex или что-то подобное должно быть, но все так уверенно начали советовать транзакции, что я начал верить в этом сам :)

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

Разработка проектов на Symfony, Laravel, 1C-Bitrix, UMI.CMS, OctoberCMS
V
На сайте с 10.01.2012
Offline
85
#13
Dreammaker:
Aisamiery, я тоже хотел написать, что здесь, mutex или что-то подобное должно быть, но все так уверенно начали советовать транзакции, что я начал верить в этом сам :)

И по-хорошему, нужно бы дополнительную информацию от ТС, что должен получить второй пользователь, который полез "невовремя". В первом посте об этом не говорится, а тут могут быть нюансы.

Второй также должен получить table_1.id, только отличительный от того что уже выбран у первого пользователя.

Aisamiery
На сайте с 12.04.2015
Offline
293
#14
Varenik:
Второй также должен получить table_1.id, только отличительный от того что уже выбран у первого пользователя.

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

Можно в table1 сделать поле lock, и воспользоваться select for update, выставляя это в поле 1, а у клиентов делать выборку на status = 0 and lock = 0 (да не забудьте тут про транзакции для них они нужны https://dev.mysql.com/doc/refman/8.0/en/innodb-locking-reads.html)

danforth
На сайте с 18.12.2015
Offline
153
#15

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

Этот вариант правильный:

livetv:
Вариант:
1. Стартуете транзакцию
2. SELECT c FOR UPDATE
3. Досыпаете 0,5 сек
4. Коммитите транзакцию

Если поле использовать, то любой уровень изоляции который исключит DIRTY WRITES, в MySQL это read committed и выше.

Junior Web Developer
Dreammaker
На сайте с 20.04.2006
Offline
570
#16
Varenik:
Второй также должен получить table_1.id, только отличительный от того что уже выбран у первого пользователя.

Генерируете UUID, пишете его в выделенное для этого поле нужной записи + возможно, в отдельное поле время когда этот UUID устареет. Затем что хотите делаете с этим полем по этому UUID'у обновляете данные + меняете статус на = 1 + затираете UUID и дату (если нужно). Если есть желание заверните это всё в транзакцию.

V
На сайте с 10.01.2012
Offline
85
#17
Aisamiery:
Для начала опишите задачу по конкретнее которую вы решаете, мы попробуем подсказать более элегантное решение в вашем случае

Вот собственно код (не весь - внутри есть еще обработчики). Частично задача решилась, но Немного баги встречаются в других выборках, связанных с таблицей

$dbase->query("BEGIN");
// начало выборки
$new_order = $dbase->super_query("SELECT o.* FROM ord o
INNER JOIN status_ord so ON so.id = o.id_status_ord
INNER JOIN status s ON s.id = so.id_status
WHERE so.id_status = 1 AND o.is_user = 0 AND o.comment!='' ORDER BY o.id ASC FOR UPDATE");
if (!$new_order) {
$new_order = $dbase->super_query("SELECT o.* FROM ord o
INNER JOIN status_ord so ON so.id = o.id_status_ord
INNER JOIN status s ON s.id = so.id_status
INNER JOIN orders_sets os ON o.id=os.order_id
WHERE so.id_status = 1 AND o.is_user = 0 ORDER BY o.id ASC FOR UPDATE");
}

/* Тут еще ряд выборок, которые и выполняются 0.5 секунды */

if ($new_order) {
$issue_informer = $dbase->super_query("SELECT * FROM messages WHERE recipient_id = ". $recipient_id ." AND type='new_order' AND parent_id='". $new_order***91;'id'] ."'");

if(!$issue_informer) {
$dbase->query("UPDATE ord SET is_user = ". $recipient_id ." WHERE id=". $new_order['id'] ."");

// По факту финиш обработки. Именно от начала до этого момента другой пользователь может успеть "выхватить" этот же $new_order['id']
$dbase->query("INSERT INTO messages(parent_id, type, sender_id, recipient_id, message, create_at, expired_at, status) VALUES (". $new_order['id'] .", 'new_order', 225, ". $recipient_id .", '', now(), now(), 0)");
}

}

$tmp = $dbase->query("COMMIT");

Буду признателен за дельный совет

V
На сайте с 10.01.2012
Offline
85
#18

Нет дельных мыслей обхода моей рукожопости?

Aisamiery
На сайте с 12.04.2015
Offline
293
#19
Varenik:
Нет дельных мыслей обхода моей рукожопости?

по вашему коду не очень понятно если честно. Почему бы вам не обрабатывать это в единый поток, например скриптом консоли? или вам нужен ответ пользователю?

V
На сайте с 10.01.2012
Offline
85
#20
Aisamiery:
по вашему коду не очень понятно если честно. Почему бы вам не обрабатывать это в единый поток, например скриптом консоли? или вам нужен ответ пользователю?

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

123

Авторизуйтесь или зарегистрируйтесь, чтобы оставить комментарий