2009-01-17 12 views
9

Ich versuche, eine optimale Lösung für das folgende Problem zu finden: Es besteht die Notwendigkeit, eine Datenbank (Postgres-basierte), das System der Trigger und Zähler in ihm zu entwerfen bildet ein System zum effizienten Abfragen, Aktualisieren und Speichern von Informationen darüber, "wie viele ungelesene Kommentare in jedem Artikel (oder Blogeintrag oder ähnlichem) vorhanden sind, der auf der Seite angezeigt wird".Implementieren ein effizientes System von "ungelesenen Kommentaren" Zähler

Jede Lösung, die an den Kopf kommt, hat einige schwerwiegende Nachteile, entweder beim Abfragen oder beim Speichern oder Aktualisieren. I.e. es benötigt zu viel Speicher oder zu viele Updates oder zu teure Abfragen.

Was ist mit Ihrer Erfahrung? Vielleicht gibt es eine bereits gebildete nette Lösung für diese Art von Problemen?

Antwort

8

Ich würde das Schema so einfach wie möglich halten, so Abfrage wird so einfach wie möglich sein. Dies hat normalerweise auch die geringsten Speicheranforderungen. Natürlich setzen Sie Indizes, um diese Abfrage zu unterstützen.

Nächster Schritt: Messen Sie die Leistung! "Messen ist Wissen." Wie ist die Reaktionszeit? Was ist die Last auf dem Server? Solange die Leistung akzeptabel ist, halten Sie das Schema und die Abfrage einfach. Opfern Sie die Wartbarkeit nicht, wenn es nicht unbedingt notwendig ist: Ihre Nachfolger werden es Ihnen später danken.

Wenn die Leistung wirklich ein Problem ist, sehen Sie sich die Caching-Funktionalität des Frameworks an, das Sie für Ihre Anwendung verwenden. Das Ausführen einer Abfrage ist immer schneller als das Ausführen einer optimierten Abfrage.

4

Wenn Sie wirklich nicht in Ihrer Ressource Umschlag erfolgreich sind, müssen Sie möglicherweise die Benutzerfreundlichkeit optimieren. Vielleicht reicht es, das Datum des letzten Zugriffs auf einen Thread zu speichern.

4

Ich glaube nicht, dass der typische, normalisierte Ansatz Sie mit ineffizienten Abfragen verlassen würde. Angenommen, Sie haben eine Tabelle article_comments mit PK (article_id, comment_id) und eine andere Tabelle comments_seen_by_user mit PK (user_id, article_id, comment_id). Alles, was Sie tun müssen, ist, für jeden Artikel auf der Seite aufgelistet:

SELECT count(*) FROM article_comments ac 
WHERE article_id = ?    -- Parameter 
AND NOT EXISTS (
    SELECT 1 FROM comments_seen_by_user csbu 
    WHERE csbu.user_id = ?   -- Parameter 
    AND csbu.article_id = ac.article_id 
    AND csbu.comment_id = ac.comment_id 
) 

Wenn Sie 20 Artikel auf einer Seite zeigen, werden Sie die obige Abfrage 20 mal laufen und laufen jeweils einen Index verwenden, ziehen out sagen 10-20 Zeilen von article_comments, und der Unterabfrage-Test ist nur ein weiterer Index-Scan auf comments_seen_by_user, so alles in allem haben Sie vielleicht 20 * (20 * 2) = 800 indizierte Suchvorgänge ausführen, um eine bestimmte Seite anzuzeigen. Für eine moderne DB ist das kein Problem. Und ich übersehe wahrscheinlich noch bessere Abfragepläne, die PostgreSQL finden könnte.

Haben Sie dies versucht, und Leistung gefunden wollen? Wenn ja, meine erste Vermutung wäre, dass Sie in einer Weile nicht VACUUM Ed haben. Ansonsten muss ich meine Schätzungen für die Anzahl der Artikel pro Seite oder Kommentare pro Artikel falsch haben - bitte aktualisieren Sie in diesem Fall mit weiteren Details.

1

Ich werde zweite Antwort von j_random_hacker, nur würde ich vermeiden, die article_id in der Tabelle "comments_seen_by_user" zu speichern, da die Kommentar-ID global eindeutig für jeden Kommentar sein sollte. Auch 3-dimensionale (und 2-d in geringerem Maße) Indizes sind in PostgreSQL immer noch langsam, versuchen Sie also, sie zu vermeiden.

Es gibt keinen wirklich guten Weg um eine Tabelle von user_id, comment_id Werten zu speichern, um die Informationen über gelesene Kommentare zu speichern, stellen Sie einfach sicher, dass es einen eindeutigen Index hat. Ein paar 10 Millionen Zeilen in einer solchen Tabelle sind für PostgreSQL überhaupt kein Problem, solange sie den Index im Speicher halten können.Sie können mit Anfragen an Systemtabellen Spur der Indexgröße (Anzahl der 8-KB-Seiten auf der Platte) halten:

select relname,relpages from pg_class where relname='comments_seen_by_user_pkey'; 
+1

Vereinbarte, weltweit einzigartige comment_ids sind eine gute Idee. –

0

Ich würde für einen normierten Ansatz gehen zustimmen und sehen, ob es klappt ist. Normalerweise sollte ich. Sie könnten jedoch auch einen INSERT-Trigger für die Tabelle 'comment' verwenden, die einen Kommentarzähler in der Basistabelle (d. H. Artikel) aktualisiert. Es hängt vom Nutzungsprofil für diese Website ab: Wenn Kommentare hauptsächlich gelesen werden (verglichen mit dem Hinzufügen von Kommentaren), sollte sich der Overhead eines triggerbasierten Ansatzes schnell amortisieren. Wenn es sich um eine Website mit einer hohen Kommentarlast handelt, kann dies die Leistung beeinträchtigen.

Ich würde für eine einfache, normalisierte Tabellenstruktur gehen und andere Optimierung später hinzufügen, wenn Sie ein vernünftiges Nutzungsprofil haben.

+0

Ihr Trigger müsste nUsers-Zeilen in einer Tabelle mit (user_id, article_id) (oder einer Variante) als PK aktualisieren, da der Verlauf der Kommentaranzeige jedes Benutzers unabhängig ist. Immer noch machbar. –