Optimieren von Abfragen für das nächste und vorheriges Element

stimmen
28

Ich bin auf der Suche nach dem besten Weg, um die nächsten und vorherigen Aufzeichnungen eines Datensatzes zurück, ohne eine vollständige Abfrage ausgeführt wird. Ich habe eine vollständig implementierte Lösung an Ort und Stelle, und würde gerne wissen, ob es eine bessere Ansätze sind dies dort tun werden.

Lassen Sie sich sagen, dass wir eine Website für einen fiktiven Gemüsehändler bauen. Neben seiner HTML-Seiten, jede Woche, will er eine Liste von Sonderangeboten auf seiner Website veröffentlichen. Er will, dass diese Angebote in einer aktuellen Datenbank-Tabelle befindet, und die Benutzer müssen in der Lage, die Angebote auf drei Arten zu sortieren.

Jeder Einzelteil hat auch mit mehr, textlichen Informationen über das Angebot und „Zurück“ und „Weiter“ , um eine Detailseite haben. Die „Zurück“ und „next“ Tasten müssen die benachbarten Einträge hinweisen , in Abhängigkeit von der Sortier der Benutzer für die Liste gewählt hatte .

Alt-Text http://www.pekkagaiser.com/stuff/Sort.gif?

Offensichtlich ist die Schaltfläche „Weiter“ für „Tomaten, Klasse I“ hat „Äpfel, Klasse 1“ im ersten Beispiel, „Birnen, Klasse I“ in den zweiten, und keiner in den dritten sein.

Die Aufgabe in der Detailansicht ist , die nächsten und vorherigen Elemente zu bestimmen , ohne eine Abfrage jedes Mal ausgeführt wird , mit der Sortierreihenfolge der Liste als die einzige verfügbare Informationen (Lassen Sie uns sagen wir , dass durch einen GET - Parameter erhalten ?sort=offeroftheweek_price, und die Auswirkungen auf die Sicherheit ignorieren) .

Offensichtlich einfach vorbei die IDs der nächsten und vorherigen Elemente als Parameter ist die erste Lösung, die den Sinn kommt. Schließlich wissen wir bereits die ID des an dieser Stelle. Aber dies ist keine Option hier - es wäre in diesem vereinfachten Beispiel arbeiten, aber nicht in vielen meinen realen Anwendungsfall Welt.

Mein aktueller Ansatz in meinem CMS ist mit etwas , das ich genannt habe „Sortier - Cache“. Wenn eine Liste geladen wird, speichere ich die Artikelpositionen in Datensätzen in einer benannten Tabelle sortingcache.

name (VARCHAR)             items (TEXT)

offeroftheweek_unsorted    Lettuce; Tomatoes; Apples I; Apples II; Pears
offeroftheweek_price       Tomatoes;Pears;Apples I; Apples II; Lettuce
offeroftheweek_class_asc   Apples II;Lettuce;Apples;Pears;Tomatoes

offensichtlich, das itemsist Spalte tatsächlich mit numerischen IDs gefüllt.

In der Detailseite, greife ich jetzt den entsprechenden sortingcacheDatensatz, holt die itemsSpalt, explodieren sie, für die aktuelle Artikel - ID suchen, und den vorherigen und nächsten Nachbarn zurückzukehren.

array(current   => Tomatoes,
      next      => Pears,
      previous  => null
      );

Das ist natürlich teuer, arbeitet für eine begrenzte Anzahl von Datensatz nur und schafft redundante Daten, aber sie , dass in der realen Welt übernehmen, die Abfrage die Listen zu erstellen ist sehr teuer (es ist), ist es in jeder Detailansicht läuft aus die Frage, und einige Caching benötigt.

Meine Fragen:

  • Glauben Sie, das ist eine gute Praxis ist die benachbarte Datensätze für verschiedene Abfrage Aufträge, um herauszufinden?

  • Wissen Sie, bessere Praktiken in Bezug auf Leistung und Einfachheit? Wissen Sie etwas, das diese völlig veraltet macht?

  • Bei der Programmierung der Theorie ist es ein Name für dieses Problem?

  • Ist der Name „Sortierung Cache“ ist angemessen und verständlich für diese Technik?

  • Gibt es anerkannt, gemeinsame Muster, dieses Problem zu lösen? Wie werden sie genannt?

