2010-10-17 12 views
11

To jest trudne do wytłumaczenia (i bardzo dziwne), więc proszę o mnie. Wyjaśnię problem i naprawię go, ale chciałbym zobaczyć, czy ktoś może wyjaśnić, dlaczego działa tak, jak działa :)Obsługa bazy danych DBI z AutoCommit ustawiona na 0, która nie zwraca właściwych danych za pomocą SELECT?

Mam aplikację internetową, która używa mod_perl. Korzysta z bazy danych MySQL i regularnie zapisuję dane w bazie danych. Jest modułowy, więc ma również swój własny typ "bazy danych" modułu, w którym obsługuję połączenia, aktualizacje itp. Baza danych :: db_connect() jest używana do łączenia się z bazą danych, a AutoCommit jest ustawiona na 0.

Zrobiłem kolejną aplikację Perla (samodzielny demon), która okresowo pobiera dane z bazy danych i wykonuje różne zadania w zależności od zwracanych danych. Załączam do niego moduł database.pm, więc nie muszę przepisywać/duplikować wszystkiego.

Problem mam przeżywa to:

Aplikacja połączy się z bazą danych przy starcie, a następnie pętli zawsze, pobierania danych z bazy danych co X sekund. Jeśli jednak dane w bazie danych zostaną zaktualizowane, moja aplikacja nadal będzie zwracana "stare" dane, które dostałem na początkowe połączenie/zapytanie do bazy danych.

Na przykład - Mam 3 wiersze, a kolumna "Nazwa" ma wartości "a", "b" i "c" - dla każdego rekordu. Jeśli zaktualizuję jeden z wierszy (na przykład przy użyciu klienta mysql z wiersza poleceń) i zmienię nazwę z "c" na "x", mój autonomiczny demon nie otrzyma tych danych - nadal będzie otrzymywał/b/c zwrócony z MySQL. Przechwyciłem ruch db za pomocą tcpdump i mogłem zdecydowanie zobaczyć, że MySQL naprawdę zwrócił te dane. Próbowałem używać SQL_NO_CACHE również z SELECT (ponieważ nie byłem pewien, co się dzieje), ale to też nie pomogło.

Następnie zmodyfikowałem ciąg połączenia DB w moim autonomicznym demonie i ustawiłem AutoCommit na 1. Nagle aplikacja zaczęła uzyskiwać poprawne dane.

Jestem zdziwiony, ponieważ sądziłem, że AutoCommit wpływa tylko na instrukcje typu INSERT/UPDATE i nie ma wpływu na instrukcję SELECT. Ale na pozór tak jest i nie rozumiem dlaczego.

Czy ktoś wie, dlaczego instrukcja SELECT nie zwróci "zaktualizowanych" wierszy z bazy danych, gdy AutoCommit jest ustawiona na 0, i dlaczego zwróci zaktualizowane wiersze, gdy AutoCommit jest ustawiona na 1?

Oto uproszczony (usunięty sprawdzanie błędów itp.) Kod, którego używam w autonomicznym demonie, który nie zwraca zaktualizowanych wierszy.

Zmiana AutoCommit na 1 to rozwiązuje. Czemu?

Thanks :)

P.S: Nie wiem, czy ktoś to obchodzi, ale wersja DBI jest 1,613, DBD :: mysql jest 4,017, Perl 5.10.1 (Ubuntu 10.04).

+0

Czy ustawienie 'auto_commit' jest włączone lub wyłączone w kliencie mysql z wiersza poleceń (gdzie wykonano operację' UPDATE')? – Ether

+0

Jest włączony (domyślnie włączony i nie zmieniłem go). Widzę "nowe" zaktualizowane dane od klienta mysql lub jakiejkolwiek innej "sesji" (nowa sesja DBI, która łączy się lub dowolny inny klient, który łączy się z bazą danych) - to tylko sesja z AutoCommit 0, która nie może uzyskać dostępu do zaktualizowanych danych . – sentinel

Odpowiedz

14

Przypuszczam, że używasz tabel InnoDB, a nie MyISAM.Jak opisano w InnoDB transaction model, wszystkie twoje zapytania (w tym SELECT) mają miejsce wewnątrz transakcji.

Po włączeniu AutoCommit, dla każdej kwerendy uruchamiana jest transakcja, a jeśli się to uda, jest ona niejawnie zatwierdzona (jeśli się nie powiedzie, zachowanie może się różnić, ale transakcja zostanie zakończona). Możesz zobaczyć niejawne zatwierdzenia w binlog MySQL. Ustawiając AutoCommit na wartość false, musisz samodzielnie zarządzać transakcjami.

Domyślnym poziomem izolacji transakcji jest REPEATABLE READ, co oznacza, że ​​wszystkie zapytania o numer SELECT będą czytać tę samą migawkę (tę, która została ustalona podczas uruchamiania transakcji).

Oprócz rozwiązania podaną w drugiej odpowiedzi (ROLLBACK przed rozpoczęciem odczytu) oto kilka rozwiązań:

Możesz wybrać inny poziom izolacji transakcji, jak READ COMMITTED, co sprawia, że ​​SELECT zapytań czytać świeża migawka za każdym razem.

Można również pozostawić wartość true true (ustawienie domyślne) i rozpocząć własne transakcje, wydając BEGIN WORK. Spowoduje to tymczasowe wyłączenie zachowania AutoCommit, dopóki nie zostanie wydana instrukcja COMMIT lub , po której każde zapytanie otrzyma ponownie swoją własną transakcję (lub rozpoczniesz inną z BEGIN WORK).

Ja osobiście wybrałbym tę drugą metodę, ponieważ wydaje się bardziej elegancka.

+0

To jest naprawdę niesamowita odpowiedź, i dziękuję bardzo za poświęcenie czasu na wyjaśnienie tego. Czytałem dokumentację i próbowałem to rozgryźć, ale tak na prawdę nie natknąłem się na to, o czym tu wspomniałeś (prawdopodobnie czytało niewłaściwe dokumenty;). Jeszcze raz dziękuję, to naprawdę wiele wyjaśnia. – sentinel

+0

Kolejne pytanie (zakładając, że jeszcze raz to przeczytałeś :). Używamy procedur przechowywanych po stronie MySQL, więc nie robię żadnych rzeczy transakcyjnych w kodzie Perla. Czy mogę użyć AutoCommit = 1 i wydać "BEGIN WORK" tuż przed wywołaniem procedury przechowywanej z kodu Perla? – sentinel

+0

Tak, możesz to zrobić. Możesz również umieścić transakcję w procedurze przechowywanej (ale * nie * wewnątrz funkcji zapisanej), niezależnie od tego, co lepiej pasuje do twojego przepływu pracy. – kixx

4

Myślę, że po wyłączeniu autocommit również rozpoczyna się transakcja. A kiedy rozpoczniesz transakcję, możesz być chroniony przed zmianami innych osób, dopóki ich nie zatwierdzisz lub nie wycofasz. Tak więc, jeśli moje półświadome przypuszczenie jest poprawne, a ponieważ wyszukujesz tylko dane, dodaj wycofanie przed operacją uśpienia (bez zatrzymywania blokady, z którego nie korzystasz, itp.):

$dbh->rollback; 
+0

Dziękuję za odpowiedź :) – sentinel

Powiązane problemy