{"id":75530,"date":"2020-03-26T19:42:23","date_gmt":"2020-03-26T17:42:23","guid":{"rendered":"https:\/\/prohoster.info\/blog\/administrirovanie\/vyvod-rezultatov-poiska-i-problemy-s-proizvoditelnostyu"},"modified":"2020-03-26T19:42:23","modified_gmt":"2020-03-26T17:42:23","slug":"vyvod-rezultatov-poiska-i-problemy-s-proizvoditelnostyu","status":"publish","type":"post","link":"https:\/\/prohoster.info\/de\/blog\/administrirovanie\/vyvod-rezultatov-poiska-i-problemy-s-proizvoditelnostyu","title":{"rendered":"Ausgabe der Suchergebnisse und Leistungsprobleme","gt_translate_keys":[{"key":"rendered","format":"text"}]},"content":{"rendered":"<p>Eine der typischen Szenarien in allen g\u00e4ngigen Anwendungen ist die Datenabfrage nach bestimmten Kriterien und die Ausgabe in einem leserfreundlichen Format. Hier k\u00f6nnen zus\u00e4tzliche Funktionen wie Sortierung, Gruppierung und Seitenaufteilung hinzukommen. Theoretisch ist die Aufgabe trivial, aber viele Entwickler machen bei der Umsetzung eine Reihe von Fehlern, die die Leistung sp\u00e4ter beeintr\u00e4chtigen. Lassen Sie uns verschiedene L\u00f6sungsans\u00e4tze betrachten und Empfehlungen f\u00fcr die Auswahl der effizientesten Implementierung formulieren.<\/p>\n<p><img decoding=\"async\" alt=\"Ausgabe der Suchergebnisse und Leistungsprobleme\" src=\"\/wp-content\/uploads\/2020\/03\/364ab29c4a6117ff441b933a38ec8401.jpg\" style=\"display:block;margin: 0 auto;\" \/><br \/>\n<noindex><a rel=\"nofollow\" name=\"habracut\"><\/a><\/noindex><\/p>\n<h2>Seitenpaging-Option #1<\/h2>\n<p>\nDie naheliegendste und einfachste Option ist die klassische Seite-f\u00fcr-Seite-Anzeige der Suchergebnisse.<\/p>\n<p><img decoding=\"async\" alt=\"Ausgabe der Suchergebnisse und Leistungsprobleme\" src=\"\/wp-content\/uploads\/2020\/03\/4957d9ad21a8a0c70390b224fe76cb33.jpg\" style=\"display:block;margin: 0 auto;\" \/><br \/>\nAngenommen, im Anwendung wird eine relationale Datenbank verwendet. In diesem Fall m\u00fcssen zwei SQL-Abfragen ausgef\u00fchrt werden, um die Informationen in dieser Form darzustellen:<\/p>\n<ul>\n<li>Holen Sie sich die Zeilen f\u00fcr die aktuelle Seite.<\/li>\n<li>Z\u00e4hlen Sie die Gesamtanzahl der Zeilen, die den Suchkriterien entsprechen \u2014 dies ist notwendig, um die Seiten anzuzeigen.<\/li>\n<\/ul>\n<p>\nLassen Sie uns die erste Abfrage am Beispiel der Testdatenbank MS SQL betrachten <noindex><a rel=\"nofollow\" href=\"https:\/\/github.com\/Microsoft\/sql-server-samples\/releases\/download\/adventureworks\/AdventureWorks2016_EXT.bak\">AdventureWorks <\/a><\/noindex>f\u00fcr den Server 2016. Zu diesem Zweck verwenden wir die Tabelle Sales.SalesOrderHeader:<\/p>\n<pre><code class=\"sql\">SELECT * FROM Sales.SalesOrderHeader\nORDER BY OrderDate DESC\nOFFSET 0 ROWS\nFETCH NEXT 50 ROWS ONLY\n<\/code><\/pre>\n<p>\nDie obige Abfrage gibt die ersten 50 Bestellungen aus einer Liste zur\u00fcck, die nach dem Datum der Hinzuf\u00fcgung in absteigender Reihenfolge sortiert ist \u2013 das hei\u00dft, die letzten 50 Bestellungen.<\/p>\n<p>Sie wird schnell auf der Testdatenbank ausgef\u00fchrt, aber lassen Sie uns den Ausf\u00fchrungsplan und die Ein- und Ausgabestatistiken betrachten:<\/p>\n<p><img decoding=\"async\" alt=\"Ausgabe der Suchergebnisse und Leistungsprobleme\" src=\"\/wp-content\/uploads\/2020\/03\/248e4b64593c7000216b46777183d639.jpg\" style=\"display:block;margin: 0 auto;\" \/><\/p>\n<pre><code class=\"plaintext\">Tabelle 'SalesOrderHeader'. Scan-Anzahl 1, logische Lesevorg\u00e4nge 698, physische Lesevorg\u00e4nge 0, Vorab-Lesevorg\u00e4nge 0, lob logische Lesevorg\u00e4nge 0, lob physische Lesevorg\u00e4nge 0, lob Vorab-Lesevorg\u00e4nge 0.<\/code><\/pre>\n<p>\n<i>Um die Ein- und Ausgabestatistiken f\u00fcr jede Abfrage zu erhalten, f\u00fchren Sie in der Abfrageumgebung den Befehl SET STATISTICS IO ON aus.<\/i><\/p>\n<p>Wie aus dem Ausf\u00fchrungsplan ersichtlich ist, ist die Sortierung aller Zeilen der Ausgangstabelle nach dem Hinzuf\u00fcgungsdatum die ressourcenintensivste Operation. Das Problem ist, dass die Sortierung umso 'aufw\u00e4ndiger' wird, je mehr Zeilen in der Tabelle vorhanden sind. Solche Situationen sollten in der Praxis vermieden werden, deshalb f\u00fcgen wir einen Index auf dem Hinzuf\u00fcgungsdatum hinzu und pr\u00fcfen, ob sich der Ressourcenverbrauch ge\u00e4ndert hat:<\/p>\n<p><img decoding=\"async\" alt=\"Ausgabe der Suchergebnisse und Leistungsprobleme\" src=\"\/wp-content\/uploads\/2020\/03\/a33e1eadf6f8f8b516b9bb834ad7ad86.jpg\" style=\"display:block;margin: 0 auto;\" \/><\/p>\n<pre><code class=\"plaintext\">Tabelle 'SalesOrderHeader'. Scan-Anzahl 1, logische Lesevorg\u00e4nge 165, physische Lesevorg\u00e4nge 0, Vorab-Lesevorg\u00e4nge 5, lob logische Lesevorg\u00e4nge 0, lob physische Lesevorg\u00e4nge 0, lob Vorab-Lesevorg\u00e4nge 0.\n<\/code><\/pre>\n<p>\nOffensichtlich hat sich die Situation deutlich verbessert. Aber sind alle Probleme gel\u00f6st? Lassen Sie uns die Anfrage \u00e4ndern, um Bestellungen zu suchen, bei denen der Gesamtwert der Waren 100 Dollar \u00fcberschreitet:<\/p>\n<pre><code class=\"sql\">SELECT * FROM Sales.SalesOrderHeader\nWHERE SubTotal &gt; 100\nORDER BY OrderDate DESC\nOFFSET 0 ROWS\nFETCH NEXT 50 ROWS ONLY\n<\/code><\/pre>\n<p>\n<img decoding=\"async\" alt=\"Ausgabe der Suchergebnisse und Leistungsprobleme\" src=\"\/wp-content\/uploads\/2020\/03\/e057bfc89e11a31c6d2855b93ec3c747.jpg\" style=\"display:block;margin: 0 auto;\" \/><\/p>\n<pre><code class=\"plaintext\">Tabelle 'SalesOrderHeader'. Scan-Anzahl 1, logische Leseoperationen 1081, physische Leseoperationen 0, Vorauslesen 0, lob logische Leseoperationen 0, lob physische Leseoperationen 0, lob Vorauslesen 0.<\/code><\/pre>\n<p>\nWir haben eine interessante Situation: Der Abfrageplan ist nicht viel schlechter als der vorherige, aber die tats\u00e4chliche Anzahl an logischen Lesevorg\u00e4ngen ist fast doppelt so hoch wie bei einem vollst\u00e4ndigen Tabellenscan. Es gibt einen Ausweg \u2014 wenn wir den bestehenden Index zu einem zusammengesetzten machen und das Gesamtpreisfeld als zweites Feld hinzuf\u00fcgen, erhalten wir wieder 165 logische Lesevorg\u00e4nge:<\/p>\n<pre><code class=\"sql\">CREATE INDEX IX_SalesOrderHeader_OrderDate_SubTotal on Sales.SalesOrderHeader(OrderDate, SubTotal);\n<\/code><\/pre>\n<p>\nDiese Beispielreihe k\u00f6nnte noch lange fortgesetzt werden, aber zwei Hauptgedanken, die ich hier zum Ausdruck bringen m\u00f6chte, sind folgende:<\/p>\n<ul>\n<li>Die Hinzuf\u00fcgung eines neuen Kriteriums oder einer Sortierreihenfolge in die Abfrage kann die Ausf\u00fchrungsgeschwindigkeit erheblich beeinflussen.<\/li>\n<li>Wenn wir jedoch nur einen Teil der Daten abrufen m\u00fcssen und nicht alle Ergebnisse, die den Suchkriterien entsprechen, gibt es viele M\u00f6glichkeiten, eine solche Anfrage zu optimieren.<\/li>\n<\/ul>\n<p>\nLassen Sie uns zum zweiten Abfrageanliegen \u00fcbergehen, das zu Beginn erw\u00e4hnt wurde \u2013 n\u00e4mlich zu dem, das die Anzahl der Datens\u00e4tze z\u00e4hlt, die dem Suchkriterium entsprechen. Nehmen wir dasselbe Beispiel \u2013 die Suche nach Bestellungen, die mehr als 100 Dollar kosten:<\/p>\n<pre><code class=\"sql\">SELECT COUNT(1) FROM Sales.SalesOrderHeader\nWHERE SubTotal &gt; 100\n<\/code><\/pre>\n<p>\nVorhanden ist ein zusammengesetzter Index, wie oben angegeben:<\/p>\n<p><img decoding=\"async\" alt=\"Ausgabe der Suchergebnisse und Leistungsprobleme\" src=\"\/wp-content\/uploads\/2020\/03\/7beab0c83a50e2d68a83a965df555ec9.jpg\" style=\"display:block;margin: 0 auto;\" \/><\/p>\n<pre><code class=\"plaintext\">Tabelle 'SalesOrderHeader'. Scan-Anzahl 1, logische Lesevorg\u00e4nge 698, physische Lesevorg\u00e4nge 0, Vorab-Lesevorg\u00e4nge 0, lob logische Lesevorg\u00e4nge 0, lob physische Lesevorg\u00e4nge 0, lob Vorab-Lesevorg\u00e4nge 0.<\/code><\/pre>\n<p>\nEs ist nicht \u00fcberraschend, dass die Abfrage den gesamten Index durchl\u00e4uft, da das Feld SubTotal nicht an erster Stelle steht. Daher kann die Abfrage nicht darauf zugreifen. Das Problem wird gel\u00f6st, indem ein weiterer Index f\u00fcr das Feld SubTotal hinzugef\u00fcgt wird, und letztendlich ergeben sich nur 48 logische Reads.<\/p>\n<p>Es k\u00f6nnen weitere Beispiele f\u00fcr Abfragen zur Z\u00e4hlung angegeben werden, aber das Prinzip bleibt dasselbe: <b>Das Abrufen von Datens\u00e4tzen und das Z\u00e4hlen der Gesamtanzahl sind zwei grundlegend unterschiedliche Abfragen<\/b>, und jede erfordert eigene Optimierungsma\u00dfnahmen. Im Allgemeinen wird es nicht m\u00f6glich sein, eine Kombination von Indizes zu finden, die f\u00fcr beide Abfragen gleicherma\u00dfen gut funktioniert.<\/p>\n<p>Daher ist eine der wichtigen Anforderungen, die bei der Entwicklung einer solchen Suchl\u00f6sung zu kl\u00e4ren ist, ob es f\u00fcr das Unternehmen wichtig ist, die Gesamtanzahl der gefundenen Objekte zu sehen. H\u00e4ufig ist dies nicht der Fall. Und meiner Meinung nach ist die Navigation zu bestimmten Seitenzahlen eine L\u00f6sung mit sehr begrenztem Anwendungsbereich, da die meisten Szenarien mit Paging so aussehen wie \u201egehe zur n\u00e4chsten Seite\u201c.<\/p>\n<h2>Paging-Option #2<\/h2>\n<p>\nAngenommen, den Nutzern ist die Kenntnis der Gesamtanzahl der gefundenen Objekte nicht wichtig. Lassen Sie uns die Suchseite vereinfachen:<\/p>\n<p><img decoding=\"async\" alt=\"Ausgabe der Suchergebnisse und Leistungsprobleme\" src=\"\/wp-content\/uploads\/2020\/03\/66f5ee6dc1a52f14e55ae31e05c502b2.jpg\" style=\"display:block;margin: 0 auto;\" \/><br \/>\nTats\u00e4chlich hat sich nur ge\u00e4ndert, dass es keine M\u00f6glichkeit mehr gibt, zu bestimmten Seitenzahlen zu navigieren, und nun ben\u00f6tigt diese Tabelle keine Kenntnisse dar\u00fcber, wie viele Seiten insgesamt existieren k\u00f6nnten. Aber es stellt sich die Frage \u2013 wie erf\u00e4hrt die Tabelle, ob Daten f\u00fcr die n\u00e4chste Seite vorhanden sind (um den Link \u201eWeiter\u201c korrekt anzuzeigen)?<\/p>\n<p>Die Antwort ist recht einfach: Man kann aus der Datenbank einen Datensatz mehr auslesen, als zur Anzeige ben\u00f6tigt wird. Das Vorhandensein dieses \u201ezus\u00e4tzlichen\u201c Datensatzes zeigt an, ob es eine weitere Charge gibt. Dadurch ben\u00f6tigt man f\u00fcr das Abrufen einer Datenseite nur einen einzigen API-Aufruf, was die Leistung erheblich verbessert und die Wartung dieser Funktionalit\u00e4t erleichtert. In der Praxis hatte ich einen Fall, in dem der Verzicht auf die Z\u00e4hlung der Gesamtdatens\u00e4tze die Ergebnisausgabe um das 4- bis 5-Fache beschleunigte.<\/p>\n<p>F\u00fcr diesen Ansatz gibt es mehrere Optionen f\u00fcr die Benutzeroberfl\u00e4che: die Schaltfl\u00e4chen \u201eZur\u00fcck\u201c und \u201eWeiter\u201c, wie im obigen Beispiel, die Schaltfl\u00e4che \u201eMehr laden\u201c, die einfach eine neue Charge zu den angezeigten Ergebnissen hinzuf\u00fcgt, sowie das \u201eendlose Scrollen\u201c, das nach dem Prinzip von \u201emehr laden\u201c funktioniert, wobei das Signal f\u00fcr das Abrufen der n\u00e4chsten Charge das Durchscrollen aller angezeigten Ergebnisse bis zum Ende durch den Benutzer ist. Unabh\u00e4ngig von der visuellen L\u00f6sung bleibt das Prinzip der Datenauswahl dasselbe.<\/p>\n<h2>Nuancen der Implementierung von Paging<\/h2>\n<p>\nIn allen oben genannten Beispielen der Abfragen wird der Ansatz \"Verschiebung + Menge\" verwendet, bei dem im Abfrage angeben wird, von welcher Ergebniszeile und wie viele Zeilen zur\u00fcckgegeben werden sollen. Lassen Sie uns zun\u00e4chst betrachten, wie die \u00dcbergabe der Parameter in diesem Fall am besten organisiert werden kann. In der Praxis habe ich mehrere M\u00f6glichkeiten gefunden:<\/p>\n<ul>\n<li>Die Seitenzahl (pageIndex) sowie die Seitengr\u00f6\u00dfe (pageSize).<\/li>\n<li>Die Reihenfolge der ersten zur\u00fcckzugebenden Datens\u00e4tze (startIndex) und die maximale Anzahl der Datens\u00e4tze im Ergebnis (count).<\/li>\n<li>Die Reihenfolge der ersten zur\u00fcckzugebenden Datens\u00e4tze (startIndex) sowie die Reihenfolge des letzten zur\u00fcckzugebenden Datensatzes (endIndex).<\/li>\n<\/ul>\n<p>\nAuf den ersten Blick mag es einfach erscheinen, sodass es keinen Unterschied gibt. Das ist jedoch nicht der Fall \u2013 die bequemste und universellste Variante ist die zweite (startIndex, count). Daf\u00fcr gibt es mehrere Gr\u00fcnde:<\/p>\n<ul>\n<li>F\u00fcr den Ansatz mit einer Lesung von +1 Datensatz, wie oben dargestellt, ist die erste Variante mit pageIndex und pageSize \u00e4u\u00dferst unpraktisch. Zum Beispiel m\u00f6chten wir 50 Datens\u00e4tze pro Seite anzeigen. Laut dem oben genannten Algorithmus m\u00fcssen mehr Datens\u00e4tze gelesen werden, als tats\u00e4chlich ben\u00f6tigt werden. Wenn dieses \u201e+1\u201c nicht serverseitig eingeplant ist, m\u00fcssen wir f\u00fcr die erste Seite die Datens\u00e4tze von 1 bis 51 anfordern, f\u00fcr die zweite von 51 bis 101 usw. Wenn wir die Seitenanzahl auf 51 festlegen und den pageIndex erh\u00f6hen, wird die zweite Seite die Datens\u00e4tze von 52 bis 102 zur\u00fcckgeben usw. Somit besteht der einzige Weg, die Schaltfl\u00e4che zum Wechseln zur n\u00e4chsten Seite vern\u00fcnftig zu implementieren, darin, auf dem Server eine Lesung der \u201e\u00fcbersch\u00fcssigen\u201c Zeile einzuplanen, was ein sehr unauff\u00e4lliger Aspekt ist.<\/li>\n<li>Die dritte Variante hat \u00fcberhaupt keinen Sinn, da f\u00fcr die Ausf\u00fchrung von Abfragen in den meisten Datenbanken dennoch die Anzahl und nicht der Index des letzten Datensatzes \u00fcbergeben werden muss. Auch wenn die Subtraktion von startIndex aus endIndex und eine elementare arithmetische Operation ist, ist sie hier \u00fcberfl\u00fcssig.<\/li>\n<\/ul>\n<p>\nNun sollten die Nachteile der Implementierung von Paging \u00fcber \u201eOffset + Anzahl\u201c beschrieben werden:<\/p>\n<ul>\n<li>Der Zugriff auf jede n\u00e4chste Seite wird teurer und langsamer sein als der vorherige, da die Datenbank alle Eintr\u00e4ge gem\u00e4\u00df den Such- und Sortierkriterien von \u201eanfangs\u201c durchgehen muss, um dann am gew\u00fcnschten Abschnitt zu stoppen.<\/li>\n<li>Nicht alle DBMS k\u00f6nnen diesen Ansatz unterst\u00fctzen.<\/li>\n<\/ul>\n<p>\nEs gibt Alternativen, aber auch diese sind nicht perfekt. Der erste dieser Ans\u00e4tze wird als \u201eKeyset Paging\u201c oder \u201eSeek-Methode\u201c bezeichnet und funktioniert wie folgt: Nach dem Abrufen einer Menge kann man die Werte der Felder im letzten Eintrag auf der Seite speichern und diese dann f\u00fcr den Zugriff auf die n\u00e4chste Menge verwenden. Zum Beispiel haben wir diese Abfrage durchgef\u00fchrt:<\/p>\n<pre><code class=\"sql\">SELECT * FROM Sales.SalesOrderHeader\nORDER BY OrderDate DESC\nOFFSET 0 ROWS\nFETCH NEXT 50 ROWS ONLY\n<\/code><\/pre>\n<p>\n\u0418 \u0432 \u043f\u043e\u0441\u043b\u0435\u0434\u043d\u0435\u0439 \u0437\u0430\u043f\u0438\u0441\u0438 \u043f\u043e\u043b\u0443\u0447\u0438\u043b\u0438 \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0435 \u0434\u0430\u0442\u044b \u0437\u0430\u043a\u0430\u0437\u0430 &#8216;2014-06-29&#8217;. \u0422\u043e\u0433\u0434\u0430 \u0434\u043b\u044f \u043f\u043e\u043b\u0443\u0447\u0435\u043d\u0438\u044f \u0441\u043b\u0435\u0434\u0443\u044e\u0449\u0435\u0439 \u0441\u0442\u0440\u0430\u043d\u0438\u0446\u044b \u043c\u043e\u0436\u043d\u043e \u0431\u0443\u0434\u0435\u0442 \u043f\u043e\u043f\u044b\u0442\u0430\u0442\u044c\u0441\u044f \u0432\u044b\u043f\u043e\u043b\u043d\u0438\u0442\u044c \u0442\u0430\u043a\u043e\u0435:<\/p>\n<pre><code class=\"sql\">SELECT * FROM Sales.SalesOrderHeader\nWHERE OrderDate &lt; &#039;2014-06-29&#039;\nORDER BY OrderDate DESC\nOFFSET 0 ROWS\nFETCH NEXT 50 ROWS ONLY\n<\/code><\/pre>\n<p>\nDas Problem ist, dass OrderDate kein eindeutiges Feld ist und die oben angegebene Bedingung mit hoher Wahrscheinlichkeit viele ben\u00f6tigte Zeilen \u00fcberspringt. Um Klarheit in diese Abfrage zu bringen, muss ein eindeutiges Feld hinzugef\u00fcgt werden (nehmen wir an, dass 75074 der letzte Wert des Prim\u00e4rschl\u00fcssels aus der ersten Charge ist):<\/p>\n<pre><code class=\"sql\">SELECT * FROM Sales.SalesOrderHeader\nWHERE (OrderDate = '2014-06-29' AND SalesOrderID &lt; 75074)\n   OR (OrderDate &lt; &#039;2014-06-29&#039;)\nORDER BY OrderDate DESC, SalesOrderID DESC\nOFFSET 0 ROWS\nFETCH NEXT 50 ROWS ONLY\n<\/code><\/pre>\n<p>\n\u042d\u0442\u043e\u0442 \u0432\u0430\u0440\u0438\u0430\u043d\u0442 \u0431\u0443\u0434\u0435\u0442 \u0440\u0430\u0431\u043e\u0442\u0430\u0442\u044c \u043a\u043e\u0440\u0440\u0435\u043a\u0442\u043d\u043e, \u043d\u043e \u0432 \u043e\u0431\u0449\u0435\u043c \u0441\u043b\u0443\u0447\u0430\u0435 \u0435\u0433\u043e \u0431\u0443\u0434\u0435\u0442 \u0442\u044f\u0436\u0435\u043b\u043e \u043e\u043f\u0442\u0438\u043c\u0438\u0437\u0438\u0440\u043e\u0432\u0430\u0442\u044c, \u0442\u0430\u043a \u043a\u0430\u043a \u0443\u0441\u043b\u043e\u0432\u0438\u0435 \u0441\u043e\u0434\u0435\u0440\u0436\u0438\u0442 \u043e\u043f\u0435\u0440\u0430\u0442\u043e\u0440 OR. \u0415\u0441\u043b\u0438 \u0441 \u0440\u043e\u0441\u0442\u043e\u043c OrderDate \u0440\u0430\u0441\u0442\u0435\u0442 \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0435 \u043f\u0435\u0440\u0432\u0438\u0447\u043d\u043e\u0433\u043e \u043a\u043b\u044e\u0447\u0430, \u0442\u043e \u0443\u0441\u043b\u043e\u0432\u0438\u0435 \u043c\u043e\u0436\u043d\u043e \u0443\u043f\u0440\u043e\u0441\u0442\u0438\u0442\u044c, \u043e\u0441\u0442\u0430\u0432\u0438\u0432 \u0442\u043e\u043b\u044c\u043a\u043e \u0444\u0438\u043b\u044c\u0442\u0440 \u043f\u043e SalesOrderID. \u041d\u043e \u0435\u0441\u043b\u0438 \u043c\u0435\u0436\u0434\u0443 \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u044f\u043c\u0438 \u043f\u0435\u0440\u0432\u0438\u0447\u043d\u043e\u0433\u043e \u043a\u043b\u044e\u0447\u0430 \u0438 \u043f\u043e\u043b\u044f, \u043f\u043e \u043a\u043e\u0442\u043e\u0440\u043e\u043c\u0443 \u043e\u0442\u0441\u043e\u0440\u0442\u0438\u0440\u043e\u0432\u0430\u043d \u0440\u0435\u0437\u0443\u043b\u044c\u0442\u0430\u0442, \u043d\u0435\u0442 \u0441\u0442\u0440\u043e\u0433\u043e\u0439 \u043a\u043e\u0440\u0440\u0435\u043b\u044f\u0446\u0438\u0438 \u2014 \u0432 \u0431\u043e\u043b\u044c\u0448\u0438\u043d\u0441\u0442\u0432\u0435 \u0421\u0423\u0411\u0414 \u0438\u0437\u0431\u0435\u0436\u0430\u0442\u044c \u044d\u0442\u043e\u0433\u043e OR \u043d\u0435 \u043f\u043e\u043b\u0443\u0447\u0438\u0442\u0441\u044f. \u0418\u0437\u0432\u0435\u0441\u0442\u043d\u044b\u043c \u043c\u043d\u0435 \u0438\u0441\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u0435\u043c \u044f\u0432\u043b\u044f\u0435\u0442\u0441\u044f PostgreSQL, \u0433\u0434\u0435 \u0432 \u043f\u043e\u043b\u043d\u043e\u0439 \u043c\u0435\u0440\u0435 \u043f\u043e\u0434\u0434\u0435\u0440\u0436\u0438\u0432\u0430\u0435\u0442\u0441\u044f \u0441\u0440\u0430\u0432\u043d\u0435\u043d\u0438\u0435 \u043a\u043e\u0440\u0442\u0435\u0436\u0435\u0439, \u0438 \u0443\u043a\u0430\u0437\u0430\u043d\u043d\u043e\u0435 \u0432\u044b\u0448\u0435 \u0443\u0441\u043b\u043e\u0432\u0438\u0435 \u043c\u043e\u0436\u043d\u043e \u0437\u0430\u043f\u0438\u0441\u0430\u0442\u044c \u043a\u0430\u043a \u00abWHERE (OrderDate, SalesOrderID) &lt; (&#8216;2014-06-29&#8217;, 75074)\u00bb. \u041f\u0440\u0438 \u043d\u0430\u043b\u0438\u0447\u0438\u0438 \u0441\u043e\u0441\u0442\u0430\u0432\u043d\u043e\u0433\u043e \u043a\u043b\u044e\u0447\u0430 \u0441 \u044d\u0442\u0438\u043c\u0438 \u0434\u0432\u0443\u043c\u044f \u043f\u043e\u043b\u044f\u043c\u0438 \u043f\u043e\u0434\u043e\u0431\u043d\u044b\u0439 \u0437\u0430\u043f\u0440\u043e\u0441 \u0434\u043e\u043b\u0436\u0435\u043d \u0431\u044b\u0442\u044c \u0434\u043e\u0441\u0442\u0430\u0442\u043e\u0447\u043d\u043e \u043b\u0435\u0433\u043a\u0438\u043c.<\/p>\n<p>Ein zweiter alternativer Ansatz findet sich beispielsweise in <noindex><a rel=\"nofollow\" href=\"https:\/\/www.elastic.co\/guide\/en\/elasticsearch\/reference\/6.8\/search-request-scroll.html\">ElasticSearch Scroll-API<\/a><\/noindex> oder <noindex><a rel=\"nofollow\" href=\"https:\/\/medium.com\/@gary.strange\/understanding-cosmosdb-continuation-tokens-hasmoreresults-and-connectionpolicy-requesttimeouts-3ed1fadfa81d\">Cosmos DB<\/a><\/noindex> \u2014 wenn die Anfrage neben den Daten eine spezielle Kennung zur\u00fcckgibt, mit der man die n\u00e4chste Datenmenge abrufen kann. Wenn diese Kennung unbegrenzte Lebensdauer hat (wie in Cosmos DB), dann ist das eine ausgezeichnete M\u00f6glichkeit, Paging mit sequenziellen Seiten\u00fcberg\u00e4ngen zu realisieren (wie in Variante #2 oben erw\u00e4hnt). M\u00f6gliche Nachteile: wird nicht von allen DBMS unterst\u00fctzt; die erhaltene Kennung der n\u00e4chsten Datenmenge kann eine begrenzte Lebensdauer haben, was im Allgemeinen f\u00fcr die Benutzerinteraktion nicht geeignet ist (wie zum Beispiel die ElasticSearch Scroll-API).<\/p>\n<h2>Komplexe Filterung<\/h2>\n<p>\nLassen Sie uns die Aufgabe weiter verkomplizieren. Angenommen, es gibt die Anforderung, eine sogenannte facettierte Suche zu implementieren, die allen aus Online-Shops bekannt ist. Die oben genannten Beispiele basieren auf der Bestelltabelle sind in diesem Fall nicht sehr aussagekr\u00e4ftig, also wechseln wir zur Produkttabelle aus der AdventureWorks-Datenbank:<\/p>\n<p><img decoding=\"async\" alt=\"Ausgabe der Suchergebnisse und Leistungsprobleme\" src=\"\/wp-content\/uploads\/2020\/03\/070890a3cb79de3edc69b60b5300efe7.jpg\" style=\"display:block;margin: 0 auto;\" \/><br \/>\nWas ist die Idee der facettierten Suche? Dass f\u00fcr jedes Filterelement die Anzahl der Eintr\u00e4ge angezeigt wird, die dem Kriterium entsprechen. <i>unter Ber\u00fccksichtigung der Filter, die in allen anderen Kategorien ausgew\u00e4hlt wurden<\/i>.<\/p>\n<p>Wenn wir beispielsweise in diesem Beispiel die Kategorie Fahrr\u00e4der und die Farbe Schwarz ausw\u00e4hlen, zeigt die Tabelle nur schwarze Fahrr\u00e4der an, aber:<\/p>\n<ul>\n<li>F\u00fcr jedes Kriterium der Gruppe \u201eKategorien\u201c wird die Anzahl der Produkte dieser Kategorie in Schwarz angezeigt.<\/li>\n<li>F\u00fcr jedes Kriterium der Gruppe \u201eFarben\u201c wird die Anzahl der Fahrr\u00e4der dieser Farbe angezeigt.<\/li>\n<\/ul>\n<p>\nHier ist ein Beispiel f\u00fcr die Ausgabe eines solchen Ergebnisses:<\/p>\n<p><img decoding=\"async\" alt=\"Ausgabe der Suchergebnisse und Leistungsprobleme\" src=\"\/wp-content\/uploads\/2020\/03\/7c8ec4d8b427f1f59f0129afebe74b08.jpg\" style=\"display:block;margin: 0 auto;\" \/><br \/>\nWenn au\u00dferdem die Kategorie \u201eBekleidung\u201c markiert ist, zeigt die Tabelle auch die schwarze Kleidung, die verf\u00fcgbar ist. Die Anzahl der Produkte in der Sektion \u201eFarben\u201c wird ebenfalls gem\u00e4\u00df den neuen Bedingungen neu berechnet, wobei sich in der Sektion \u201eKategorien\u201c nichts \u00e4ndern wird\u2026 Ich hoffe, diese Beispiele sind ausreichend, um den gewohnten Algorithmus der facettierten Suche zu verstehen.<\/p>\n<p>Nun stellen wir uns vor, wie dies auf einer relationalen Datenbank umgesetzt werden kann. Jede Gruppe von Kriterien, wie Kategorie und Farbe, erfordert eine separate Abfrage:<\/p>\n<pre><code class=\"sql\">W\u00c4HLEN Sie pc.ProductCategoryID, pc.Name, COUNT(1) VON Production.Product p\n  INNER JOIN Production.ProductSubcategory ps ON p.ProductSubcategoryID = ps.ProductSubcategoryID\n  INNER JOIN Production.ProductCategory pc ON ps.ProductCategoryID = pc.ProductCategoryID\nWO p.Farbe = 'Schwarz'\nGRUPPIEREN NACH pc.ProductCategoryID, pc.Name\nORDERN NACH COUNT(1) DESC\n<\/code><\/pre>\n<p>\n<img decoding=\"async\" alt=\"Ausgabe der Suchergebnisse und Leistungsprobleme\" src=\"\/wp-content\/uploads\/2020\/03\/3b59b68ce934c4ec1630ec649e68ccdb.jpg\" style=\"display:block;margin: 0 auto;\" \/><\/p>\n<pre><code class=\"sql\">W\u00c4HLEN Sie Farbe, COUNT(1) VON Production.Product p\n  INNER JOIN Production.ProductSubcategory ps ON p.ProductSubcategoryID = ps.ProductSubcategoryID\nWO ps.ProductCategoryID = 1 --Fahrr\u00e4der\nGRUPPIEREN NACH Farbe\nORDERN NACH COUNT(1) DESC\n<\/code><\/pre>\n<p>\n<img decoding=\"async\" alt=\"Ausgabe der Suchergebnisse und Leistungsprobleme\" src=\"\/wp-content\/uploads\/2020\/03\/82e1e6ab0fae683d383e3fecf4e3a15a.jpg\" style=\"display:block;margin: 0 auto;\" \/><br \/>\nWas ist das Problem mit dieser L\u00f6sung? Ganz einfach \u2013 sie l\u00e4sst sich schlecht skalieren. Jedes Filtersegment ben\u00f6tigt eine separate Anfrage zur Z\u00e4hlung der Anzahl, und diese Anfragen sind nicht gerade leicht. In Online-Shops k\u00f6nnen in bestimmten Kategorien mehrere Dutzend Filtersegmente vorhanden sein, was eine ernsthafte Herausforderung f\u00fcr die Performance darstellen kann.<\/p>\n<p>In der Regel werden mir nach diesen Aussagen einige L\u00f6sungen angeboten, n\u00e4mlich:<\/p>\n<ul>\n<li>Alle Z\u00e4hlungen in einer Anfrage zu kombinieren. Technisch gesehen ist das mit dem Schl\u00fcsselwort UNION m\u00f6glich, aber die Performance wird dadurch nicht signifikant verbessert \u2013 die Datenbank muss dennoch jeden der Fragmente von Grund auf ausf\u00fchren.<\/li>\n<li>Die Menge zu cachen, wird mir praktisch jedes Mal vorgeschlagen, wenn ich ein Problem beschreibe. Der Haken dabei ist, dass dies im Allgemeinen unm\u00f6glich ist. Nehmen wir an, wir haben 10 \"Facetten\", von denen jede 5 Werte hat. Das ist eine sehr \"modeste\" Situation im Vergleich zu dem, was man in den gleichen Online-Shops sehen kann. Die Auswahl eines Facettenelements beeinflusst die Mengen in 9 anderen. Mit anderen Worten, f\u00fcr jede Kombination von Kriterien k\u00f6nnen die Mengen unterschiedlich sein. Insgesamt gibt es in unserem Beispiel 50 Kriterien, die der Benutzer ausw\u00e4hlen kann; entsprechend wird es 250 m\u00f6gliche Kombinationen geben. Um ein solches Datenarray zu f\u00fcllen, reicht weder der Speicher noch die Zeit aus. Hier k\u00f6nnte man dagegen einwenden, dass nicht alle Kombinationen realistisch sind und Benutzer selten mehr als 5-10 Kriterien ausw\u00e4hlen. Ja, man k\u00f6nnte eine lazy Loading- und Caching-L\u00f6sung nur f\u00fcr das implementieren, was jemals ausgew\u00e4hlt wurde, aber je mehr Auswahlm\u00f6glichkeiten es gibt, desto weniger effektiv wird dieses Cache und desto sichtbarere werden die Probleme mit der Reaktionszeit sein (insbesondere wenn sich der Datensatz regelm\u00e4\u00dfig \u00e4ndert).<\/li>\n<\/ul>\n<p>\nGl\u00fccklicherweise gibt es schon seit l\u00e4ngerer Zeit effektive L\u00f6sungen f\u00fcr diese Art von Aufgabe, die zuverl\u00e4ssig mit gro\u00dfen Datenmengen arbeiten. F\u00fcr jede dieser Optionen ist es sinnvoll, die Berechnung der Facetten und das Abrufen der Ergebnisse in zwei parallele Serveranfragen aufzuteilen. Zudem sollte die Benutzeroberfl\u00e4che so gestaltet werden, dass das Laden der Daten zu den Facetten die Anzeige der Suchergebnisse nicht st\u00f6rt.<\/p>\n<ul>\n<li>Versuchen Sie, die vollst\u00e4ndige Neuberechnung der \"Fasetten\" so selten wie m\u00f6glich auszuf\u00fchren. Berechnen Sie zum Beispiel nicht alles bei jeder \u00c4nderung der Suchkriterien, sondern ermitteln Sie stattdessen die Gesamtzahl der Ergebnisse, die den aktuellen Bedingungen entsprechen, und schlagen Sie dem Benutzer vor, diese anzuzeigen: \"1425 Eintr\u00e4ge gefunden, anzeigen?\" Der Benutzer kann entweder die Suchkriterien weiter anpassen oder auf die Schaltfl\u00e4che \u201eanzeigen\u201c klicken. Nur im zweiten Fall werden alle Abfragen zur Ergebniserfassung und Neuberechnung der Mengen in allen \"Fasetten\" ausgef\u00fchrt. Dabei m\u00fcssen Sie, wie leicht zu erkennen ist, mit einer Anfrage zur Ermittlung der Gesamtzahl der Ergebnisse und deren Optimierung umgehen. Diese Methode findet man h\u00e4ufig in vielen kleinen Online-Shops. Offensichtlich ist dies kein Allheilmittel f\u00fcr dieses Problem, kann aber in einfachen F\u00e4llen ein akzeptabler Kompromiss sein.<\/li>\n<li>Verwenden Sie eine Suchmaschine, um Ergebnisse zu suchen und Facetten zu z\u00e4hlen, wie Solr, ElasticSearch, Sphinx und andere. Alle sind f\u00fcr den Aufbau von \u201eFacetten\u201c konzipiert und tun dies recht effizient durch Hilfe eines invertierten Indexes. Wie Suchmaschinen funktionieren, warum sie in solchen F\u00e4llen effektiver sind als allgemeine Datenbanken, welche Praktiken und Fallstricke es gibt \u2013 das ist Thema f\u00fcr einen eigenen Artikel. Hier m\u00f6chte ich darauf hinweisen, dass eine Suchmaschine kein Ersatz f\u00fcr das prim\u00e4re Datenspeicher ist, sondern als Erg\u00e4nzung genutzt wird: Alle \u00c4nderungen in der Hauptdatenbank, die f\u00fcr die Suche relevant sind, werden im Suchindex synchronisiert; der Suchmechanismus interagiert normalerweise nur mit der Suchmaschine und greift nicht auf die Hauptdatenbank zu. Einer der wichtigsten Punkte hierbei ist, wie man diese Synchronisation zuverl\u00e4ssig organisiert. Alles h\u00e4ngt von den Anforderungen an die \u201eReaktionszeit\u201c ab. Wenn die Zeit zwischen der \u00c4nderung in der Hauptdatenbank und deren \u201eManifestation\u201c in der Suche nicht kritisch ist, kann ein Dienst eingerichtet werden, der alle paar Minuten nach zuletzt ge\u00e4nderten Datens\u00e4tzen sucht und sie indiziert. Wenn die minimal m\u00f6gliche Reaktionszeit erforderlich ist, kann etwas \u00c4hnliches wie <noindex><a rel=\"nofollow\" href=\"https:\/\/microservices.io\/patterns\/data\/transactional-outbox.html\">transaktionale Ausgangsbotschaft<\/a><\/noindex> zum Senden von Updates an den Suchdienst.<\/li>\n<\/ul>\n<p><\/p>\n<h2>Fazit<\/h2>\n<p><\/p>\n<ol>\n<li>Die Implementierung von Paging auf der Serverseite ist eine ernsthafte Komplikation und sollte nur f\u00fcr schnell wachsende oder einfach gro\u00dfe Datens\u00e4tze angewendet werden. Es gibt kein absolut pr\u00e4zises Rezept daf\u00fcr, was \"gro\u00df\" oder \"schnell wachsend\" ist, aber ich w\u00fcrde diesen Ansatz verfolgen:\n<ul>\n<li>Wenn das Abrufen der vollst\u00e4ndigen Datensammlung unter Ber\u00fccksichtigung der Serverzeiten und der Netzwerkauslastung innerhalb der Leistungsanforderungen liegt, macht die Umsetzung von Paging auf der Serverseite wenig Sinn.<\/li>\n<li>Es kann vorkommen, dass vorerst keine Leistungsprobleme zu erwarten sind, da es nur wenige Daten gibt, die Datensammlung jedoch st\u00e4ndig w\u00e4chst. Wenn ein Datensatz in der Zukunft m\u00f6glicherweise nicht mehr den vorherigen Punkt erf\u00fcllt, ist es besser, Paging direkt einzuplanen.<\/li>\n<\/ul>\n<\/li>\n<li>Wenn es von der Gesch\u00e4ftseite keine strengen Anforderungen an die Gesamtanzahl der Ergebnisse oder an die Anzeige der Seitenzahlen gibt und Ihr System keinen Suchmechanismus besitzt, ist es besser, diese Punkte nicht umzusetzen und Variante #2 in Betracht zu ziehen.<\/li>\n<li>Wenn es eine klare Anforderung an die facettierte Suche gibt, haben Sie zwei Optionen, um die Leistung nicht zu beeintr\u00e4chtigen:\n<ul>\n<li>Z\u00e4hlen Sie nicht alle Mengen bei jeder \u00c4nderung der Suchkriterien neu.<\/li>\n<li>Verwenden Sie Suchmaschinen wie Solr, ElasticSearch, Sphinx und andere. Es ist jedoch wichtig zu verstehen, dass sie keine Ersatzl\u00f6sung f\u00fcr die Hauptdatenbank darstellen und als Erg\u00e4nzung zum Hauptspeicher f\u00fcr die L\u00f6sung von Suchaufgaben genutzt werden sollten. <\/li>\n<\/ul>\n<\/li>\n<li>Auch im Fall der facettierten Suche ist es sinnvoll, das Abrufen der Suchergebnisseite und das Z\u00e4hlen der Mengen in zwei parallele Anfragen zu unterteilen. Das Z\u00e4hlen der Mengen kann l\u00e4nger dauern als das Abrufen der Ergebnisse, w\u00e4hrend die Ergebnisse f\u00fcr den Benutzer wichtiger sind.<\/li>\n<li>Wenn Sie eine SQL-Datenbank f\u00fcr Suchvorg\u00e4nge verwenden, sollten alle Code\u00e4nderungen, die sich auf diesen Bereich beziehen, gr\u00fcndlich auf deren Leistung mit einem entsprechenden Datenvolumen (gr\u00f6\u00dfer als das Volumen in der \u201eLive\u201c-Datenbank) getestet werden. Es ist au\u00dferdem ratsam, die Abfrageausf\u00fchrungszeiten auf allen Datenbankinstanzen zu \u00fcberwachen, insbesondere auf der \u201eLive\u201c-Instanz. Selbst wenn in der Entwicklungsphase mit den Abfragepl\u00e4nen alles gut war, kann sich die Situation mit wachsendem Datenvolumen erheblich \u00e4ndern.<\/li>\n<\/ol>\n<p>Quelle: <a content=\"nofollow\" rel=\"nofollow\" href=\"https:\/\/habr.com\/ru\/company\/epam_systems\/blog\/493438\/\">habr.com<\/a> <\/p>","protected":false,"gt_translate_keys":[{"key":"rendered","format":"html"}]},"excerpt":{"rendered":"<p>\u041e\u0434\u0438\u043d \u0438\u0437 \u0442\u0438\u043f\u043e\u0432\u044b\u0445 \u0441\u0446\u0435\u043d\u0430\u0440\u0438\u0435\u0432 \u0432\u043e \u0432\u0441\u0435\u0445 \u043f\u0440\u0438\u0432\u044b\u0447\u043d\u044b\u0445 \u043d\u0430\u043c \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u044f\u0445 \u2014 \u043f\u043e\u0438\u0441\u043a \u0434\u0430\u043d\u043d\u044b\u0445 \u043f\u043e \u043e\u043f\u0440\u0435\u0434\u0435\u043b\u0435\u043d\u043d\u044b\u043c \u043a\u0440\u0438\u0442\u0435\u0440\u0438\u044f\u043c \u0438 \u0432\u044b\u0432\u043e\u0434 \u0438\u0445 \u0432 \u0443\u0434\u043e\u0431\u043d\u043e\u043c \u0434\u043b\u044f \u0447\u0442\u0435\u043d\u0438\u044f \u0432\u0438\u0434\u0435. \u0422\u0443\u0442 \u0436\u0435 \u043c\u043e\u0433\u0443\u0442 \u0431\u044b\u0442\u044c \u0434\u043e\u043f\u043e\u043b\u043d\u0438\u0442\u0435\u043b\u044c\u043d\u044b\u0435 \u0432\u043e\u0437\u043c\u043e\u0436\u043d\u043e\u0441\u0442\u0438 \u043f\u043e \u0441\u043e\u0440\u0442\u0438\u0440\u043e\u0432\u043a\u0435, \u0433\u0440\u0443\u043f\u043f\u0438\u0440\u043e\u0432\u043a\u0435, \u043f\u043e\u0441\u0442\u0440\u0430\u043d\u0438\u0447\u043d\u043e\u043c\u0443 \u0432\u044b\u0432\u043e\u0434\u0443. \u0417\u0430\u0434\u0430\u0447\u0430, \u043f\u043e \u0438\u0434\u0435\u0435, \u0442\u0440\u0438\u0432\u0438\u0430\u043b\u044c\u043d\u0430\u044f, \u043d\u043e \u043f\u0440\u0438 \u0435\u0435 \u0440\u0435\u0448\u0435\u043d\u0438\u0438 \u043c\u043d\u043e\u0433\u0438\u0435 \u0440\u0430\u0437\u0440\u0430\u0431\u043e\u0442\u0447\u0438\u043a\u0438 \u0434\u0435\u043b\u0430\u044e\u0442 \u0440\u044f\u0434 \u043e\u0448\u0438\u0431\u043e\u043a, \u0438\u0437-\u0437\u0430 \u043a\u043e\u0442\u043e\u0440\u044b\u0445 \u043f\u043e\u0442\u043e\u043c \u0441\u0442\u0440\u0430\u0434\u0430\u0435\u0442 \u043f\u0440\u043e\u0438\u0437\u0432\u043e\u0434\u0438\u0442\u0435\u043b\u044c\u043d\u043e\u0441\u0442\u044c. \u041f\u043e\u043f\u0440\u043e\u0431\u0443\u0435\u043c \u0440\u0430\u0441\u0441\u043c\u043e\u0442\u0440\u0435\u0442\u044c \u0440\u0430\u0437\u043b\u0438\u0447\u043d\u044b\u0435 [&hellip;]<\/p>\n","protected":false,"gt_translate_keys":[{"key":"rendered","format":"html"}]},"author":1,"featured_media":75531,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[688],"tags":[],"class_list":["post-75530","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-administrirovanie"],"aioseo_notices":[],"aioseo_head":"\n\t\t<!-- All in One SEO 4.9.10 - aioseo.com -->\n\t<meta name=\"description\" content=\"\u041e\u0434\u0438\u043d \u0438\u0437 \u0442\u0438\u043f\u043e\u0432\u044b\u0445 \u0441\u0446\u0435\u043d\u0430\u0440\u0438\u0435\u0432 \u0432\u043e \u0432\u0441\u0435\u0445 \u043f\u0440\u0438\u0432\u044b\u0447\u043d\u044b\u0445 \u043d\u0430\u043c \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u044f\u0445 \u2014 \u043f\u043e\u0438\u0441\u043a \u0434\u0430\u043d\u043d\u044b\u0445 \u043f\u043e \u043e\u043f\u0440\u0435\u0434\u0435\u043b\u0435\u043d\u043d\u044b\u043c \u043a\u0440\u0438\u0442\u0435\u0440\u0438\u044f\u043c \u0438 \u0432\u044b\u0432\u043e\u0434 \u0438\u0445 \u0432 \u0443\u0434\u043e\u0431\u043d\u043e\u043c \u0434\u043b\u044f \u0447\u0442\u0435\u043d\u0438\u044f \u0432\u0438\u0434\u0435. \u0422\u0443\u0442 \u0436\u0435 \u043c\u043e\u0433\u0443\u0442 \u0431\u044b\u0442\u044c \u0434\u043e\u043f\u043e\u043b\u043d\u0438\u0442\u0435\u043b\u044c\u043d\u044b\u0435 \u0432\u043e\u0437\u043c\u043e\u0436\u043d\u043e\u0441\u0442\u0438 \u043f\u043e \u0441\u043e\u0440\u0442\u0438\u0440\u043e\u0432\u043a\u0435, \u0433\u0440\u0443\u043f\u043f\u0438\u0440\u043e\u0432\u043a\u0435, \u043f\u043e\u0441\u0442\u0440\u0430\u043d\u0438\u0447\u043d\u043e\u043c\u0443 \u0432\u044b\u0432\u043e\u0434\u0443. \u0417\u0430\u0434\u0430\u0447\u0430, \u043f\u043e \u0438\u0434\u0435\u0435, \u0442\u0440\u0438\u0432\u0438\u0430\u043b\u044c\u043d\u0430\u044f, \u043d\u043e \u043f\u0440\u0438 \u0435\u0435 \u0440\u0435\u0448\u0435\u043d\u0438\u0438 \u043c\u043d\u043e\u0433\u0438\u0435 \u0440\u0430\u0437\u0440\u0430\u0431\u043e\u0442\u0447\u0438\u043a\u0438 \u0434\u0435\u043b\u0430\u044e\u0442 \u0440\u044f\u0434 \u043e\u0448\u0438\u0431\u043e\u043a, \u0438\u0437-\u0437\u0430 \u043a\u043e\u0442\u043e\u0440\u044b\u0445 \u043f\u043e\u0442\u043e\u043c \u0441\u0442\u0440\u0430\u0434\u0430\u0435\u0442 \u043f\u0440\u043e\u0438\u0437\u0432\u043e\u0434\u0438\u0442\u0435\u043b\u044c\u043d\u043e\u0441\u0442\u044c. \u041f\u043e\u043f\u0440\u043e\u0431\u0443\u0435\u043c \u0440\u0430\u0441\u0441\u043c\u043e\u0442\u0440\u0435\u0442\u044c \u0440\u0430\u0437\u043b\u0438\u0447\u043d\u044b\u0435\" \/>\n\t<meta name=\"robots\" content=\"max-image-preview:large\" \/>\n\t<meta name=\"author\" content=\"Yuri Gagarin\"\/>\n\t<link rel=\"canonical\" href=\"https:\/\/prohoster.info\/de\/blog\/administrirovanie\/vyvod-rezultatov-poiska-i-problemy-s-proizvoditelnostyu\" \/>\n\t<meta name=\"generator\" content=\"All in One SEO (AIOSEO) 4.9.10\" \/>\n\t\t<meta property=\"og:locale\" content=\"de_DE\" \/>\n\t\t<meta property=\"og:site_name\" content=\"ProHoster | \u041a\u0443\u043f\u0438\u0442\u044c \u043d\u0430\u0434\u0435\u0436\u043d\u044b\u0439 \u0445\u043e\u0441\u0442\u0438\u043d\u0433 \u0434\u043b\u044f \u0441\u0430\u0439\u0442\u043e\u0432 \u0441 \u0437\u0430\u0449\u0438\u0442\u043e\u0439 \u043e\u0442 DDoS, VPS VDS \u0441\u0435\u0440\u0432\u0435\u0440\u044b\" \/>\n\t\t<meta property=\"og:type\" content=\"article\" \/>\n\t\t<meta property=\"og:title\" content=\"\ud83e\udd47\u0412\u044b\u0432\u043e\u0434 \u0440\u0435\u0437\u0443\u043b\u044c\u0442\u0430\u0442\u043e\u0432 \u043f\u043e\u0438\u0441\u043a\u0430 \u0438 \u043f\u0440\u043e\u0431\u043b\u0435\u043c\u044b \u0441 \u043f\u0440\u043e\u0438\u0437\u0432\u043e\u0434\u0438\u0442\u0435\u043b\u044c\u043d\u043e\u0441\u0442\u044c\u044e | ProHoster\" \/>\n\t\t<meta property=\"og:description\" content=\"\u041e\u0434\u0438\u043d \u0438\u0437 \u0442\u0438\u043f\u043e\u0432\u044b\u0445 \u0441\u0446\u0435\u043d\u0430\u0440\u0438\u0435\u0432 \u0432\u043e \u0432\u0441\u0435\u0445 \u043f\u0440\u0438\u0432\u044b\u0447\u043d\u044b\u0445 \u043d\u0430\u043c \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u044f\u0445 \u2014 \u043f\u043e\u0438\u0441\u043a \u0434\u0430\u043d\u043d\u044b\u0445 \u043f\u043e \u043e\u043f\u0440\u0435\u0434\u0435\u043b\u0435\u043d\u043d\u044b\u043c \u043a\u0440\u0438\u0442\u0435\u0440\u0438\u044f\u043c \u0438 \u0432\u044b\u0432\u043e\u0434 \u0438\u0445 \u0432 \u0443\u0434\u043e\u0431\u043d\u043e\u043c \u0434\u043b\u044f \u0447\u0442\u0435\u043d\u0438\u044f \u0432\u0438\u0434\u0435. \u0422\u0443\u0442 \u0436\u0435 \u043c\u043e\u0433\u0443\u0442 \u0431\u044b\u0442\u044c \u0434\u043e\u043f\u043e\u043b\u043d\u0438\u0442\u0435\u043b\u044c\u043d\u044b\u0435 \u0432\u043e\u0437\u043c\u043e\u0436\u043d\u043e\u0441\u0442\u0438 \u043f\u043e \u0441\u043e\u0440\u0442\u0438\u0440\u043e\u0432\u043a\u0435, \u0433\u0440\u0443\u043f\u043f\u0438\u0440\u043e\u0432\u043a\u0435, \u043f\u043e\u0441\u0442\u0440\u0430\u043d\u0438\u0447\u043d\u043e\u043c\u0443 \u0432\u044b\u0432\u043e\u0434\u0443. \u0417\u0430\u0434\u0430\u0447\u0430, \u043f\u043e \u0438\u0434\u0435\u0435, \u0442\u0440\u0438\u0432\u0438\u0430\u043b\u044c\u043d\u0430\u044f, \u043d\u043e \u043f\u0440\u0438 \u0435\u0435 \u0440\u0435\u0448\u0435\u043d\u0438\u0438 \u043c\u043d\u043e\u0433\u0438\u0435 \u0440\u0430\u0437\u0440\u0430\u0431\u043e\u0442\u0447\u0438\u043a\u0438 \u0434\u0435\u043b\u0430\u044e\u0442 \u0440\u044f\u0434 \u043e\u0448\u0438\u0431\u043e\u043a, \u0438\u0437-\u0437\u0430 \u043a\u043e\u0442\u043e\u0440\u044b\u0445 \u043f\u043e\u0442\u043e\u043c \u0441\u0442\u0440\u0430\u0434\u0430\u0435\u0442 \u043f\u0440\u043e\u0438\u0437\u0432\u043e\u0434\u0438\u0442\u0435\u043b\u044c\u043d\u043e\u0441\u0442\u044c. \u041f\u043e\u043f\u0440\u043e\u0431\u0443\u0435\u043c \u0440\u0430\u0441\u0441\u043c\u043e\u0442\u0440\u0435\u0442\u044c \u0440\u0430\u0437\u043b\u0438\u0447\u043d\u044b\u0435\" \/>\n\t\t<meta property=\"og:url\" content=\"https:\/\/prohoster.info\/de\/blog\/administrirovanie\/vyvod-rezultatov-poiska-i-problemy-s-proizvoditelnostyu\" \/>\n\t\t<meta property=\"og:image\" content=\"https:\/\/prohoster.info\/wp-content\/uploads\/2021\/11\/logo-350.jpg\" \/>\n\t\t<meta property=\"og:image:secure_url\" content=\"https:\/\/prohoster.info\/wp-content\/uploads\/2021\/11\/logo-350.jpg\" \/>\n\t\t<meta property=\"og:image:width\" content=\"350\" \/>\n\t\t<meta property=\"og:image:height\" content=\"350\" \/>\n\t\t<meta property=\"article:published_time\" content=\"2020-03-26T17:42:23+00:00\" \/>\n\t\t<meta property=\"article:modified_time\" content=\"2020-03-26T17:42:23+00:00\" \/>\n\t\t<meta property=\"article:publisher\" content=\"https:\/\/www.facebook.com\/prohoster\" \/>\n\t\t<meta property=\"article:author\" content=\"https:\/\/www.facebook.com\/prohoster\" \/>\n\t\t<!-- All in One SEO -->\n\n","aioseo_head_json":{"title":"\ud83e\udd47Suchergebnisse und Leistungsprobleme | ProHoster","description":"Eines der typischen Szenarien in all unseren gewohnten Anwendungen ist die Suche nach Daten anhand bestimmter Kriterien und die Ausgabe in einem lesbaren Format. Hierbei k\u00f6nnen zus\u00e4tzliche Funktionen zum Sortieren, Gruppieren und zur paginierten Ausgabe vorhanden sein. Die Aufgabe erscheint einfach, doch viele Entwickler machen bei ihrer L\u00f6sung eine Reihe von Fehlern, die sp\u00e4ter die Leistung beeintr\u00e4chtigen. Lassen Sie uns verschiedene Aspekte betrachten.","canonical_url":"https:\/\/prohoster.info\/de\/blog\/administrirovanie\/vyvod-rezultatov-poiska-i-problemy-s-proizvoditelnostyu","robots":"max-image-preview:large","keywords":"","webmasterTools":{"miscellaneous":""},"schema":null,"og:locale":"de_DE","og:site_name":"ProHoster | \u041a\u0443\u043f\u0438\u0442\u044c \u043d\u0430\u0434\u0435\u0436\u043d\u044b\u0439 \u0445\u043e\u0441\u0442\u0438\u043d\u0433 \u0434\u043b\u044f \u0441\u0430\u0439\u0442\u043e\u0432 \u0441 \u0437\u0430\u0449\u0438\u0442\u043e\u0439 \u043e\u0442 DDoS, VPS VDS \u0441\u0435\u0440\u0432\u0435\u0440\u044b","og:type":"article","og:title":"\ud83e\udd47\u0412\u044b\u0432\u043e\u0434 \u0440\u0435\u0437\u0443\u043b\u044c\u0442\u0430\u0442\u043e\u0432 \u043f\u043e\u0438\u0441\u043a\u0430 \u0438 \u043f\u0440\u043e\u0431\u043b\u0435\u043c\u044b \u0441 \u043f\u0440\u043e\u0438\u0437\u0432\u043e\u0434\u0438\u0442\u0435\u043b\u044c\u043d\u043e\u0441\u0442\u044c\u044e | ProHoster","og:description":"\u041e\u0434\u0438\u043d \u0438\u0437 \u0442\u0438\u043f\u043e\u0432\u044b\u0445 \u0441\u0446\u0435\u043d\u0430\u0440\u0438\u0435\u0432 \u0432\u043e \u0432\u0441\u0435\u0445 \u043f\u0440\u0438\u0432\u044b\u0447\u043d\u044b\u0445 \u043d\u0430\u043c \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u044f\u0445 \u2014 \u043f\u043e\u0438\u0441\u043a \u0434\u0430\u043d\u043d\u044b\u0445 \u043f\u043e \u043e\u043f\u0440\u0435\u0434\u0435\u043b\u0435\u043d\u043d\u044b\u043c \u043a\u0440\u0438\u0442\u0435\u0440\u0438\u044f\u043c \u0438 \u0432\u044b\u0432\u043e\u0434 \u0438\u0445 \u0432 \u0443\u0434\u043e\u0431\u043d\u043e\u043c \u0434\u043b\u044f \u0447\u0442\u0435\u043d\u0438\u044f \u0432\u0438\u0434\u0435. \u0422\u0443\u0442 \u0436\u0435 \u043c\u043e\u0433\u0443\u0442 \u0431\u044b\u0442\u044c \u0434\u043e\u043f\u043e\u043b\u043d\u0438\u0442\u0435\u043b\u044c\u043d\u044b\u0435 \u0432\u043e\u0437\u043c\u043e\u0436\u043d\u043e\u0441\u0442\u0438 \u043f\u043e \u0441\u043e\u0440\u0442\u0438\u0440\u043e\u0432\u043a\u0435, \u0433\u0440\u0443\u043f\u043f\u0438\u0440\u043e\u0432\u043a\u0435, \u043f\u043e\u0441\u0442\u0440\u0430\u043d\u0438\u0447\u043d\u043e\u043c\u0443 \u0432\u044b\u0432\u043e\u0434\u0443. \u0417\u0430\u0434\u0430\u0447\u0430, \u043f\u043e \u0438\u0434\u0435\u0435, \u0442\u0440\u0438\u0432\u0438\u0430\u043b\u044c\u043d\u0430\u044f, \u043d\u043e \u043f\u0440\u0438 \u0435\u0435 \u0440\u0435\u0448\u0435\u043d\u0438\u0438 \u043c\u043d\u043e\u0433\u0438\u0435 \u0440\u0430\u0437\u0440\u0430\u0431\u043e\u0442\u0447\u0438\u043a\u0438 \u0434\u0435\u043b\u0430\u044e\u0442 \u0440\u044f\u0434 \u043e\u0448\u0438\u0431\u043e\u043a, \u0438\u0437-\u0437\u0430 \u043a\u043e\u0442\u043e\u0440\u044b\u0445 \u043f\u043e\u0442\u043e\u043c \u0441\u0442\u0440\u0430\u0434\u0430\u0435\u0442 \u043f\u0440\u043e\u0438\u0437\u0432\u043e\u0434\u0438\u0442\u0435\u043b\u044c\u043d\u043e\u0441\u0442\u044c. \u041f\u043e\u043f\u0440\u043e\u0431\u0443\u0435\u043c \u0440\u0430\u0441\u0441\u043c\u043e\u0442\u0440\u0435\u0442\u044c \u0440\u0430\u0437\u043b\u0438\u0447\u043d\u044b\u0435","og:url":"https:\/\/prohoster.info\/de\/blog\/administrirovanie\/vyvod-rezultatov-poiska-i-problemy-s-proizvoditelnostyu","og:image":"https:\/\/prohoster.info\/wp-content\/uploads\/2021\/11\/logo-350.jpg","og:image:secure_url":"https:\/\/prohoster.info\/wp-content\/uploads\/2021\/11\/logo-350.jpg","og:image:width":350,"og:image:height":350,"article:published_time":"2020-03-26T17:42:23+00:00","article:modified_time":"2020-03-26T17:42:23+00:00","article:publisher":"https:\/\/www.facebook.com\/prohoster","article:author":"https:\/\/www.facebook.com\/prohoster"},"aioseo_meta_data":{"post_id":"75530","title":null,"description":null,"keywords":null,"keyphrases":null,"primary_term":null,"canonical_url":null,"og_title":null,"og_description":null,"og_object_type":"default","og_image_type":"default","og_image_url":null,"og_image_width":null,"og_image_height":null,"og_image_custom_url":null,"og_image_custom_fields":null,"og_video":null,"og_custom_url":null,"og_article_section":null,"og_article_tags":null,"twitter_use_og":false,"twitter_card":"default","twitter_image_type":"default","twitter_image_url":null,"twitter_image_custom_url":null,"twitter_image_custom_fields":null,"twitter_title":null,"twitter_description":null,"schema":{"blockGraphs":[],"customGraphs":[],"default":{"data":{"Article":[],"Course":[],"Dataset":[],"FAQPage":[],"Movie":[],"Person":[],"Product":[],"ProductReview":[],"Car":[],"Recipe":[],"Service":[],"SoftwareApplication":[],"WebPage":[]},"graphName":"","isEnabled":true},"graphs":[]},"schema_type":null,"schema_type_options":null,"pillar_content":false,"robots_default":true,"robots_noindex":false,"robots_noarchive":false,"robots_nosnippet":false,"robots_nofollow":false,"robots_noimageindex":false,"robots_noodp":false,"robots_notranslate":false,"robots_max_snippet":null,"robots_max_videopreview":null,"robots_max_imagepreview":"large","priority":null,"frequency":null,"local_seo":null,"seo_analyzer_scan_date":null,"breadcrumb_settings":null,"limit_modified_date":false,"reviewed_by":null,"ai":null,"created":"2021-02-28 17:55:26","updated":"2022-10-02 02:12:16"},"gt_translate_keys":[{"key":"link","format":"url"}],"_links":{"self":[{"href":"https:\/\/prohoster.info\/de\/wp-json\/wp\/v2\/posts\/75530","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/prohoster.info\/de\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/prohoster.info\/de\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/prohoster.info\/de\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/prohoster.info\/de\/wp-json\/wp\/v2\/comments?post=75530"}],"version-history":[{"count":0,"href":"https:\/\/prohoster.info\/de\/wp-json\/wp\/v2\/posts\/75530\/revisions"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/prohoster.info\/de\/wp-json\/wp\/v2\/media\/75531"}],"wp:attachment":[{"href":"https:\/\/prohoster.info\/de\/wp-json\/wp\/v2\/media?parent=75530"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/prohoster.info\/de\/wp-json\/wp\/v2\/categories?post=75530"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/prohoster.info\/de\/wp-json\/wp\/v2\/tags?post=75530"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}