2010-09-17 15 views
11

Welches ist der beste Primärschlüssel zum Speichern von Website-Adressen und Seiten-URLs?Der beste Primärschlüssel zum Speichern von URLs

Um die Verwendung der autoincremental ID zu vermeiden (die nicht wirklich an die Daten gebunden ist), entwarf ich das Schema mit der Verwendung einer SHA1-Signatur der URL als Primärschlüssel.

Dieser Ansatz ist in vielerlei Hinsicht nützlich: Ich brauche zum Beispiel die last_id nicht aus der Datenbank zu lesen, damit ich alle Tabellenaktualisierungen vorbereiten kann, um den Schlüssel zu berechnen und die eigentliche Aktualisierung in einer einzigen Transaktion durchzuführen. Keine Einschränkung der Beschränkung

Jedenfalls lese ich zwei Bücher, die mir sagen, dass ich falsch liege. In "Hochleistungs-MySQL" wird gesagt, dass der Zufallsschlüssel nicht gut für den DB-Optimierer ist. Außerdem sagt Joe Celko in seinen Büchern, dass der Primärschlüssel ein Teil der Daten sein sollte.

Die Frage ist: die natürlichen Schlüssel für URLs sind ... URLs selbst. Tatsache ist, dass für eine Website, die kurz ist (www.something.com), keine Beschränkung für eine URL besteht (siehe http://www.boutell.com/newfaq/misc/urllength.html).

Betrachten Sie, ich muss einige Millionen von ihnen speichern (und arbeiten).

Welches ist dann der beste Schlüssel? Autoincremental-IDs, URLs, Hashwerte von URLs?

+1

Ich denke, es hängt viel davon ab, was Sie sonst noch mit diesen URLs, Zugangsmustern usw. tun. Die Verwendung von SHA1 sollte vor Kollisionen sicher sein, wo eine kürzere Hash-Funktion (zB CRC32) offensichtlich unpassend wäre. aber Kollisionen sind immer noch möglich, Sie hätten nur Pech. –

Antwort

15

Sie benötigen einen numerischen Primärschlüssel mit automatischer Inkrementierung. Für die Zeiten, in denen Sie IDs weitergeben oder sich mit anderen Tabellen verbinden müssen (z. B. optionale Attribute für eine URL), benötigen Sie etwas Kleines und Numerisches.

Was für andere Spalten und Indizes Sie wollen, hängt wie immer davon ab, wie Sie sie verwenden werden.

Eine Spalte, die einen Hash jeder URL speichert, ist eine ausgezeichnete Idee für fast jede Anwendung, die eine beträchtliche Anzahl von URLs verwendet. Es macht das Auswählen einer URL mit ihrem vollständigen Text so schnell wie möglich. Ein zweiter Vorteil besteht darin, dass Sie, wenn Sie diese Spalte als UNIQUE definieren, keine Bedenken haben müssen, dass die Spalte, die die tatsächliche URL speichert, eindeutig ist und Sie REPLACE INTO und INSERT IGNORE als einfache, schnelle atomare Schreiboperationen verwenden können.

Ich würde hinzufügen, dass die Verwendung der integrierten MD5() - Funktion von MySQL für diesen Zweck gut ist. Der einzige Nachteil ist, dass ein dedizierter Angreifer Kollisionen erzwingen kann, was ich ziemlich sicher nicht interessiert. Die Verwendung der integrierten Funktion erleichtert beispielsweise einige Join-Arten erheblich. Es kann ein kleines bisschen langsamer sein, eine vollständige URL über die Leitung zu übergeben ("SELECT url FROM URLs WHERE hash = MD5 ('verylongurl')" anstelle von "WHERE hash = '32charhexstring'", aber Sie haben die Option um das zu tun, wenn du willst. Wenn Sie sich nicht ein konkretes Szenario ausdenken, in dem MD5() Sie im Stich lässt, können Sie es verwenden.

Die schwierige Frage ist, ob und wie Sie URLs auf andere Weise als ihren Volltext suchen müssen: zum Beispiel möchten Sie alle URLs finden, die mit "/ foo" auf jedem "Balken" beginnen. com "Gastgeber? Während "LIKE '% bar.com%/foo%'" im Test funktioniert, wird es kläglich scheitern. Wenn Ihre Anforderungen solche Dinge beinhalten, können Sie kreative Wege finden, um nicht-eindeutige Indizes zu erzeugen, die auf die Art von Daten ausgerichtet sind, die Sie brauchen ... vielleicht eine Domain-Name-Spalte für Anfänger. Sie müssen diese Spalten fast sicher aus Ihrer Anwendung auffüllen (Trigger und Stored Procedures sind viel problematischer, als sie hier wert sind, besonders wenn Sie sich um die Leistung sorgen - nicht stören).

Die gute Nachricht ist, dass relationale Datenbanken für solche Dinge sehr flexibel sind. Sie können jederzeit neue Spalten hinzufügen und sie später auffüllen. Ich würde für Anfänger vorschlagen: int unsigned auto_increment Primärschlüssel, eindeutige Hash-Char (32) und (unter der Annahme, dass 64K Zeichen genügt) Text-URL.

+0

+1 - es gibt ernsthafte Auswirkungen auf die Leistung, wenn Sie breitere primrary-Schlüssel haben, die vom SQL-Team gut dokumentiert und von den meisten Entwicklern meist ignoriert werden. – TomTom

+0

Warum Hashes als Hexadezimale anstelle von Dezimalzahlen speichern? –

1

Hängt davon ab, wie Sie die Tabelle verwenden. Wenn Sie meistens mit WHERE url='<url>' auswählen, ist es in Ordnung, eine einspaltige Tabelle zu haben. Wenn Sie eine Autoinkrement-ID verwenden können, um eine URL an allen Orten in Ihrer App zu identifizieren, verwenden Sie die Autoinkremente

2

Vermutlich sprechen Sie über eine vollständige URL, nicht nur einen Hostnamen, einschließlich CGI-Parameter und andere Sachen.

SHA-1 Hashing die URLs macht alle Schlüssel lang, und macht das Sortieren Schwierigkeiten ziemlich unklar. Ich musste einmal Indizes für Hashes verwenden, um einige vertrauliche Daten zu verschleiern, während ich die Fähigkeit beibehalten konnte, zwei Tabellen zu verbinden, und die Leistung war schlecht.

Es gibt zwei mögliche Ansätze. Einer ist der naive und offensichtliche; es wird tatsächlich gut in mySQL funktionieren. Es hat Vorteile wie Einfachheit und die Möglichkeit, URL LIKE 'whatever%' zu verwenden, um effizient zu suchen.

Aber wenn Sie in ein paar Domains konzentriert viele URLs haben ... zum Beispiel ....

http://stackoverflow.com/questions/3735390/best-primary-key-for-storing-urls 
http://stackoverflow.com/questions/3735391/how-to-add-a-c-compiler-flag-to-extconf-rb 

etc, sind Sie bei Indizes suchen, die erst in den letzten Zeichen variieren. In diesem Fall könnten Sie die URLs mit umgekehrter Zeichenreihenfolge speichern und indexieren. Dies kann zu einem Index mit einem effizienteren Zugriff führen.

(Der Oracle-Tabelle-Server-Produkt geschieht über einen eingebauten hat mit einer so genannten umgekehrten Index in Art und Weise, dies zu tun.)

Wenn ich Sie wäre, würde ich einen autoincrement Schlüssel vermeiden, wenn Sie mehr als zwei verbinden haben Tabellen ON TABLE_A.URL = TABLE_B.URL oder eine andere Join-Bedingung mit dieser Art von Meaing.

+1

Eine Möglichkeit, die Leistung von Joins auf Hashes zu verbessern, besteht darin, eine zweite indizierte Spalte mit einer "konzentrierteren" Version der Hash-Daten hinzuzufügen. Ein BIGINT mit den ersten 64 Bits eines MD5 kann effizienter indiziert werden als ein CHAR (32). Kollisionen werden zig Mal häufiger auftreten, was extrem selten ist. Ihr WHERE kann in beiden Spalten beitreten ("WHERE t1.inthash = t2.inthash UND t1.charhash = t2.charshash") und im extrem seltenen Fall einer BIGINT-Kollision wird der vollständige Hash sicherstellen, dass Sie immer noch die richtige Antwort erhalten. –

Verwandte Themen