Hinweis: Meine Frage geht es nicht um den Aufbau der Liste, oder wie in der Detailansicht angezeigt werden soll . Das sind nur Beispiele. Meine Frage ist die grundlegende Funktionalität der Nachbarn eines Datensatzes festzustellen , wann eine erneute Abfrage nicht möglich ist, und die schnellste und billigste Weg , um dorthin zu gelangen.

Wenn etwas unklar ist, lassen Sie einen Kommentar und ich werde klären.

Starten einer Prämie - vielleicht gibt es ein paar mehr Infos zu diesem Thema gibt.

Veröffentlicht am 22/02/2010 um 12:06
quelle vom benutzer
In anderen Sprachen...                            


11 antworten

stimmen
-3

Sie haben also zwei Aufgaben:

  1. Aufbau sortierte Liste von Elementen (SELECTs mit unterschiedlichen ORDER BY)
  2. Details anzeigen zu jedem Elemente (SELECT Details aus der Datenbank mit möglichem Caching).

Was ist das Problem?

PS: wenn geordnete Liste zu groß sein können, brauchen Sie nur PAGER Funktionalität implementiert. Es könnten verschiedene Implementierungen, zB Sie „LIMIT 5“ hinzufügen in Abfrage kann wünschen und „Show nächsten 5“ -Taste zur Verfügung stellen. Wenn diese Taste gedrückt wird, Zustand wie „WHERE Preis <0,89 LIMIT 5“ hinzugefügt.

Beantwortet am 22/02/2010 um 15:04
quelle vom benutzer

stimmen
16

Hier ist eine Idee. Sie könnten die teueren Operationen zu einem Update abzuladen, wenn die Lebensmittelhändler Einsätze / neue Angebote aktualisiert statt, wenn der Endbenutzer wählt die Daten anzuzeigen. Das mag wie eine nicht-dynamische Weise scheint die Art Daten zu handhaben, aber es kann die Geschwindigkeit erhöhen. Und, wie wir wissen, gibt es immer einen Kompromiss zwischen Leistung und anderen Codierungs Faktoren.

Erstellen Sie eine Tabelle für jedes Angebot und jede Sortieroption Vor und Zurück zu halten. (Alternativ können Sie dieses in der Angebotstabelle speichern können, wenn Sie immer drei Sortieroptionen haben - Abfragegeschwindigkeit ist ein guter Grund, um Ihre Datenbank zu denormalize)

So würden Sie diese Spalten haben:

  • Nach Typ sortieren (unsortiert, Preis, Klasse und Preis Desc)
  • Offer ID
  • Zurück ID
  • Next ID

Wenn die Detailinformationen für das Angebot Detailseite wird aus der Datenbank abgefragt, würde die NextID und PrevID Teil der Ergebnisse sein. Also würden Sie brauchen nur eine Abfrage für jede Detailseite.

Jedes Mal, wenn ein Angebot eingefügt, aktualisiert oder gelöscht werden, würden Sie brauchen einen Prozess auszuführen, die die Integrität / Genauigkeit der sorttype Tabelle validiert.

Beantwortet am 22/02/2010 um 20:20
quelle vom benutzer

stimmen
1

Ich bin mir nicht sicher, ob ich verstand rechts, also, wenn nicht, sagen Sie mir nur,)

Lassen Sie uns sagen, dass die givens die Abfrage für die sortierte Liste und der Strom in dieser Liste versetzt sind, also haben wir ein $queryund ein $n.

Eine sehr offensichtliche Lösung, die Abfragen zu minimieren, wäre sofort alle Daten zu holen:

list($prev, $current, $next) = DB::q($query . ' LIMIT ?i, 3', $n - 1)->fetchAll(PDO::FETCH_NUM);

Diese Aussage holt die früheren, aktuellen und die nächsten Elemente aus der Datenbank in der aktuellen Sortierreihenfolge und stellt die zugehörigen Informationen in die entsprechenden Variablen.

