Frage

Ich habe mich gefragt, wie es am besten ist, ein Tag -System zu implementieren, wie das, auf dem sie verwendet wurden. Ich habe darüber nachgedacht, aber ich kann keine gute skalierbare Lösung finden.

Ich dachte darüber nach, eine grundlegende 3 -Table -Lösung zu haben: a tags Tabelle, an articles Tische und a tag_to_articles Tisch.

Ist dies die beste Lösung für dieses Problem oder gibt es Alternativen? Mit dieser Methode würde die Tabelle rechtzeitig extrem groß werden, und für die Suche ist ich nicht allzu effizient. Andererseits ist es nicht so wichtig, dass die Abfrage schnell ausgeführt wird.

War es hilfreich?

Lösung

Ich glaube, Sie werden diesen Blog -Beitrag interessant finden: Tags: Datenbankschemata

Das Problem: Sie möchten ein Datenbankschema haben, in dem Sie ein Lesezeichen (oder einen Blog -Beitrag oder was auch immer) mit so vielen Tags markieren können, wie Sie möchten. Später möchten Sie Anfragen ausführen, um die Lesezeichen auf eine Vereinigung oder eine Schnittstelle von Tags zu beschränken. Sie möchten auch einige Tags aus dem Suchergebnis ausschließen (z. B.: minus) ausschließen.

"MySqlicious" -Lösung

In dieser Lösung hat das Schema nur einen Tisch, es wird denormalisiert. Dieser Typ wird als „MySQLicious -Lösung“ bezeichnet, da MySQLicious del.icio.us Daten in eine Tabelle mit dieser Struktur importiert.

enter image description hereenter image description here

Intersektion (und) Abfrage für "Suche+Webservice+Semweb":

SELECT *
FROM `delicious`
WHERE tags LIKE "%search%"
AND tags LIKE "%webservice%"
AND tags LIKE "%semweb%"

Union (oder) Abfrage für "Suche | WebService | Semweb":

SELECT *
FROM `delicious`
WHERE tags LIKE "%search%"
OR tags LIKE "%webservice%"
OR tags LIKE "%semweb%"

Minus-Abfrage für "Suchen+WebService-Semweb"

SELECT *
FROM `delicious`
WHERE tags LIKE "%search%"
AND tags LIKE "%webservice%"
AND tags NOT LIKE "%semweb%"

"Scuttle" -Lösung

Schutt organisiert seine Daten in zwei Tabellen. Diese Tabelle "sccategories" ist das "Tag" -Table und hat einen fremden Schlüssel zum "Lesezeichen" -Table.

enter image description here

Kreuzung (und) Abfrage für "Lesezeichen+WebService+Semweb":

SELECT b.*
FROM scBookmarks b, scCategories c
WHERE c.bId = b.bId
AND (c.category IN ('bookmark', 'webservice', 'semweb'))
GROUP BY b.bId
HAVING COUNT( b.bId )=3

