2012-02-04 17 views
10

Mam skrypt PHP, który uruchamia zapytanie SELECT, a następnie natychmiast usuwa rekord. Istnieje wiele maszyn, które wysyłają ping do tego samego pliku php i pobierają dane z tej samej tabeli. Każda zdalna maszyna działa w zadaniu cron.SELECT następnie natychmiast USUŃ rekord mysql

Mój problem polega na tym, że czasami nie można usunąć wystarczająco szybko, ponieważ niektóre maszyny pingują dokładnie w tym samym czasie.

Moje pytanie brzmi: jak mogę wybrać rekord z bazy danych i usunąć go, zanim następny komputer go pobierze. Na razie dodałem tylko krótkie opóźnienie, ale nie działa zbyt dobrze. Próbowałem użyć transakcji, ale nie sądzę, że ma ona zastosowanie tutaj.

Oto przykładowy fragment mojego skryptu:

<?php 

$query = "SELECT * FROM `queue` LIMIT 1"; 
$result = mysql_query($query) or die(mysql_error()); 

while($row = mysql_fetch_array($result)){ 
    $email = $row['email']; 
    $campaign_id = $row['campaign']; 
} 

$queryx = "DELETE FROM `queue` WHERE `email` = '".$email."'"; 
$resultx = mysql_query($queryx) or die(mysql_error()); 

?> 

naprawdę docenić pomoc.

+2

jak to nie dotyczy? to brzmi * dokładnie * tak, jak to, do czego jest dobra transakcja. – mpen

+0

Czy dozwolone jest korzystanie z procedur przechowywanych? – dvicino

+0

@Mark - Czy transakcje również uniemożliwiają działanie SELECT? Zastanawiam się, czy to nie może być [email protected] - Jeśli są one uruchamiane przez 'cron', jaki jest sens' umrzeć (mysql_error()) '? Dlaczego nie zarejestrować błędu w pliku lub czymś? –

Odpowiedz

-3

Połóż swoje kwerendy do usunięcia w pętli while, po prostu przysięgnij, że chcesz zwiększyć limit od swojego wyboru.

<?php 
$query = mysql_query("SELECT * FROM `queue` LIMIT 1") or die(mysql_error()); 

while($row = mysql_fetch_array($query)){ 
    mysql_query("DELETE FROM `queue` WHERE `email` = '" . $row['email'] . "' LIMIT 1") or die(mysql_error()); 
} 
?> 

Powyższy kod będzie tak samo jak bieganie:

mysql_query("DELETE FROM `queue` LIMIT 1") or die(mysql_error()); 

Bądź ostrożny przy użyciu DELETE, jeśli pole email jest puste, to usunąć wszystkie wiersze, które mają pusty e-mail. Dodaj LIMIT 1 do zapytania usuwania, aby uniknąć usunięcia wielu wierszy.

Aby dodać losowe opóźnienie, można dodać sleep na początku skryptu,

np

<?php 
$seconds = mt_rand(1,10); 
sleep($seconds); 
?> 
+1

Nie wiem, że to pozbyć się problemu stanu wyścigu/rywalizacji, po prostu "skraca" ilość "możliwej" aktywności. W niektórych przypadkach może działać, ale nadal mogą występować anomalie. Zauważ także, że ponieważ istnieje 'LIMIT 1',' while() 'jest zbędne. Tak więc nie wiem, czy to naprawdę mogłoby zrobić coś wydajnego w końcu ... –

+0

Jeśli kiedykolwiek zdecyduje się zmienić SELECT na więcej niż 1 wynik, kwerenda usuwania będzie działać tylko na pierwszym wyniku. Tak więc w końcu jest bardziej produktywny i nie ma innych "zasobów mądrych" do posiadania DELETE poza pętlą. –

+0

"Jeśli"? Jest dużo jeśli. Jeśli PO strzela kolejką, wyobrażam sobie, że ma to być tylko jedna. Punkt jest wciąż taki sam (i wzmacniany, jeśli jest wiele wyników zwróconych dla później przetworzonych wyników). –

6

dobrze użyłbym zamki tabeli read more here

zamek jest bezpieczny i dotyczy jednej sesji klienta. Blokada stołu chroni tylko przed nieodpowiednimi odczytami lub zapisami wykonanymi przez inne sesje.

1

uruchom zapytanie o aktualizację, które zmieni klucz przed dokonaniem wyboru. Dokonaj wyboru za pomocą tego nowego klucza, który jest znany tylko w tej samej sesji.
Jeśli tabela jest innoDB, rekord jest zablokowany, a kiedy zostanie zwolniony, pozostałe opcje nie znajdą rekordu.

+1

[WYBIERZ ... DO AKTUALIZACJI i WYBIERZ ... BLOKADA W TRYBIE UDOSTĘPNIANIA Odczytywanie blokad] (http://dev.mysql.com/doc/refman/5.0/en/innodb-locking-reads.html). –

+0

Mam tylko pytanie, które mam z powyższym komentarzem, czy można "usunąć" z trybu "WYBIERZ ... DLA AKTUALIZACJI"? A może najpierw musisz odblokować (a więc wrócić do miejsca, w którym zaczęliśmy)? –

3

Należy użyć podzapytania następująco ...

<?php 

$queryx = "DELETE FROM `queue` WHERE `email` IN (SELECT email FROM `queue` LIMIT 1)"; 
$resultx = mysql_query($queryx) or die(mysql_error()); 

?> 

* Uwaga: Zawsze wybrać tylko te pola, które chcesz ... unikać select * ... będzie to spowolnić działanie

+0

Interesujące. Jeśli celem jest wyczyszczenie wszystkich oczek w kolejce za pomocą "losowej" wiadomości e-mail, może to działać. –

+0

Również "SELECT *" to świetna rada. "SELECT email" powinno wystarczyć. –

+0

Nie można użyć LIMIT wewnątrz podzapytania. W przeciwnym razie to było dokładnie to, co próbowałem zrobić. – john

4

Jeśli używasz MariaDB 10:

DELETE FROM `queue` LIMIT 1 RETURNING * 

Documentation.

+0

To jest fajne, ale działa tylko w MariaDB 10.0.5 lub nowszym –