Aber da diese Lösung zu einfach ist, ich nehme an, ich etwas falsch verstanden.

Beantwortet am 07/02/2011 um 20:31
quelle vom benutzer

stimmen
2

Ich habe Alpträume und mit diesem einen hatte. Ihr aktueller Ansatz scheint die beste Lösung auch für Listen von 10k Elementen zu sein. Das Caching die IDs der Listenansicht in der HTTP - Sitzung und dann , dass die Verwendung für die Anzeige (personalisiert den aktuellen Benutzer) vorherigen / nächsten. Das funktioniert gut , vor allem , wenn es zu viele Möglichkeiten gibt , die ursprüngliche Liste der Elemente statt nur 3 zu filtern und zu sortieren ,
auch durch die ganze IDs Liste speichern Sie angezeigt bekommen "you are at X out of Y"Nutzbarkeit zu verbessern Text.
JIRA des vorherigen / nächsten

By the way, ist es das , was JIRA als auch der Fall ist.

Um direkt Ihre Fragen zu beantworten:

  • Ja, es ist eine gute Praxis, weil es ohne zusätzliche Codekomplexität skaliert, wenn der Filter / Sortierung und Elementtypen komplexer krähen. Ich verwende es in einem Produktionssystem mit 250k Artikeln mit „unendlich“ Filter / sortieren Variationen. Beschneiden der zwischenspeicherbar IDs bis 1000 ist auch eine Möglichkeit, da der Benutzer höchstwahrscheinlich nie zurück klicken oder neben mehr als 500-mal (Er wird höchstwahrscheinlich zurückgehen und die Suche verfeinern oder Paginieren).
  • Ich weiß nicht, einen besseren Weg. Wenn aber die Art, wo beschränkt und dies war eine öffentliche Stelle (ohne http-Sitzung), dann würde ich wahrscheinlich denormalize.
  • Keine Ahnung.
  • Ja, Sortierung Cache klingt gut. In meinem Projekt nenne ich es „vorherige / nächste auf den Suchergebnissen“ oder „Navigation in den Suchergebnissen“.
  • Keine Ahnung.
Beantwortet am 07/02/2011 um 21:04
quelle vom benutzer

stimmen
2

Im Allgemeinen denormalize ich die Daten aus dem Indizes. Sie können in den gleichen Zeilen gespeichert werden, aber ich fast immer mein Ergebnis IDs abrufen, dann für die Daten eine separate Reise. Dies macht die Daten zwischenspeichern sehr einfach. Es ist nicht so wichtig, in PHP, wo die Latenz gering und die Bandbreite hoch ist, aber eine solche Strategie ist sehr nützlich, wenn Sie eine hohe Latenz, geringe Bandbreite Anwendung, wie beispielsweise eine AJAX-Website, wo ein großer Teil der Website in JavaScript gerendert wird.

I-Cache immer die Listen der Ergebnisse und die Ergebnisse selbst getrennt. Wenn etwas die Ergebnisse einer Listenabfrage betroffen sind, ist der Cache der Liste Ergebnisse aktualisiert. Wenn etwas die Ergebnisse selbst betrifft, sind diese speziellen Ergebnisse aktualisiert. Dies ermöglicht es mir entweder ein zu aktualisieren, ohne alles zu regenerieren, in effektivem Caching führt.

alle Listen zur gleichen Zeit, da meine Listen selten der Ergebnisse, erzeuge ich ändern. Dies kann die erste Reaktion macht etwas langsamer, aber es vereinfacht Cache-Aktualisierung (alle Listen in einem einzigen Cache-Eintrag gespeichert werden).

Weil ich die gesamte Liste gecached habe, ist es trivial benachbarte Elemente zu finden, ohne die Datenbank erneuten Besuch. Mit etwas Glück werden auch die Daten für diese Elemente zwischengespeichert werden. Dies ist besonders praktisch, wenn Daten in JavaScript zu sortieren. Wenn ich bereits eine Kopie im Cache auf dem Client, kann ich sofort zurückgreifen.