Zuerst werden alle Lesezeichen-Tag-Kombinationen durchsucht, wobei das Tag "Lesezeichen", "WebService" oder "Semweb" (C.Category in ("Lesezeichen", "WebService", "Semweb") ist, dann nur die Lesezeichen, die die Lesezeichen, die Haben Sie alle drei Tags durchsucht, nach denen berücksichtigt wird (zählt (B.bid) = 3).

Union (oder) Abfrage für „Lesezeichen | WebService | Semweb“:Lassen Sie einfach die Klausel aus und Sie haben Union:

SELECT b.*
FROM scBookmarks b, scCategories c
WHERE c.bId = b.bId
AND (c.category IN ('bookmark', 'webservice', 'semweb'))
GROUP BY b.bId

Minus (Ausschluss) Abfrage für „Lesezeichen+WebService-Semweb“, dh Lesezeichen und Webservice und nicht Semweb.

SELECT b. *
FROM scBookmarks b, scCategories c
WHERE b.bId = c.bId
AND (c.category IN ('bookmark', 'webservice'))
AND b.bId NOT
IN (SELECT b.bId FROM scBookmarks b, scCategories c WHERE b.bId = c.bId AND c.category = 'semweb')
GROUP BY b.bId
HAVING COUNT( b.bId ) =2

Auslassen, dass die Anzahl der Zähler zur Abfrage für „Lesezeichen | WebService-Semweb“ führt.


"Toxi" -Lösung

Toxi kam mit einer Drei-Tisch-Struktur. Über die Tabelle "Tagmap" sind die Lesezeichen und die Tags mit N-zu-M verwandt. Jedes Tag kann zusammen mit verschiedenen Lesezeichen verwendet werden und umgekehrt. Dieses DB-Schema wird auch von WordPress verwendet. Die Abfragen sind genau die gleichen wie in der Lösung „Scuttle“.

enter image description here

Kreuzung (und) Abfrage für "Lesezeichen+WebService+Semweb"

SELECT b.*
FROM tagmap bt, bookmark b, tag t
WHERE bt.tag_id = t.tag_id
AND (t.name IN ('bookmark', 'webservice', 'semweb'))
AND b.id = bt.bookmark_id
GROUP BY b.id
HAVING COUNT( b.id )=3

Union (oder) Abfrage für "Lesezeichen | WebService | Semweb"

SELECT b.*
FROM tagmap bt, bookmark b, tag t
WHERE bt.tag_id = t.tag_id
AND (t.name IN ('bookmark', 'webservice', 'semweb'))
AND b.id = bt.bookmark_id
GROUP BY b.id

Minus (Ausschluss) Abfrage für „Lesezeichen+WebService-Semweb“, dh Lesezeichen und Webservice und nicht Semweb.

SELECT b. *
FROM bookmark b, tagmap bt, tag t
WHERE b.id = bt.bookmark_id
AND bt.tag_id = t.tag_id
AND (t.name IN ('Programming', 'Algorithms'))
AND b.id NOT IN (SELECT b.id FROM bookmark b, tagmap bt, tag t WHERE b.id = bt.bookmark_id AND bt.tag_id = t.tag_id AND t.name = 'Python')
GROUP BY b.id
HAVING COUNT( b.id ) =2

Auslassen, dass die Anzahl der Zähler zur Abfrage für „Lesezeichen | WebService-Semweb“ führt.

Andere Tipps

Mit Ihrer Drei-Tab-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 (wie 5 in SO) und diese direkt zu Ihrer Artikeltabelle hinzufügen.

Die Normalisierung des DB hat seine Vorteile und Nachteile, genau wie die hart umkassenden Dinge in einem Tisch Vorteile und Nachteile.

Nichts sagt, dass man beides nicht tun kann. Es verstößt gegen relationale DB -Paradigmen, um Informationen zu wiederholen. Wenn das Ziel jedoch die Leistung ist, müssen Sie möglicherweise die Paradigmen brechen.

Ihre vorgeschlagene Implementierung von drei Tabellen funktioniert für das Markieren.

Stack Overflow verwendet jedoch unterschiedliche Implementierung. Sie speichern Tags in der Varchar -Spalte in der Post -Tabelle im Klartext und verwenden die Volltextindexierung, um Beiträge abzuholen, die den Tags entsprechen. Zum Beispiel posts.tags = "algorithm system tagging best-practices". Ich bin sicher, dass Jeff das irgendwo erwähnt hat, aber ich vergesse wo.

Die vorgeschlagene Lösung ist die beste-wenn nicht die einzig praktikable, die ich mir vorstellen kann, um die viel-zu-Viele-Beziehung zwischen Tags und Artikeln anzugehen. Meine Stimme ist also für "Ja, es ist immer noch das Beste." Ich würde mich jedoch für Alternativen interessieren.

Wenn Ihre Datenbank indexbare Arrays unterstützt (z. B. nach PostgreSQL), würde ich eine vollständig denormalisierte Lösung empfehlen - speichern Tags als Array von Zeichenfolgen in derselben Tabelle. Wenn nicht, ist ein sekundärer Tabellenzuordnungsobjekte zu Tags die beste Lösung. Wenn Sie zusätzliche Informationen gegen Tags speichern müssen, können Sie eine separate Tags -Tabelle verwenden. Es macht jedoch keinen Sinn, einen zweiten Join für jede Tag -Suche vorzustellen.

Ich möchte vorschlagen, mySQlicious für eine bessere Leistung optimiert zu werden. Vorher sind die Nachteile der Toxi -Lösung (3 Tabelle)

Wenn Sie Millionen von Fragen haben und jeweils 5 Tags enthält, werden in der Tagmap -Tabelle 5 Millionen Einträge enthalten. Zuerst müssen wir zuerst 10.000 Tagmap -Einträge basierend auf der Tagsuche herausfiltern und dann erneut übereinstimmende Fragen dieser 10.000 herausfiltern. Wenn Sie also herausfiltern, wenn die künstliche ID einfach numerisch ist, ist es in Ordnung, aber wenn es sich um eine Art UUID (32 VARCHAR) handelt, benötigt es einen größeren Vergleich, obwohl sie indiziert ist.

Meine Lösung:

Wenn ein neues Tag erstellt wird, haben Sie Zähler ++ (Basis 10) und konvertieren Sie diesen Gegenstand in Base64. Jetzt hat jeder Tag -Name Base64 ID. und übertragen Sie diese ID zusammen mit dem Namen an die Benutzeroberfläche. Auf diese Weise haben Sie maximal zwei Zeichen, bis wir 4095 Tags in unserem System erstellt haben. Verketten Sie nun diese Mehrfach -Tags in jede Spalte für Frage -Tabellen -Tags. Fügen Sie auch Trennzeichen hinzu und lassen Sie ihn sortiert.

Also sieht der Tisch so aus

enter image description here

Fragen Sie beim Abfragen auf der ID anstelle des echten Tag -Namens. Seit es ist Sortiert, and Bedingung auf dem Tag ist effizienter (LIKE '%|a|%|c|%|f|%).

Beachten Sie, dass der Einzelraum -Trennzeichen nicht ausreicht und wir einen doppelten Trennzeichen benötigen, um Tags wie zu unterscheiden sql und mysql Weil LIKE "%sql%" wird zurückkehren mysql Ergebnisse auch. Sollte sein LIKE "%|sql|%"

Ich weiß, dass die Suche nicht indiziert ist, aber Sie haben möglicherweise immer noch in anderen Spalten im Zusammenhang mit dem Artikel wie Author/DateTime an anderer Hinweise zum vollständigen Tabellen -Scan führen.

Mit dieser Lösung müssen schließlich kein innerer Join erforderlich sein, wenn Millionen Datensätze mit 5 Millionen Rekorde unter Join -Bedingung verglichen werden müssen.

CREATE TABLE Tags (
    tag VARHAR(...) NOT NULL,
    bid INT ... NOT NULL,
    PRIMARY KEY(tag, bid),
    INDEX(bid, tag)
)

Anmerkungen:

  • Dies ist besser als Toxi, da es nicht extra viele durchläuft: viele Tabellen, die die Optimierung erschweren.
  • Sicher, mein Ansatz ist aufgrund der redundanten Tags möglicherweise etwas sperriger (als Toxi), aber das ist ein kleiner Prozentsatz der ganz Datenbank und die Leistungsverbesserungen können signifikant sein.
  • Es ist sehr skalierbar.
  • Es gibt keinen Ersatz (weil es nicht braucht) AUTO_INCREMENT Pk. Daher ist es besser als Scuttle.
  • Mysqlicious ist scheiße, weil es keinen Index verwenden kann (LIKE mit führend Wildkarte; falsche Hits auf Substrings)
  • Verwenden Sie für MySQL unbedingt Engine = InnoDB, um "Clustering" -Effekte zu erhalten.

Verwandte Diskussionen (für MySQL):
Viele: Viele Mapping -Tabellenoptimierung
Bestellte Listen

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top