Witaj Gościu! ( Zaloguj | Rejestruj )

Forum PHP.pl

> [PHP] Wygenerowanie unikalnych hashy
DNMX
post
Post #1





Grupa: Zarejestrowani
Postów: 130
Pomógł: 0
Dołączył: 18.09.2021

Ostrzeżenie: (0%)
-----


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ć?
Go to the top of the page
+Quote Post
 
Start new topic
Odpowiedzi (1 - 7)
ohm
post
Post #2





Grupa: Zarejestrowani
Postów: 623
Pomógł: 144
Dołączył: 22.12.2010

Ostrzeżenie: (0%)
-----


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ść?

Ten post edytował ohm 6.01.2023, 19:44:02
Go to the top of the page
+Quote Post
DNMX
post
Post #3





Grupa: Zarejestrowani
Postów: 130
Pomógł: 0
Dołączył: 18.09.2021

Ostrzeżenie: (0%)
-----


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,
Go to the top of the page
+Quote Post
Salvation
post
Post #4





Grupa: Zarejestrowani
Postów: 405
Pomógł: 73
Dołączył: 15.07.2014

Ostrzeżenie: (0%)
-----


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ę.

Ten post edytował Salvation 6.01.2023, 22:04:22
Go to the top of the page
+Quote Post
DNMX
post
Post #5





Grupa: Zarejestrowani
Postów: 130
Pomógł: 0
Dołączył: 18.09.2021

Ostrzeżenie: (0%)
-----


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.

Ten post edytował DNMX 6.01.2023, 22:36:15
Go to the top of the page
+Quote Post
Salvation
post
Post #6





Grupa: Zarejestrowani
Postów: 405
Pomógł: 73
Dołączył: 15.07.2014

Ostrzeżenie: (0%)
-----


OK, racja. To jeszcze druga próba (IMG:style_emoticons/default/wink.gif)
W sumie to dwie (IMG:style_emoticons/default/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.

Ten post edytował Salvation 7.01.2023, 00:26:23
Go to the top of the page
+Quote Post
viking
post
Post #7





Grupa: Zarejestrowani
Postów: 6 381
Pomógł: 1116
Dołączył: 30.08.2006

Ostrzeżenie: (0%)
-----


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.
Go to the top of the page
+Quote Post
kreatiff
post
Post #8





Grupa: Zarejestrowani
Postów: 324
Pomógł: 105
Dołączył: 7.08.2012

Ostrzeżenie: (0%)
-----


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.

Ten post edytował kreatiff 17.01.2023, 16:11:31
Go to the top of the page
+Quote Post

Reply to this topicStart new topic
2 Użytkowników czyta ten temat (2 Gości i 0 Anonimowych użytkowników)
0 Zarejestrowanych:

 



RSS Aktualny czas: 22.09.2025 - 07:31