Состояние гонки

Вурдалак

Продвинутый новичок
Состояние гонки

PHP:
<?php

$db->query('SELECT `timestamp` FROM ...');

if( $db->result() < time() )
{
	$db->query('UPDATE ... SET `timestamp` = ' . ( time() + 3600 ));

	# Выполнение какой-то полезной работы
}

?>
Тут я просто примерно набросал логику моего скрипта.

Иногда случается ситуация, при которой практически одновременно 2 скрипта запрашивают timestamp, в обоих же происходит UPDATE timestamp'а и, соответственно, дважды выполняется "полезная работа".
Как лучше избежать такой ситуации? Был совет воспользоваться преимуществом mysql_insert_id(), но немного не нравится. Есть другие способы решения?
 

whirlwind

TDD infected, paranoid
Вопросы чотко правильные. Тока не надо делать из мухи слона :) просто те, кто в курсе, запутываются постановкой.

2tc: читай топ, на который сослался DiMA и найдешь решение проблемы
 

Вурдалак

Продвинутый новичок
После чтения приведённой темы, я непонял морали: транзакции или "... FOR UPDATE"?

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

Далее, "... FOR UPDATE" не позволит заблокировать таблицу другому потоку. Этот "запрет" выражается в том, что MySQL будет ждать завершения работы первого потока или появится ошибка о невозможности блокировки?

P.S. И о каких индексах говорится в той теме вообще?
 

whirlwind

TDD infected, paranoid
Если говорить о транзакции, то не пойму как она может помочь: разве транзакция может запретить второму потоку делать SELECT?
если не задумываясь, то ответ - да. если задумываться, то есть тонкости.

PS. если тебе пипец как важно, то только читать и пробовать. а если не пипец, то просто транзакцию. делаешь и делов.
 

DiMA

php.spb.ru
Команда форума
про индексы забудь, я же по-русски написал - решение в SELECT ... FOR UPDATE
 

whirlwind

TDD infected, paranoid
Автор оригинала: DiMA
про индексы забудь, я же по-русски написал - решение в SELECT ... FOR UPDATE
мы не поняли друг друга? :) select for update без индекса == lock table, грубо

нормальное решение = транзакция с serialize
 

DiMA

php.spb.ru
Команда форума
индексы у него уже должны быть, а транзакции подразумеваются самой командой
 

whirlwind

TDD infected, paranoid
лан. я умываюсь. кто нид максимум perofrmance, тот велкам ту тот самый топег. вопрос и простой и сложный по мэни причинам, но лично я считаю что это оффтопно отношение к вопросу.
 

Вурдалак

Продвинутый новичок
Итак, верно ли, что код
PHP:
<?php

$db->query('SET `autocommit` = 0;');
$db->query('START TRANSACTION;');
$db->query('SET TRANSACTION ISOLATION LEVEL SERIALIZABLE;');
$db->query('SELECT `timestamp` FROM ... WHERE id = ... FOR UPDATE');

if( $db->result() < time() )
{
    $db->query('UPDATE ... SET `timestamp` = ' . ( time() + 3600 ) . ' WHERE id = ...');

    # Выполнение какой-то полезной работы
}

$db->query('COMMIT;');

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

P.S.
Вот, в мануале: http://dev.mysql.com/doc/refman/5.1/en/innodb-locking-reads.html:
Locks set by LOCK IN SHARE MODE and FOR UPDATE reads are released when the transaction is committed or rolled back.
Как правильнее эту фразу перевести?
P.P.S. Добавил "... FOR UPDATE"
 

dimagolov

Новичок
Вурдалак, княпни на ссылку в мане на FOR UPDATE, они там не просто так синенькие.
 

DiMA

php.spb.ru
Команда форума
Вурдалак
Ты почему все еще здесь? Открой 2 консоли к базе. Пиши последовательно в обоих эти команды (в разных вариантах). Там проверишь и убедишься во всем сам. Можешь скриптом + sleep() тоже самое эмулировать.
 

Вурдалак

Продвинутый новичок
dimagolov, а я ссылку тут на что привёл?

-~{}~ 28.09.09 23:12:

DiMA, сейчас попробуем

-~{}~ 28.09.09 23:35:

Да, работает вроде бы нормально.
 

FractalizeR

Новичок
Locks set by LOCK IN SHARE MODE and FOR UPDATE reads are released when the transaction is committed or rolled back
Блокировки, установленные операторами LOCK IN SHARE MODE и SELECT FOR UPDATE, снимаются в случае фиксации или отката транзакции.
 

Вурдалак

Продвинутый новичок
FractalizeR, спасибо

-~{}~ 22.10.09 19:40:

Меня никто не поправил, правильнее писать
PHP:
<?php

$db->query('SET `autocommit` = 0;');
$db->query('SET TRANSACTION ISOLATION LEVEL SERIALIZABLE;'); 
$db->query('START TRANSACTION;');
На старом сервере работало нормально, а на новом вылезло "Transaction isolation level can't be changed while a transaction is in progress"
 
Сверху