Zur Beantwortung Ihrer Fragen im Einzelnen:

  • Ja, es ist eine fantastische Idee, die Nachbarn vor der Zeit, um herauszufinden, oder was auch immer Informationen der Kunde wahrscheinlich neben Zugang, vor allem, wenn die Kosten jetzt niedrig ist und die Kosten neu zu berechnen ist hoch. Dann ist es einfach ein Trade-off von zusätzlicher Vorkalkulation und Lagerung gegenüber der Geschwindigkeit.
  • In Bezug auf Leistung und Einfachheit vermeiden zusammen binden Dinge, die logisch verschiedene Dinge sind. Indizes und Daten unterschiedlich sind, sind wahrscheinlich zu unterschiedlichen Zeiten geändert werden soll (zB ein neues Datum hinzugefügt werden Auswirkungen auf die Indizes, aber nicht die vorhandenen Daten) und somit separat zugegriffen werden. Dies kann etwas weniger effizient aus einer Single-Thread-Sicht sein, aber jedes Mal, wenn Sie etwas zusammen zu binden, verlieren Sie die Caching Wirksamkeit und asychronosity (der Schlüssel zur Skalierung ist asychronosity).
  • Der Begriff für Abrufen von Daten vor der Zeit ist Prefetching. Prefetching kann zum Zeitpunkt des Zugriffs oder im Hintergrund geschehen, aber bevor die vorge abgerufenen Daten tatsächlich benötigt wird. Ebenfalls mit Vorkalkulation. Es ist ein Abwägen von Kosten jetzt, Lagerkosten und Kosten zu erhalten, wenn nötig.
  • „Sortieren von Cache“ ist ein passender Name.
  • Ich weiß es nicht.

Auch wenn Sie Dinge zwischenzuspeichern, zwischenzuspeichern sie am allgemeinsten Ebene möglich. Einige Sachen können benutzerspezifische (zB Ergebnisse für eine Suchanfrage) sein, wo andere vielleicht Benutzer Agnostiker, wie gerade ein Katalog sein. Beide können vom Caching profitieren. Der Katalog Abfrage kann häufig sein und ein wenig jedes Mal speichern, und die Suchabfrage teuer sein kann und eine Menge ein paar Mal speichern.

Beantwortet am 09/02/2011 um 08:00
quelle vom benutzer

stimmen
0

Es gibt so viele Möglichkeiten, die sprichwörtlich Katze dies als die Haut zu tun. Also hier sind ein paar von mir.

Wenn Ihre ursprüngliche Abfrage teuer ist, was Sie sagen, es ist, erstellen Sie dann eine andere Tabelle möglicherweise eine Speichertabelle mit den Ergebnissen Ihres teuren bevölkert und selten Haupt-Abfrage ausführen.

Diese zweite Tabelle könnte dann auf jeder Ansicht abgefragt werden und die Sortierung ist so einfach wie die entsprechende Sortierreihenfolge einstellen.

Je nach Bedarf wird repopulate die zweite Tabelle mit den Ergebnissen aus der ersten Tabelle, so dass die Daten frisch, hält aber die Verwendung der teuren Abfrage minimieren.

Alternativ Wenn Sie auch an die DB eine Verbindung vermeiden möchten, dann können Sie alle Daten in einer PHP-Array speichern und speichern Sie es Memcached verwenden. Dies würde sehr schnell sein und zur Verfügung gestellt Ihre Listen würden ressourceneffiziente nicht zu groß waren. und kann leicht sortiert werden.

DC

Beantwortet am 11/02/2011 um 05:19
quelle vom benutzer

stimmen
0

Grundannahmen:

  • Angebote werden wöchentlich
  • Wir können die Website erwarten selten zu ändern ... wahrscheinlich täglich?
  • Wir können Updates steuern auf die Datenbank mit Ether eine API oder über Trigger reagieren

