Gdy rozmiar bazy danych przekracza miliony wierszy, zaleca się rozpoczęcie skalowania aplikacji i rozdzielenie bazy danych na wiele serwerów fizycznych.
Największym problemem związanym z podziałem bazy danych na wiele części jest jej późniejsza synchronizacja, gdy użytkownik zażąda określonych danych.
Załóżmy, że masz tabelę artykulów
, ale ponieważ masz ogromną witrynę, na wyższych poziomach znajdują się dziesiątki milionów artykułów i musisz fizycznie rozdzielić je na wiele maszyn.
Gdybyśmy mieli użyć zwykłej liczby całkowitej jako id
(klucz główny) z ustawieniem autoinkrementacji, bardzo szybko okazałoby się, że podczas tworzenia rekordów na różnych maszynach w sposób zdecentralizowany, a następnie ich synchronizacji, dochodzi do kolizji identyfikatorów i musimy w skomplikowany sposób przenumerować rekordy. Dodatkowo, jeśli rozwiązujemy wiele sesji do innych tabel, może to być bardzo skomplikowane i łatwo popełnić błąd.
Dlatego zamiast identyfikatora numerycznego można wygenerować UUID
, czyli ciąg tekstowy generowany przez złożony algorytm, który gwarantuje, że będzie on unikalny, nawet jeśli zostanie wygenerowany niezależnie na wielu maszynach.
Zalety:
klucz główny
przed faktycznym wstawieniem rekordu do bazy danych. Zmniejsza to liczbę zapytań SQL, upraszcza logikę transakcji i pozwala łatwo użyć go jako klucza obcego, zanim kolekcja rekordów będzie istnieć.19010018
, łatwo się domyślić, że istnieje również użytkownik 19010017
i inni. Atak ten nazywany jest atakiem wektorowym.UUID można uzyskać albo za pomocą prostego zapytania SQL SELECT UUID();
, ale zwiększa to liczbę zapytań do bazy danych i tracimy możliwość przygotowania danych najpierw hurtowo w logice aplikacji, a następnie zapisania ich od razu.
Dlatego chętnie korzystam z pakietu ramsey/uuid uzyskanego przez Composera jako dobrego rozwiązania. Sam identyfikator UUID ma kilka wersji, a pakiet może dowolnie generować ich rodzaje.
Dzięki temu jest łatwy w użyciu:
require 'vendor/autoload.php';use Ramsey\Uuid\Uuid;// Generuje obiekt UUID wersji 1 (oparty na czasie)$uuid1 = Uuid::uuid1();echo $uuid1->toString() . "\n"; // e4eaaaf2-d142-11e1-b3e4-080027620cdd// Generuje obiekt UUID w wersji 3 (oparty na nazwie i haszowany jako MD5)$uuid3 = Uuid::uuid3(Uuid::NAMESPACE_DNS, 'php.net');echo $uuid3->toString() . "\n"; // 11a38b9a-b3da-360f-9353-a5a725514269// Generuje obiekt UUID w wersji 4 (losowy)$uuid4 = Uuid::uuid4();echo $uuid4->toString() . "\n"; // 25769c6c-d34d-4bfe-ba98-e0ee856f3e7a// Generuje obiekt UUID w wersji 5 (oparty na nazwie i haszowany jako SHA1)$uuid5 = Uuid::uuid5(Uuid::NAMESPACE_DNS, 'php.net');echo $uuid5->toString() . "\n"; // c4a760a8-dbcf-5254-a0d9-6a4474bd1b62
Jeśli korzystasz z Doctrine, istnieje rozszerzenie ramsey/uuid-doctrine, które generuje identyfikator bezpośrednio jako typ danych.
W moich pierwszych próbach użyłem varchar(36)
jako klucza głównego (ID), ale to wcale nie jest dobry pomysł.
Objaśnienie logiki wewnętrznej:.
Bazy danych MySql (i wiele innych) nie potrafią efektywnie używać
varchar
,char
lub innych typów danych wyrażających ciąg znaków jako klucza głównego. W niektórych bazach danych istnieje typ danychGUID
, który jest przeznaczony do bezpośredniego przechowywania identyfikatorów UUID. Jeśli nie można użyć tego typu, istnieje odpowiedni zamiennik w postacibinary(16)
.
Podczas fizycznego sprawdzania bazy danych identyfikator jest wtedy reprezentowany w formacie HEX (ponieważ nie można wyświetlić formatu binarnego), zamiast ładnego identyfikatora 726c67c4-e5eb-4a4c-8fcc-031da5d6f3c6
, zobaczysz po prostu 726C67C4E5EB4A4C8FCC031DA5D6F3C6
, co wygląda jak '?kYߟKg2c;'
w zapytaniu INSERT.
varchar(36)
na binary(16)
.Zakładam, że reprezentujesz (lub planujesz reprezentować) nowo ustawiony identyfikator w bazie danych jako:
`id` binary(16) NOT NULL
Jednak sama zmiana typu danych nie działa, dlatego należy zastosować coś takiego:
SET FOREIGN_KEY_CHECKS=0;ALTER TABLE article CHANGE id id BINARY(16) NOT NULLSET FOREIGN_KEY_CHECKS=1;
Istnieją zasadniczo dwa powody:
muszą mieć ten sam typ danych
. Dlatego należy zmienić zarówno typ danych dla identyfikatora artykułu, jak i na przykład w tabeli relacyjnej, która dopasowuje artykuły do autorów.Dlatego jedynym właściwym rozwiązaniem jest utworzenie kopii zapasowej danych (ale i tak należy to robić przed każdą migracją), przygotowanie pustej bazy danych z funkcjonalnymi relacjami i ponowne umieszczenie w niej danych za pomocą migracji.
Jeśli identyfikatory UUID zostały wcześniej wygenerowane w dziwny sposób, lepiej jest wybrać jakąś sekwencyjną metodę uzyskiwania identyfikatora UUID i przenumerować wszystkie rekordy. Powodem jest to, że układ sekwencyjny pozwala na lepsze uporządkowanie wartości i utworzenie drzewa
, co sprawia, że wydajność jest prawie identyczna jak w przypadku bigint
.
Jeśli znasz lepszy sposób na konwersję istniejącej bazy danych z UUID przechowywanych w formacie varchar do formatu binarnego bez konieczności opracowywania skomplikowanych migracji i z zachowaniem kluczy obcych, byłbym bardzo wdzięczny za informacje zwrotne.
Jan Barášek Více o autorovi
Autor článku pracuje jako seniorní vývojář a software architekt v Praze. Navrhuje a spravuje velké webové aplikace, které znáte a používáte. Od roku 2009 nabral bohaté zkušenosti, které tímto webem předává dál.
Rád vám pomůžu:
Články píše Jan Barášek © 2009-2024 | Kontakt | Mapa webu
Status | Aktualizováno: ... | pl