В MySQL я должен проверить, вернул ли запрос select какие-либо записи, если нет, я вставляю запись.Однако я боюсь, что вся операция if-else в сценариях PHP НЕ настолько атомарна, как хотелось бы, то есть будет прерываться в некоторых сценариях, например, если вызывается другой экземпляр сценария, где необходимо работать с той же записью:
if(select returns at least one record)
{
update record;
}
else
{
insert record;
}
Я не использовал транзакции здесь, и автокоммит включен.Я использую MySQL 5.1 с PHP 5.3.Таблица InnoDB.Я хотел бы знать, является ли приведенный выше код неоптимальным и действительно сломается.Я имею в виду, что один и тот же сценарий повторно вводится двумя экземплярами, и происходит следующая последовательность запросов:
- экземпляр 1 пытается выбрать запись, не находит ни одного, входит в блок для запроса вставки
- экземпляр 2 пытается выбрать запись, не находит ничего, входит в блок для запроса вставки
- экземпляр 1 пытается вставить запись, успешно
- экземпляр 2 пытается вставить запись, не удается, прерываетсяскрипт автоматически
Это означает, что экземпляр 2 будет прерван и вернет ошибку, пропуская что-либо после оператора запроса вставки.Я мог бы сделать ошибку не фатальной, но мне не нравится игнорировать ошибки, я бы скорее хотел знать, реальны ли мои страхи здесь.
Обновление: что я в итоге делал (это нормально дляТАК?)
Данная таблица помогает регулировать (разрешать / отклонять, действительно) количество сообщений, отправляемых приложением каждому получателю.Система не должна отправлять более X сообщений получателю Y в течение периода Z. Таблица [концептуально] выглядит следующим образом:
create table throttle
(
recipient_id integer unsigned unique not null,
send_count integer unsigned not null default 1,
period_ts timestamp default current_timestamp,
primary key (recipient_id)
) engine=InnoDB;
И блок [несколько упрощенного / концептуального] кода PHP, которыйдолжен делать атомарную транзакцию, которая поддерживает правильные данные в таблице и разрешает / запрещает отправку сообщения в зависимости от состояния газа:
function send_message_throttled($recipient_id) /// The 'Y' variable
{
query('begin');
query("select send_count, unix_timestamp(period_ts) from throttle where recipient_id = $recipient_id for update");
$r = query_result_row();
if($r)
{
if(time() >= $r[1] + 60 * 60 * 24) /// The numeric offset is the length of the period, the 'Z' variable
{/// new period
query("update throttle set send_count = 1, period_ts = current_timestamp where recipient_id = $recipient_id");
}
else
{
if($r[0] < 5) /// Amount of messages allowed per period, the 'X' variable
{
query("update throttle set send_count = send_count + 1 where recipient_id = $recipient_id");
}
else
{
trigger_error('Will not send message, throttled down.', E_USER_WARNING);
query('rollback');
return 1;
}
}
}
else
{
query("insert into throttle(recipient_id) values($recipient_id)");
}
if(failed(send_message($recipient_id)))
{
query('rollback');
return 2;
}
query('commit');
}
Что ж, несмотря на то, что возникают тупиковые ситуации InnoDB, это довольно хорошонет?Я не стучу в грудь или что-то еще, но это просто лучшее сочетание производительности / стабильности, которое я могу сделать, если не использовать MyISAM и не блокировать всю таблицу, что я не хочу делать из-за более частых обновлений / вставок по сравнению свыбирает. * 1 028 *