Pomoc - Szukaj - Użytkownicy - Kalendarz
Pełna wersja: [PHP] Wygenerowanie unikalnych hashy
Forum PHP.pl > Forum > Przedszkole
DNMX
Hej. Mam bazę ok. 2mln rekordów. Każdemu chcę nadać indywidualny, losowy hash skłądający się z 5-ciu cyfr lub małych i dużych znaków. Stworzyłem coś takiego:
  1. ?php
  2. function generateRandomhash($length = 5) {
  3. $characters = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ';
  4. $charactersLength = strlen($characters);
  5. $randomString = '';
  6. for ($i = 0; $i < $length; $i++) {
  7. $randomString .= $characters[rand(0, $charactersLength - 1)];
  8. }
  9. return $randomString;
  10. }
  11.  
  12. $mysqli = new mysqli("localhost","test","test","test");
  13.  
  14.  
  15. $db_name="test";
  16. $q = "SELECT * FROM `$db_name` WHERE hash IS NULL LIMIT 1000;";
  17. $res = $mysqli -> query($q);
  18. while($i = mysqli_fetch_array($res,MYSQLI_ASSOC)) {
  19. $hash = generateRandomhash();
  20. $cq = "SELECT * FROM `$db_name` WHERE hash='$hash'";
  21. $resc = $mysqli -> query($cq);
  22. if (mysqli_num_rows($resc) == 0 ) {
  23. $uq = "UPDATE `$db_name` SET hash='$hash' WHERE id='$i[id]'";
  24. $mysqli -> query($uq);
  25. } else {
  26. echo "Zostawiam $i[name] do ponownego losowania";
  27. }
  28.  
  29. }
  30. ?>

Z początku działa szybko (wiadomo, nie ma jeszcze tyle hashy co się powtarzają) ale pod koniec wolniej. Na obecnej maszynie oszacowałem czas wykonywania się tego kody na 50-100 godzin. Jak mogę to zoptywalizować?
ohm
Indeks/indeksy, dac unique dla hash i nie bawic sie w select

A no i jeszcze jaki typ kolumny, varchar? Jeśli tak to jaka długość?
DNMX
Myślałem, żeby dać unique ale nie przechodzi ze względu na te pola, które mają NULL lub ieustawiony hash. Tak wygląda struktura tego pola:
Kod
  `hash` varchar(5) COLLATE utf8_unicode_ci DEFAULT NULL,
Salvation
Przeoranie tego przez PHP zajmie trochę czasu i zje zasobów. Jeżeli tak może być, to spoko, ale ja raczej szukałbym rozwiązania bliżej bazy danych.

Dla MySQL, można skorzystać z takiego czegoś do generowania hasha:
  1. LEFT(MD5(RANDOM_BYTES()), 5)

Nie powinno wygenerować tych samych hashy, więc na spokojnie możesz dodać `unique` na kolumnę.
DNMX
Próbowałem i nestety generuje multum powtórzeń. MD5 daje wynik heksadecymalny a zatem pole hash ma 5^16 = 1 048 576 możliwości, mniej niż moja baza danych. Ciąg znaków składający się z 0-9, a-z i A-Z (58 różnych znaków) czyli 58^5 = 656 356 768 (ok. 300 razy za dużo) daje ok 3 powtórzeń przy generowaniu końcowych hashy porcjami po 1000.
Salvation
OK, racja. To jeszcze druga próba wink.gif
W sumie to dwie biggrin.gif
  1. LEFT(TO_BASE64(REPLACE(UUID(), '-', '')), 5)
  2.  
  3. LEFT(TO_BASE64(RANDOM_BYTES(16)), 5)

Natomiast teraz zdałem sobie z tego sprawę, że `RANDOM_BYTES` może wygenerować znak np. slasha, a to z kolei może nie być poprawnym hashem... Zależy od wytycznych.
viking
Ja bym poszedł w kierunku https://hashids.org/php/ które generuje stały ciąg dla id. Nie potrzebujesz wtedy select (który w ten sposób zrobiony czyli bazując na offset i limit będzie coraz wolniejszy na kolejnym zbiorze danych) tylko możesz od razu robić update po numerycznym pk.
kreatiff
Już to pewnie zdążyłeś załatwić, ale jeszcze dodam coś innego dla zainteresowanych.

base 62 posiada wszystkie cyfry oraz małe i duże litery ASCII.
Dla pięcioznakowych id złożonych z tych znaków, ich kombinacji jest w sumie 62^5 = 916132832

Losuj dowolną liczbę od 0 czy 1 do tych 900+ milionów i zamieniaj ją na base 62:
  1. gmp_strval(gmp_init(rand(0, 62*62*62*62*62), 10), 62);

Jeszcze można dodawać zera z przodu, gdyby wylosowało niską liczbę i znaków było mniej niż 5.
Tym sposobem możesz też zamieniać istniejące id na znaki, ale naturalnie w tym wypadku pojawi się zauważalny wzór w id, jeśli były one po kolei.
To jest wersja lo-fi głównej zawartości. Aby zobaczyć pełną wersję z większą zawartością, obrazkami i formatowaniem proszę kliknij tutaj.
Invision Power Board © 2001-2024 Invision Power Services, Inc.