Wenn die Website auf einer täglichen Basis ändert, schlage ich vor, dass alle Seiten werden über Nacht statisch erzeugt. Eine Abfrage für jede Art Ordnung durchläuft und macht alle verknüpften Seiten. Auch wenn es dynamische Elemente, stehen die Chancen, dass Sie sie, indem Sie die statischen Seitenelemente ansprechen können. Dies würde eine optimale Seite Service und keine Datenbank laden. In der Tat könnte man möglicherweise einzelne Seiten und prev / next Elemente erzeugen, die in den Seiten enthalten sind. Dies kann verrücktere mit 200 Möglichkeiten, um zu sortieren, aber mit 3 Ich bin ein großer Fan von ihm.

?sort=price
include(/sorts/$sort/tomatoes_class_1)
/*tomatoes_class_1 is probably a numeric id; sanitize your sort key... use numerics?*/

Wenn aus irgendeinem Grund dies nicht möglich ist, würde ich auf Auswendiglernen greifen. Memcache ist beliebt für diese Art der Sache (das Wortspiel!). Wenn etwas in die Datenbank gedrückt wird, können Sie einen Trigger-Ausgabe des Cache mit den korrekten Werten zu aktualisieren. Tun Sie dies auf die gleiche Weise würden Sie, wenn als ob Ihr aktualisierte Element in 3 verkettete Listen bestanden - relink gegebenenfalls (this.next.prev = this.prev, etc). Von diesen, solange Ihr Cache nicht überfüllt, werden Sie in einem Primärschlüssel Art und Weise einfache Werte aus dem Speicher gezogen werden.

Dieses Verfahren dauert einige zusätzliche Codierung auf der Auswahl und Aktualisierung / Insert Methoden, aber es sollte ziemlich minimal sein. Am Ende werden Sie am suchen [id of tomatoes class 1].price.next. Wenn dieser Schlüssel im Cache ist, golden. Falls nicht, legen Sie in den Cache und Display.

  • Glauben Sie , das ist eine gute Praxis ist die benachbarte Datensätze für verschiedene Abfrage Aufträge , um herauszufinden? Ja. Es ist ratsam , Look-aheads auf erwartete kommenden Anfragen auszuführen.
  • Wissen Sie , bessere Praktiken in Bezug auf Leistung und Einfachheit? Wissen Sie etwas , das diese völlig veraltet macht? Hoffentlich wird die oben
  • Bei der Programmierung der Theorie ist es ein Name für dieses Problem? Optimierung?
  • Ist der Name „Sortierung Cache“ ist angemessen und verständlich für diese Technik? Ich bin von einem bestimmten entsprechenden Namen nicht sicher. Es ist Caching, es ist ein Cache der Art ist, aber ich bin nicht sicher , dass ich sagen Sie eine „Sortier Cache“ haben würde sofort Verständnis vermitteln.
  • Gibt es anerkannt, gemeinsame Muster , dieses Problem zu lösen? Wie werden sie genannt? Caching?

Leider meine Tailing Antworten sind so eine Art nutzlos, aber ich glaube, meine Erzählung Lösungen sollten sehr nützlich sein.

Beantwortet am 11/02/2011 um 18:13
quelle vom benutzer

stimmen
0

Sie könnten die speichern Zeilennummern der geordneten Listen in Aussicht , und man konnte die vorherigen und nächsten Elemente in der Liste unter (current_rownum-1) und (current_rownum + 1) Zeilennummern erreichen.

Beantwortet am 12/02/2011 um 14:01
quelle vom benutzer

stimmen
0

Das Problem / datastructur wird bidirektionaler Graph benannt oder man könnte sagen, dass Sie mehrere verkettete Listen haben.

Wenn Sie es als eine verknüpfte Liste denken, könnten Sie nur Felder für jede Sortier- und prev / next Schlüssel zum Artikel Tisch. Aber die DB Person wird Sie dafür töten, es ist wie GOTO.

Wenn Sie es als (bi) gerichteten Graphen denken, gehen Sie mit Jessicas Antwort. Das Hauptproblem ist, dass, um Updates teuere Operationen sind.

 Item Next Prev
   A   B     -
   B   C     A
   C   D     B
   ...

