Mysql
 sql >> Baza danych >  >> RDS >> Mysql

Zapętlaj, aż hasło będzie unikalne

Ściśle mówiąc, test na unikalność nie zagwarantuje unikalności przy jednoczesnym obciążeniu. Problem polega na tym, że sprawdzasz unikalność przed (i oddzielnie od) miejscem, w którym wstawiasz wiersz, aby „odebrać” nowo wygenerowany kod dostępu. Innym procesem może być robienie tego samego w tym samym czasie. Oto jak to działa...

Dwa procesy generują dokładnie ten sam kod dostępu. Każdy z nich zaczyna się od sprawdzenia wyjątkowości. Ponieważ żaden proces (jeszcze) nie wstawił wiersza do tabeli, oba procesy nie znajdą pasującego hasła w bazie danych, więc oba procesy przyjmą, że kod jest unikalny. Teraz, gdy każdy z procesów kontynuuje swoją pracę, w końcu będą obie wstaw wiersz do files tabela za pomocą wygenerowanego kodu - a tym samym otrzymasz duplikat.

Aby obejść ten problem, musisz przeprowadzić kontrolę i wstawić w jednej „atomowej” operacji. Poniżej znajduje się wyjaśnienie tego podejścia:

Jeśli chcesz, aby hasło było unikalne, powinieneś zdefiniować kolumnę w swojej bazie danych jako UNIQUE . Zapewni to unikalność (nawet jeśli Twój kod php tego nie robi) poprzez odmowę wstawienia wiersza, który spowodowałby zduplikowanie hasła.

CREATE TABLE files (
  id int(10) unsigned NOT NULL auto_increment PRIMARY KEY,
  filename varchar(255) NOT NULL,
  passcode varchar(64) NOT NULL UNIQUE,
)

Teraz użyj SHA1() mysql i NOW() aby wygenerować kod dostępu jako część instrukcja wstawiania. Połącz to z INSERT IGNORE ... (dokumentacja ) i wykonaj pętlę, aż wiersz zostanie pomyślnie wstawiony:

do {
    $query = "INSERT IGNORE INTO files 
       (filename, passcode) values ('whatever', SHA1(NOW()))";
    $res = mysql_query($query);
} while( $res && (0 == mysql_affected_rows()) )

if( !$res ) {
   // an error occurred (eg. lost connection, insufficient permissions on table, etc)
   // no passcode was generated.  handle the error, and either abort or retry.
} else {
   // success, unique code was generated and inserted into db.
   // you can now do a select to retrieve the generated code (described below)
   // or you can proceed with the rest of your program logic.
}

Uwaga: Powyższy przykład został zredagowany w celu uwzględnienia doskonałych obserwacji zamieszczonych przez @martinstoeckli w sekcji komentarzy. Wprowadzono następujące zmiany:

  • zmieniono mysql_num_rows() (dokumenty ) do mysql_affected_rows() (dokumenty ) — liczba_wierszy nie dotyczy wstawek. Usunięto również argument do mysql_affected_rows() , ponieważ ta funkcja działa na poziomie połączenia, a nie na poziomie wyniku (w każdym razie wynikiem wstawienia jest wartość logiczna, a nie numer zasobu).
  • dodano sprawdzanie błędów w warunku pętli i dodano test na błąd/powodzenie po zakończeniu pętli. Obsługa błędów jest ważna, ponieważ bez niej błędy bazy danych (takie jak utracone połączenia lub problemy z uprawnieniami) spowodują, że pętla będzie się obracać w nieskończoność. Podejście pokazane powyżej (używając IGNORE i mysql_affected_rows() i testowanie $res oddzielnie dla błędów) pozwala nam odróżnić te „prawdziwe błędy bazy danych” od unikatowego naruszenia ograniczenia (co jest całkowicie prawidłowym warunkiem braku błędu w tej sekcji logiki).

Jeśli chcesz uzyskać kod dostępu po jego wygenerowaniu, po prostu wybierz rekord ponownie:

$res = mysql_query("SELECT * FROM files WHERE id=LAST_INSERT_ID()");
$row = mysql_fetch_assoc($res);
$passcode = $row['passcode'];

Edytuj :zmieniono powyższy przykład, aby użyć funkcji mysql LAST_INSERT_ID() , a nie funkcję PHP. Jest to wydajniejszy sposób na osiągnięcie tego samego, a wynikowy kod jest czystszy, jaśniejszy i mniej zagracony.



  1. Database
  2.   
  3. Mysql
  4.   
  5. Oracle
  6.   
  7. Sqlserver
  8.   
  9. PostgreSQL
  10.   
  11. Access
  12.   
  13. SQLite
  14.   
  15. MariaDB
  1. Czy możesz wykonać pętlę For Each Row za pomocą MySQL?

  2. Klauzula SQL IN wolniejsza niż pojedyncze zapytania

  3. Kiedy używać modelu partycjonowania mysql

  4. Cena MIN/MAX dla każdego produktu (zapytanie)

  5. cross-dbms sposób na sprawdzenie, czy ciąg jest numeryczny