Ich habe mich gefragt, wie man am besten ein Tag-System implementiert, wie es auf SO verwendet wird. Ich habe darüber nachgedacht, aber ich kann keine gute skalierbare Lösung finden.
Ich dachte an eine grundlegende 3-Tisch-Lösung: einen tags
Tisch, articles
einen tag_to_articles
Tisch und einen Tisch.
Ist dies die beste Lösung für dieses Problem oder gibt es Alternativen? Mit dieser Methode würde die Tabelle mit der Zeit extrem groß werden, und für die Suche ist dies vermutlich nicht zu effizient. Andererseits ist es nicht so wichtig, dass die Abfrage schnell ausgeführt wird.
Antworten:
Ich glaube, Sie werden diesen Blog-Beitrag interessant finden: Tags: Datenbankschemata
"MySQLicious" -Lösung
In dieser Lösung hat das Schema nur eine Tabelle, es wird denormalisiert. Dieser Typ wird als "MySQLicious-Lösung" bezeichnet, da MySQLicious del.icio.us-Daten in eine Tabelle mit dieser Struktur importiert.
Schnittpunktabfrage (UND) für "Suche + Webservice + Semweb":
Union (OR) Abfrage nach "search | webservice | semweb":
Minus-Abfrage für "Suche + Webservice-Semweb"
"Scuttle" -Lösung
Scuttle organisiert seine Daten in zwei Tabellen. Diese Tabelle "scCategories" ist die "Tag" -Tabelle und hat einen Fremdschlüssel für die "Lesezeichen" -Tabelle.
Schnittpunktabfrage (UND) für "Lesezeichen + Webservice + Semweb":
Zuerst werden alle Lesezeichen-Tag-Kombinationen durchsucht, wobei das Tag "Lesezeichen", "Webservice" oder "Semweb" ist (c.Kategorie IN ("Lesezeichen", "Webservice", "Semweb")), dann nur die Lesezeichen, die Alle drei Tags, nach denen gesucht wurde, werden berücksichtigt (HAVING COUNT (b.bId) = 3).
Union (OR) Abfrage für "bookmark | webservice | semweb": Lassen Sie einfach die HAVING-Klausel weg und Sie haben union:
Minus (Ausschluss) Abfrage für "Lesezeichen + Webservice-Semweb", dh: Lesezeichen UND Webservice UND NICHT Semweb.
Das Weglassen des HAVING COUNT führt zur Abfrage für "bookmark | webservice-semweb".
"Toxi" -Lösung
Toxi entwickelte eine Struktur mit drei Tischen. Über die Tabelle "Tagmap" sind die Lesezeichen und die Tags n-zu-m-bezogen. Jedes Tag kann zusammen mit verschiedenen Lesezeichen verwendet werden und umgekehrt. Dieses DB-Schema wird auch von WordPress verwendet. Die Abfragen sind die gleichen wie bei der "scuttle" -Lösung.
Schnittpunktabfrage (UND) für "Lesezeichen + Webservice + Semweb"
Union (OR) Abfrage für "Lesezeichen | Webservice | Semweb"
Minus (Ausschluss) Abfrage für "Lesezeichen + Webservice-Semweb", dh: Lesezeichen UND Webservice UND NICHT Semweb.
Das Weglassen des HAVING COUNT führt zur Abfrage für "bookmark | webservice-semweb".
quelle
An Ihrer Drei-Tabellen-Lösung ist nichts auszusetzen.
Eine andere Möglichkeit besteht darin, die Anzahl der Tags zu begrenzen, die auf einen Artikel angewendet werden können (z. B. 5 in SO), und diese direkt zu Ihrer Artikeltabelle hinzuzufügen.
Das Normalisieren der Datenbank hat Vor- und Nachteile, genau wie das Festverdrahten von Dingen in einer Tabelle Vor- und Nachteile hat.
Nichts sagt, dass Sie nicht beides können. Es widerspricht relationalen DB-Paradigmen, Informationen zu wiederholen, aber wenn das Ziel Leistung ist, müssen Sie möglicherweise die Paradigmen brechen.
quelle
Ihre vorgeschlagene Implementierung mit drei Tabellen funktioniert für das Tagging.
Der Stapelüberlauf verwendet jedoch eine andere Implementierung. Sie speichern Tags in der varchar-Spalte in der Posts-Tabelle im Klartext und verwenden die Volltextindizierung, um Posts abzurufen, die mit den Tags übereinstimmen. Zum Beispiel
posts.tags = "algorithm system tagging best-practices"
. Ich bin sicher, dass Jeff dies irgendwo erwähnt hat, aber ich vergesse wo.quelle
Die vorgeschlagene Lösung ist die beste - wenn nicht die einzig praktikable - Möglichkeit, die viele-zu-viele-Beziehung zwischen Tags und Artikeln anzugehen. Ich stimme also für "Ja, es ist immer noch das Beste". Ich würde mich allerdings für Alternativen interessieren.
quelle
Wenn Ihre Datenbank indizierbare Arrays unterstützt (wie z. B. PostgreSQL), würde ich eine vollständig denormalisierte Lösung empfehlen: Speichern Sie Tags als Array von Zeichenfolgen in derselben Tabelle. Wenn nicht, ist eine sekundäre Tabelle, die Objekte Tags zuordnet, die beste Lösung. Wenn Sie zusätzliche Informationen für Tags speichern müssen, können Sie eine separate Tags-Tabelle verwenden. Es macht jedoch keinen Sinn, für jede Tag-Suche einen zweiten Join einzuführen.
quelle
Ich möchte optimiertes MySQLicious für eine bessere Leistung vorschlagen. Davor sind die Nachteile der Toxi-Lösung (3 Tabellen)
Wenn Sie Millionen von Fragen haben und jeweils 5 Tags enthalten, enthält die Tagmap-Tabelle 5 Millionen Einträge. Also müssen wir zuerst 10 Tausend Tagmap-Einträge basierend auf der Tag-Suche herausfiltern und dann wieder übereinstimmende Fragen dieser 10 Tausend herausfiltern. Wenn Sie also herausfiltern, ob die künstliche ID einfach numerisch ist, ist dies in Ordnung. Wenn es sich jedoch um eine Art UUID (32 varchar) handelt, muss herausgefiltert werden, obwohl sie indiziert ist.
Meine Lösung:
Wenn ein neues Tag erstellt wird, haben Sie den Zähler ++ (Basis 10) und konvertieren Sie diesen Zähler in base64. Jetzt hat jeder Tag-Name die Base64-ID. und übergeben Sie diese ID zusammen mit dem Namen an die Benutzeroberfläche. Auf diese Weise haben Sie maximal zwei Zeichen, bis 4095 Tags in unserem System erstellt wurden. Verketten Sie nun diese mehreren Tags in jede Fragetabellen-Tag-Spalte. Fügen Sie auch ein Trennzeichen hinzu und sortieren Sie es.
Der Tisch sieht also so aus
Fragen Sie beim Abfragen die ID anstelle des echten Tag-Namens ab. Da es SORTIERT ist , ist die
and
Bedingung auf dem Tag effizienter (LIKE '%|a|%|c|%|f|%
).Beachten Sie, dass Leerzeichen Trennzeichen ist nicht genug , und wir brauchen doppelte Trennzeichen zu unterscheiden Tags wie
sql
undmysql
daLIKE "%sql%"
kehrtmysql
Ergebnisse als gut. Sollte seinLIKE "%|sql|%"
Ich weiß, dass die Suche nicht indiziert ist, aber Sie haben möglicherweise andere Spalten indiziert, die sich auf Artikel wie author / dateTime beziehen. Andernfalls führt dies zu einem vollständigen Tabellenscan.
Schließlich ist bei dieser Lösung kein innerer Join erforderlich, bei dem Millionen Datensätze mit 5 Millionen Datensätzen unter Join-Bedingungen verglichen werden müssen.
quelle
Anmerkungen:
AUTO_INCREMENT
PK (weil es keine benötigt) . Daher ist es besser als Scuttle.LIKE
mit führendem Platzhalter ; falsche Treffer auf Teilzeichenfolgen)Verwandte Diskussionen (für MySQL):
viele: viele geordnete Listen zur Optimierung der Zuordnungstabelle
quelle