Wenn Sie einen Artikel Position A in die neue Reihenfolge ändern, C, B, D, werden Sie 4 Zeilen aktualisieren.

Beantwortet am 13/02/2011 um 02:20
quelle vom benutzer

stimmen
4

Ich habe eine Idee etwas ähnlich wie Jessicas. Anstatt jedoch Links zu dem nächsten und vorherigen Sortier Artikel zu speichern, speichern Sie die Sortierreihenfolge für jede Art Typen. Um die vorherige oder nächste Datensatz zu finden, nur um die Zeile mit SortX = currentSort ++ oder SortX = currentSort--.

Beispiel:

Type     Class Price Sort1  Sort2 Sort3
Lettuce  2     0.89  0      4     0
Tomatoes 1     1.50  1      0     4
Apples   1     1.10  2      2     2
Apples   2     0.95  3      3     1
Pears    1     1.25  4      1     3

Diese Lösung würde ergibt sehr kurze Abfragezeiten und weniger Speicherplatz als Jessicas Idee nehmen würde. Aber, wie ich bin sicher, dass Sie erkennen, die Kosten für eine Reihe von Daten zu aktualisieren sind deutlich höher, da Sie müssen neu berechnet werden und alle Sortierreihenfolgen zu speichern. Aber noch, je nach Ihrer Situation, wenn Daten-Updates sind selten und vor allem, wenn sie immer in der Masse passieren, dann könnte diese Lösung die beste sein.

dh

once_per_day
  add/delete/update all records
  recalculate sort orders

Hoffe, das ist nützlich.

Beantwortet am 13/02/2011 um 03:30
quelle vom benutzer

stimmen
0

Entschuldigt, wenn ich falsch verstanden, aber ich denke, Sie die geordnete Liste beibehalten möchten zwischen Benutzer auf den Server zugreift. Wenn ja, kann auch Ihre Antwort liegt in Ihrer Caching-Strategie und Technologien, anstatt in Datenbankabfrage / schema-Optimierung.

Mein Ansatz wäre, zu serialisieren (), um das Array einmal seinen ersten abgerufen und dann in diesen Cache in einen separaten Speicherbereich; ob das / APC Memcached / Festplatte / MongoDB / usw. und seinem Cache Standortdetails für jeden Benutzer einzeln durch ihre Sitzungsdaten behalten. Der tatsächliche Speicher-Backend wäre natürlich von der Größe des Arrays abhängig sein, die Sie nicht gehen sehr ins Detail über, aber Memcached Skalen groß über mehrere Server und Mongo noch weiter zu einer etwas größeren Latenz Kosten.

Sie zeigen auch nicht, wie viel Art Permutationen es in der realen Welt ist; zB haben Sie separate Listen pro Benutzer cachen müssen, oder können Sie global Cache pro Art Permutation und dann herauszufiltern, was Sie über PHP nicht brauchen ?. Im Beispiel Sie geben, würde ich einfach beiden Permutationen und Speicher-Cache, die von den beiden I () in den Sitzungsdaten unserialize benötigt.

Wenn der Benutzer auf die Website zurückkehrt, überprüfen Sie die Amortisierungszeit der zwischengespeicherten Daten zu leben und wiederverwenden es, wenn nach wie vor gültig. Ich würde auch einen Trigger läuft auf INSERT IGNORE / UPDATE / DELETE für die speziellen Angebote, die einfach ein Zeitstempel-Feld in einer separaten Tabelle setzt. Dies würde sofort an, ob der Cache war trocken und die Abfrage zu re-run für einen sehr niedrigen Abfragekosten benötigt. Die große Sache über nur den Auslöser mit einem einzigen Feld zu setzen ist, dass es keine Notwendigkeit, über Beschneiden alt / redundante Werte aus dieser Tabelle zu kümmern.

Ob dies geeignet wäre von der Größe der Daten abhängen zurückgegeben werden, wie häufig es wurde geändert, und welche Caching-Technologien sind auf dem Server zur Verfügung.

Beantwortet am 13/02/2011 um 15:47
quelle vom benutzer

Cookies help us deliver our services. By using our services, you agree to our use of cookies. Learn more