Wenn die Größe der Datenbank Millionen von Zeilen überschreitet, ist es ratsam, die Anwendung zu skalieren und die Datenbank auf mehrere physische Server aufzuteilen.
Das größte Problem bei der Aufteilung der Datenbank in mehrere Teile ist die anschließende Synchronisierung, wenn der Benutzer bestimmte Daten anfordert.
Nehmen wir an, Sie haben eine Tabelle mit "Artikeln", aber da Sie eine riesige Website haben, gibt es mehrere Millionen Artikel und Sie müssen sie physisch auf mehrere Rechner verteilen.
Wenn wir eine gewöhnliche Ganzzahl als "ID" (Primärschlüssel) mit der Einstellung "Autoinkrement" verwenden würden, würden wir sehr schnell feststellen, dass es bei der dezentralen Erstellung von Datensätzen auf verschiedenen Rechnern und ihrer anschließenden Synchronisierung zu ID-Kollisionen kommt und wir die Datensätze auf komplizierte Weise neu nummerieren müssen. Wenn wir außerdem viele Sitzungen in andere Tabellen auflösen, kann dies ein sehr komplexer Overhead sein, bei dem man leicht Fehler machen kann.
Daher können wir anstelle eines numerischen Bezeichners eine "UUID" generieren, eine Textzeichenkette, die durch einen komplexen Algorithmus erzeugt wird, der garantiert, dass sie eindeutig ist, selbst wenn sie unabhängig auf mehreren Rechnern erzeugt wird.
Vorteile:
19010018
bin, ist es leicht zu erraten, dass der Benutzer 19010017
und andere auch existieren. Der Angriff wird als Vektorangriff bezeichnet.Die UUID kann entweder durch eine einfache SQL-Abfrage SELECT UUID();
ermittelt werden, aber dadurch erhöht sich die Anzahl der Abfragen an die Datenbank und wir verlieren die Möglichkeit, die Daten zunächst in der Anwendungslogik in großen Mengen vorzubereiten und sie dann auf einmal zu schreiben.
Daher verwende ich gerne das ramsey/uuid-Paket von Composer als gute Lösung. Die UUID selbst hat mehrere Versionen, und das Paket kann spielerisch alle Arten nach Bedarf erzeugen.
Das macht die Nutzung einfach:
require 'vendor/autoload.php';use Ramsey\Uuid\Uuid;// Erzeugt UUID-Objekt der Version 1 (zeitbasiert)$uuid1 = Uuid::uuid1();echo $uuid1->toString() . "\n"; // e4eaaaf2-d142-11e1-b3e4-080027620cdd// Erzeugt UUID-Objekt der Version 3 (namensbasiert und als MD5 gehasht)$uuid3 = Uuid::uuid3(Uuid::NAMESPACE_DNS, 'php.net');echo $uuid3->toString() . "\n"; // 11a38b9a-b3da-360f-9353-a5a725514269// Erzeugt ein UUID-Objekt der Version 4 (zufällig)$uuid4 = Uuid::uuid4();echo $uuid4->toString() . "\n"; // 25769c6c-d34d-4bfe-ba98-e0ee856f3e7a// Erzeugt UUID-Objekt der Version 5 (namensbasiert und als SHA1 gehasht)$uuid5 = Uuid::uuid5(Uuid::NAMESPACE_DNS, 'php.net');echo $uuid5->toString() . "\n"; // c4a760a8-dbcf-5254-a0d9-6a4474bd1b62
Wenn Sie Doctrine verwenden, gibt es eine Erweiterung ramsey/uuid-doctrine, die die ID direkt als Datentyp erzeugt.
In meinen ersten Versuchen habe ich varchar(36)
als Primärschlüssel (ID) verwendet, aber das ist überhaupt keine gute Idee.
Erläuterung der internen Logik:
MySql-Datenbanken (und viele andere) können
varchar
,char
oder andere Datentypen, die eine Zeichenkette ausdrücken, nicht effizient als Primärschlüssel verwenden. In einigen Datenbanken gibt es einen Datentyp "GUID", der für die direkte Speicherung von UUIDs vorgesehen ist. Wenn Sie diesen Typ nicht verwenden können, gibt es einen geeigneten Ersatz in der Formbinary(16)
.
Bei der physischen Untersuchung der Datenbank wird die ID dann im HEX-Format dargestellt (da das Binärformat nicht angezeigt werden kann), statt der schönen ID 726c67c4-e5eb-4a4c-8fcc-031da5d6f3c6
sehen Sie nur 726C67C4E5EB4A4C8FCC031DA5D6F3C6
, was in der INSERT-Abfrage wie '?kYߟKg2c;'
aussieht.
Ich nehme an, dass Sie die neu eingestellte ID in der Datenbank als darstellen (oder darstellen wollen):
`id` binary(16) NOT NULL
Allerdings funktioniert eine einfache Änderung des Datentyps nicht, also etwas wie:
SET FOREIGN_KEY_CHECKS=0;ALTER TABLE article CHANGE id id BINARY(16) NOT NULLSET FOREIGN_KEY_CHECKS=1;
Dafür gibt es im Wesentlichen zwei Gründe:
Die einzig richtige Lösung besteht daher darin, die Daten zu sichern (was Sie aber ohnehin vor jeder Migration tun sollten), eine leere Datenbank mit funktionalen Beziehungen vorzubereiten und die Daten per Migration wieder dorthin zu übertragen.
Wenn Sie zuvor UUIDs auf seltsame Weise erzeugt haben, ist es besser, eine sequenzielle Methode zu wählen, um die UUID zu erhalten und alle Datensätze neu zu nummerieren. Der Grund dafür ist, dass ein sequentielles Layout eine bessere Anordnung der Werte und die Erstellung eines Baums
ermöglicht, was die Leistung fast identisch mit Bigint
macht.
Wenn Sie eine bessere Möglichkeit kennen, eine bestehende Datenbank von UUID, die als varchar gespeichert ist, in ein binäres Format zu konvertieren, ohne komplexe Migrationen durchführen zu müssen und unter Beibehaltung der Fremdschlüssel, wäre ich Ihnen für eine Rückmeldung sehr dankbar.
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: ... | de