2013-06-22 9 views
4

У меня есть один поток Perl скрипт работает на организованном общем сервере, который в основном выполняет следующий код:Гусеницы недетерминированные ошибки MySQL в Perl

my $O_dbh = DBI->connect("dbi:mysql:dbname=dbname", "abc", "xxx", {RaiseError => 1}); 
$O_dbh->begin_work(); 

my $O_sth1 = $O_dbh->prepare('SELECT COUNT(*) FROM mytable WHERE any = 5'); 

$O_sth1->execute(); 
my @result1 = $O_sth1->fetchrow_array(); 
my $oldValue = $result1[0]; 

$O_sth1->finish(); 

my $O_sth2 = $O_dbh->prepare('INSERT INTO mytable (any) VALUES (5)'); 
$O_sth2->execute(); 

$O_sth1->execute(); 
my @result2 = $O_sth1->fetchrow_array(); 
my $newValue = $result2[0]; 

if ($oldValue + 1 == $newValue) { 
    $O_dbh->commit(); 
} 
else { 
    $O_dbh->rollback(); 
    die "why?! new = $newValue, old = $oldValue"; 
} 

Несколько раз (< 1%) код работает в случай отката и не выполняется. В моей локальной системе я не могу воспроизвести эту ошибку. База данных MySQL 5.

CREATE TABLE `mytable` (
    `id` int(11) NOT NULL auto_increment, 
    `any` int(11) NOT NULL default '1', 
    PRIMARY KEY (`id`) 
) ENGINE=InnoDB DEFAULT CHARSET=utf8; 

Как отследить эту ошибку? Любая помощь будет принята с благодарностью.

+0

Почему вы называете 'finish' на вашем первом дескрипторе инструкции? Документы [DBI docs] (https://metacpan.org/module/TIMB/DBI-1.627/DBI.pm#finish), похоже, предполагают, что вы не должны. – innaM

+0

Вы пытались отладить это? Я хотел бы узнать результаты некоторых экспериментов, таких как: Устраняет ли вызов 'finish' какой-либо разницы? Изменяет ли что-либо, если вы ставите 'sleep 1' между двумя вызовами' execute'? Получаете ли вы ожидаемый результат, если вы «выполняете» 'SELECT' второй раз, когда он терпит неудачу. Устранена ли эта проблема, если вы удалите «begin_work»? Каков эффект удаления предложения 'WHERE' на' SELECT'? Что * * '$ newvalue', если оно не' $ oldvalue + 1'? – Borodin

+0

@innM: Убираем финишный вызов, до сих пор нет функционального эффекта. Я буду наблюдать это в системе предварительной подготовки. К сожалению, это займет некоторое время. (Произошла ошибка выше трех раз за последний месяц ...) – Meise2000

ответ

3

Предполагая, что ваша база данных работает с настройками по умолчанию, я больше удивляюсь, что ваш SELECTкогда-либо возвращает два разных значения.

The documentation говорит этот

Если уровень изоляции транзакции REPEATABLE READ (уровень по умолчанию), все согласованные чтения внутри той же транзакции читать снимок, установленный первой такой чтения в этой транзакции. Вы можете получить более свежий снимок для своих запросов, совершив текущую транзакцию и после этого выдав новые запросы.

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

Однако, это звучит, как это может помочь

С READ COMMITTED уровня изоляции, каждый из которых соответствует чтения в транзакции наборов и читает свой свежий снимок.

Я думаю, вы должны попробовать

$O_dbh->do('SET SESSION TRANSACTION ISOLATION LEVEL READ COMMITTED'); 

сразу после подключения, и посмотреть, если это исправляет вещи для вас.

Однако после этой транзакции вы должны либо обработать disconnect дескриптор базы данных, либо вернуть его на предыдущий уровень изоляции. В противном случае вы начнете получать непоследовательные результаты.

+1

Я использую уровень изоляции по умолчанию (REPEATABLE READ).Документация скрывает, что выбранные данные считываются из первого моментального снимка И вызванных модификаций (INSERT/UPDATE) текущей транзакции. SET AUTOCOMMIT = 0; SELECT COUNT (*) FROM mytable; INSERT INTO mytable (любой) ЦЕННОСТИ (5); SELECT COUNT (*) FROM mytable; – Meise2000

Смежные вопросы