{"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":"Suchergebnisse anzeigen und Leistungsprobleme","gt_translate_keys":[{"key":"rendered","format":"text"}]},"content":{"rendered":"<p>Eines der typischen Szenarien in allen uns vertrauten Anwendungen ist die Suche nach Daten anhand bestimmter Kriterien und deren Ausgabe in einem lesbaren Format. Hierbei k\u00f6nnen auch zus\u00e4tzliche Funktionen wie Sortierung, Gruppierung und paginierte Ausgabe vorhanden sein. Die Aufgabe scheint trivial zu sein, doch viele Entwickler machen bei der L\u00f6sung eine Reihe von Fehlern, die sich negativ auf die Leistung auswirken. Lassen Sie uns verschiedene L\u00f6sungsm\u00f6glichkeiten f\u00fcr dieses Problem betrachten und Empfehlungen f\u00fcr die Auswahl der effektivsten Implementierung formulieren.<\/p>\n<p><img decoding=\"async\" alt=\"Suchergebnisse anzeigen 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>Paginierungsoption #1<\/h2>\n<p>\nDie einfachste L\u00f6sung, die man sich vorstellen kann, ist die paginierte Ausgabe der Suchergebnisse in der klassischsten Form.<\/p>\n<p><img decoding=\"async\" alt=\"Suchergebnisse anzeigen und Leistungsprobleme\" src=\"\/wp-content\/uploads\/2020\/03\/4957d9ad21a8a0c70390b224fe76cb33.jpg\" style=\"display:block;margin: 0 auto;\" \/><br \/>\nAngenommen, die Anwendung nutzt eine relationale Datenbank. In diesem Fall m\u00fcssen zur Anzeige der Informationen in dieser Form zwei SQL-Abfragen durchgef\u00fchrt werden:<\/p>\n<ul>\n<li>Die Zeilen f\u00fcr die aktuelle Seite abrufen.<\/li>\n<li>Die Gesamtanzahl der Zeilen z\u00e4hlen, die den Suchkriterien entsprechen \u2013 dies ist notwendig, um die Seiten anzuzeigen.<\/li>\n<\/ul>\n<p>\nBetrachten wir die erste Abfrage am Beispiel einer Test-MS-SQL-Datenbank. <noindex><a rel=\"nofollow\" href=\"https:\/\/github.com\/Microsoft\/sql-server-samples\/releases\/download\/adventureworks\/AdventureWorks2016_EXT.bak\">AdventureWorks <\/a><\/noindex>f\u00fcr den 2016-Server. 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 der Liste zur\u00fcck, die absteigend nach Hinzuf\u00fcgungsdatum sortiert sind, mit anderen Worten \u2013 die letzten 50 Bestellungen.<\/p>\n<p>Diese Abfrage wird schnell auf der Testdatenbank ausgef\u00fchrt, aber schauen wir uns den Ausf\u00fchrungsplan und die E\/A-Statistik an:<\/p>\n<p><img decoding=\"async\" alt=\"Suchergebnisse anzeigen 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-Z\u00e4hler 1, logische Reads 698, physische Reads 0, Read-Ahead Reads 0, LOB logische Reads 0, LOB physische Reads 0, LOB Read-Ahead Reads 0.<\/code><\/pre>\n<p>\n<i>Die E\/A-Statistik f\u00fcr jede Abfrage kann erhalten werden, indem im Abfrageausf\u00fchrungsumfeld der Befehl SET STATISTICS IO ON ausgef\u00fchrt wird.<\/i><\/p>\n<p>Wie aus dem Ausf\u00fchrungsplan hervorgeht, ist die sortieren von allen Zeilen der Ursprungstabelle nach Hinzuf\u00fcgungsdatum am ressourcenintensivsten. Und das Problem besteht darin, dass je mehr Zeilen in der Tabelle vorhanden sind, desto \u201eschwerer\u201c wird die Sortierung. In der Praxis sollte man solche Situationen vermeiden, daher f\u00fcgen wir einen Index auf dem Hinzuf\u00fcgungsdatum hinzu und pr\u00fcfen, ob die Ressourcennutzung sich ge\u00e4ndert hat:<\/p>\n<p><img decoding=\"async\" alt=\"Suchergebnisse anzeigen 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-Z\u00e4hler 1, logische Reads 165, physische Reads 0, Read-Ahead Reads 5, LOB logische Reads 0, LOB physische Reads 0, LOB Read-Ahead Reads 0.\n<\/code><\/pre>\n<p>\nOffensichtlich ist es viel besser geworden. Aber sind alle Probleme gel\u00f6st? Lassen Sie uns die Suchanfrage f\u00fcr Bestellungen \u00e4ndern, bei denen der Gesamtwert der Produkte 100 Dollar \u00fcbersteigt:<\/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=\"Suchergebnisse anzeigen 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 Lesevorg\u00e4nge 1081, physische Lesevorg\u00e4nge 0, Vorablesevorg\u00e4nge 0, lob logische Lesevorg\u00e4nge 0, lob physische Lesevorg\u00e4nge 0, lob Vorablesevorg\u00e4nge 0.<\/code><\/pre>\n<p>\nWir haben eine lustige Situation: Der Abfrageplan ist nicht viel schlechter als der vorherige, aber die tats\u00e4chliche Anzahl der logischen Lesevorg\u00e4nge ist fast doppelt so hoch wie bei einem vollst\u00e4ndigen Tabellenscan. Es gibt einen Ausweg \u2014 wenn wir den bereits vorhandenen Index zusammensetzen und das zweite Feld den Gesamtpreis der Produkte hinzuzuf\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 Reihe von Beispielen l\u00e4sst sich noch lange fortsetzen, aber zwei Hauptgedanken, die ich hier \u00e4u\u00dfern m\u00f6chte, sind folgende:<\/p>\n<ul>\n<li>Die Hinzuf\u00fcgung eines neuen Kriteriums oder einer neuen Sortierreihenfolge in der Suchanfrage kann die Ausf\u00fchrungsgeschwindigkeit erheblich beeinflussen.<\/li>\n<li>Wenn wir jedoch nur einen Teil der Daten abfragen m\u00fcssen und nicht alle Ergebnisse, die den Suchkriterien entsprechen, gibt es viele M\u00f6glichkeiten, eine solche Abfrage zu optimieren.<\/li>\n<\/ul>\n<p>\nKommen wir jetzt zur zweiten Abfrage, die zu Beginn erw\u00e4hnt wurde \u2014 die, die die Anzahl der Datens\u00e4tze z\u00e4hlt, die den Suchkriterien entsprechen. Nehmen wir dasselbe Beispiel \u2014 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>\nMit dem oben genannten zusammengesetzten Index erhalten wir:<\/p>\n<p><img decoding=\"async\" alt=\"Suchergebnisse anzeigen 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-Z\u00e4hler 1, logische Reads 698, physische Reads 0, Read-Ahead Reads 0, LOB logische Reads 0, LOB physische Reads 0, LOB Read-Ahead Reads 0.<\/code><\/pre>\n<p>\nDass die Abfrage \u00fcber den gesamten Index ausgef\u00fchrt wird, ist nicht \u00fcberraschend, da das Feld SubTotal nicht an erster Stelle steht, und daher kann die Abfrage nicht davon profitieren. Das Problem l\u00e4sst sich l\u00f6sen, indem ein weiterer Index auf dem Feld SubTotal hinzugef\u00fcgt wird, und am Ende ergibt sich bereits nur noch 48 logische Lesevorg\u00e4nge.<\/p>\n<p>Es lassen sich noch einige weitere Beispiele f\u00fcr Abfragen zur Z\u00e4hlung anf\u00fchren, aber die Grunds\u00e4tze bleiben die gleichen: <b>Die Abfrage von Daten und die Z\u00e4hlung der Gesamtanzahl sind zwei grundlegend verschiedene Abfragen<\/b>, und jede erfordert ihre eigenen Ma\u00dfnahmen zur Optimierung. 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 gekl\u00e4rt werden muss, ob es f\u00fcr das Gesch\u00e4ft wichtig ist, die Gesamtzahl der gefundenen Objekte zu sehen. Oft ist dem nicht so. Und die Navigation zu bestimmten Seitenzahlen ist meiner Meinung nach eine L\u00f6sung mit einem sehr engen Anwendungsbereich, da die meisten Szenarien mit Paging wie \u201ezur n\u00e4chsten Seite wechseln\u201c aussehen.<\/p>\n<h2>Paging-Option #2<\/h2>\n<p>\nAngenommen, den Benutzern ist die Kenntnis der Gesamtzahl der gefundenen Objekte nicht wichtig. Lassen Sie uns die Suchseite vereinfachen:<\/p>\n<p><img decoding=\"async\" alt=\"Suchergebnisse anzeigen 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 gibt, zu bestimmten Seitenzahlen zu wechseln, und jetzt muss diese Tabelle f\u00fcr die Anzeige nicht wissen, wie viele es insgesamt geben k\u00f6nnte. Aber die Frage stellt sich \u2013 wie erf\u00e4hrt die Tabelle, ob es Daten f\u00fcr die n\u00e4chste Seite gibt (um den Link \u201eWeiter\u201c korrekt anzuzeigen)?<\/p>\n<p>Die Antwort ist ganz einfach: Man kann einen Datensatz mehr aus der Datenbank abfragen, als f\u00fcr die Anzeige ben\u00f6tigt wird, und das Vorhandensein dieses \u201ezus\u00e4tzlichen\u201c Datensatzes zeigt, ob eine weitere Portion vorhanden ist. Auf diese Weise muss nur eine Abfrage ausgef\u00fchrt werden, um eine Seite von Daten zu erhalten, was die Leistung erheblich verbessert und die Unterst\u00fctzung einer solchen Funktionalit\u00e4t erleichtert. Ich habe in der Praxis einen Fall erlebt, bei dem der Verzicht auf die Z\u00e4hlung der Gesamtzahl der Datens\u00e4tze die Ergebnisse um das 4- bis 5-fache beschleunigt hat.<\/p>\n<p>F\u00fcr diesen Ansatz gibt es mehrere Optionen f\u00fcr die Benutzeroberfl\u00e4che: die Schaltfl\u00e4chen \u201ezur\u00fcck\u201c und \u201evorw\u00e4rts\u201c, wie im obigen Beispiel, eine Schaltfl\u00e4che \u201emehr laden\u201c, die einfach eine neue Portion zu den angezeigten Ergebnissen hinzuf\u00fcgt, und \u201eunendliches Scrollen\u201c, das nach dem Prinzip \u201emehr laden\u201c funktioniert, bei dem das Scrollen des Benutzers an das Ende aller angezeigten Ergebnisse das Signal f\u00fcr das Abrufen der n\u00e4chsten Portion ist. Wie auch immer die visuelle L\u00f6sung aussieht, das Prinzip der Datenauswahl bleibt dasselbe.<\/p>\n<h2>Details zur Implementierung von Paging<\/h2>\n<p>\nIn allen oben genannten Anfragen wird der Ansatz \u201eOffset + Anzahl\u201c verwendet, bei dem im Antrag angegeben wird, von welcher Zeile im Ergebnis und wie viele Zeilen zur\u00fcckgegeben werden sollen. Zuerst betrachten wir, wie man die \u00dcbergabe der Parameter in diesem Fall am besten organisiert. In der Praxis habe ich verschiedene Methoden gefunden:<\/p>\n<ul>\n<li>Die Seitenzahl des angeforderten Dokuments (pageIndex), die Seitengr\u00f6\u00dfe (pageSize).<\/li>\n<li>Die Reihenfolge der ersten zu zur\u00fcckgebenden Datens\u00e4tze (startIndex), die maximale Anzahl der Datens\u00e4tze im Ergebnis (count).<\/li>\n<li>Die Reihenfolge der ersten zu zur\u00fcckgebenden Datens\u00e4tze (startIndex), die Reihenfolge der letzten zu zur\u00fcckgebenden Datens\u00e4tze (endIndex).<\/li>\n<\/ul>\n<p>\nAuf den ersten Blick k\u00f6nnte man denken, dass dies so einfach ist, dass es keinen Unterschied macht. Das ist aber nicht der Fall \u2013 die praktischste 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 dem Lesen von +1 Datensatz, wie oben beschrieben, ist die erste Variante mit pageIndex und pageSize \u00e4u\u00dferst unpraktisch. Zum Beispiel wollen wir 50 Datens\u00e4tze auf einer Seite anzeigen. Laut dem oben beschriebenen Algorithmus m\u00fcssen wir einen Datensatz mehr lesen als n\u00f6tig. Wenn dieses \"+1\" nicht auf dem Server ber\u00fccksichtigt wird, bedeutet das, dass wir f\u00fcr die erste Seite die Datens\u00e4tze von 1 bis 51 anfordern m\u00fcssen, f\u00fcr die zweite von 51 bis 101 usw. Wenn wir die Seitengr\u00f6\u00dfe auf 51 festlegen und pageIndex erh\u00f6hen, gibt die zweite Seite die Datens\u00e4tze von 52 bis 102 zur\u00fcck usw. Dementsprechend ist im ersten Ansatz die einzige M\u00f6glichkeit, den Button f\u00fcr die n\u00e4chste Seite ordnungsgem\u00e4\u00df zu implementieren, das Einrechnen eines \"\u00fcberfl\u00fcssigen\" Datensatzes auf dem Server, was ein sehr unauff\u00e4lliger Aspekt sein wird.<\/li>\n<li>Der dritte Ansatz macht \u00fcberhaupt keinen Sinn, da f\u00fcr die Durchf\u00fchrung von Anfragen in den meisten Datenbanken dennoch die Anzahl und nicht der Index des letzten Datensatzes \u00fcbergeben werden muss. Es ist zwar eine einfache arithmetische Operation, das subtrahieren von startIndex von endIndex, aber sie ist hier \u00fcberfl\u00fcssig.<\/li>\n<\/ul>\n<p>\nNun sollten die Nachteile der Implementierung von Paging \u00fcber \"Offset + Anzahl\" beschrieben werden:<\/p>\n<ul>\n<li>Das Abrufen jeder n\u00e4chsten Seite wird aufwendiger und langsamer sein als die vorherige, da die Datenbank dennoch alle Datens\u00e4tze \"von Anfang an\" entsprechend den Such- und Sortierkriterien durchgehen muss, bevor sie an dem ben\u00f6tigten Abschnitt stoppt.<\/li>\n<li>Nicht alle Datenbankmanagementsysteme unterst\u00fctzen diesen Ansatz.<\/li>\n<\/ul>\n<p>\nEs gibt Alternativen, aber auch diese sind nicht ideal. Der erste solcher Ans\u00e4tze wird als \u201eKeyset Paging\u201c oder \u201eSeek-Methode\u201c bezeichnet und besteht darin, dass man nach dem Abrufen eines Teils die Werte der Felder im letzten Datensatz auf der Seite speichern kann, und diese dann verwenden, um den n\u00e4chsten Teil abzurufen. Zum Beispiel haben wir eine solche Anfrage 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>\nIn dem letzten Eintrag haben wir das Bestelldatum '2014-06-29' erhalten. Um die n\u00e4chste Seite zu erhalten, kann es versucht werden, Folgendes auszuf\u00fchren:<\/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 \u00fcberspringen wird. Um Eindeutigkeit in diese Abfrage zu bringen, muss ein eindeutiges Feld zur Bedingung 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>\nDiese Variante funktioniert korrekt, aber im Allgemeinen wird es schwierig sein, sie zu optimieren, da die Bedingung den Operator OR enth\u00e4lt. Wenn mit steigenden OrderDate auch der Wert des Prim\u00e4rschl\u00fcssels zunimmt, kann man die Bedingung vereinfachen, indem man nur den Filter nach SalesOrderID beibeh\u00e4lt. Doch wenn zwischen den Werten des Prim\u00e4rschl\u00fcssels und dem Feld, nach dem das Ergebnis sortiert ist, keine strikte Korrelation besteht, l\u00e4sst sich dieser OR in den meisten DBMS nicht vermeiden. Eine mir bekannte Ausnahme ist PostgreSQL, wo Vergleiche von Tupeln vollst\u00e4ndig unterst\u00fctzt werden und die oben genannte Bedingung als \u201eWHERE (OrderDate, SalesOrderID) &lt; (&#039;2014-06-29&#039;, 75074)\u201c formuliert werden kann. Bei einem zusammengesetzten Schl\u00fcssel mit diesen beiden Feldern sollte eine solche Abfrage relativ einfach sein.<\/p>\n<p>Einen alternativen Ansatz findet man 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 Abfrage neben den Daten einen speziellen Identifikator zur\u00fcckgibt, mit dem die n\u00e4chste Charge von Daten abgerufen werden kann. Wenn dieser Identifikator unbegrenzte Lebensdauer hat (wie in Cosmos DB), ist es eine hervorragende M\u00f6glichkeit, Paging mit sequenziellen \u00dcberg\u00e4ngen zwischen den Seiten zu implementieren (Variante #2, die oben erw\u00e4hnt wurde). M\u00f6gliche Nachteile: wird nicht von allen DBMS unterst\u00fctzt; der erhaltene Identifikator der n\u00e4chsten Charge kann eine begrenzte Lebensdauer haben, was im Allgemeinen nicht f\u00fcr die Interaktion mit Benutzern geeignet ist (wie beispielsweise bei der ElasticSearch Scroll API).<\/p>\n<h2>Komplexe Filterung<\/h2>\n<p>\nWir erschweren die Aufgabe weiter. 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 Bestellungstabelle, sind in diesem Fall jedoch nicht sehr aussagekr\u00e4ftig, also wechseln wir zur Produkttabelle aus der AdventureWorks-Datenbank:<\/p>\n<p><img decoding=\"async\" alt=\"Suchergebnisse anzeigen und Leistungsprobleme\" src=\"\/wp-content\/uploads\/2020\/03\/070890a3cb79de3edc69b60b5300efe7.jpg\" style=\"display:block;margin: 0 auto;\" \/><br \/>\nWas ist die Idee der facettierten Suche? Es ist so, dass f\u00fcr jedes Filterelement die Anzahl der Datens\u00e4tze angezeigt wird, die diesem Kriterium entsprechen. <i>unter Ber\u00fccksichtigung der Filter, die in allen anderen Kategorien ausgew\u00e4hlt wurden.<\/i>.<\/p>\n<p>Wenn wir in diesem Beispiel die Kategorie Fahrr\u00e4der und die Farbe Schwarz ausw\u00e4hlen, zeigt die Tabelle nur schwarze Fahrr\u00e4der an, aber dabei:<\/p>\n<ul>\n<li>F\u00fcr jedes Kriterium der Gruppe \u00abKategorien\u00bb wird die Anzahl der Produkte aus dieser Kategorie in Schwarz angezeigt.<\/li>\n<li>F\u00fcr jedes Kriterium der Gruppe \u00abFarben\u00bb wird die Anzahl der Fahrr\u00e4der in dieser Farbe angezeigt.<\/li>\n<\/ul>\n<p>\nHier ist ein Beispiel f\u00fcr die Ausgabe des Ergebnisses unter solchen Bedingungen:<\/p>\n<p><img decoding=\"async\" alt=\"Suchergebnisse anzeigen und Leistungsprobleme\" src=\"\/wp-content\/uploads\/2020\/03\/7c8ec4d8b427f1f59f0129afebe74b08.jpg\" style=\"display:block;margin: 0 auto;\" \/><br \/>\nWenn wir zus\u00e4tzlich die Kategorie \u00abBekleidung\u00bb ausw\u00e4hlen, zeigt die Tabelle auch schwarze Kleidung an, die verf\u00fcgbar ist. Die Anzahl der Produkte in Schwarz in der Rubrik \u00abFarben\u00bb wird ebenfalls gem\u00e4\u00df den neuen Bedingungen neu berechnet, aber in der Rubrik \u00abKategorien\u00bb bleibt alles unver\u00e4ndert\u2026 Ich hoffe, diese Beispiele sind ausreichend, um den gewohnten Algorithmus der funktionsf\u00e4higen Suche zu verstehen.<\/p>\n<p>Stellen wir uns nun vor, wie dies in einer relationalen Datenbank umgesetzt werden kann. Jede Gruppe von Kriterien, wie Kategorie und Farbe, erfordert eine separate Abfrage:<\/p>\n<pre><code class=\"sql\">SELECT pc.ProductCategoryID, pc.Name, COUNT(1) FROM 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\nWHERE p.Color = 'Black'\nGROUP BY pc.ProductCategoryID, pc.Name\nORDER BY COUNT(1) DESC\n<\/code><\/pre>\n<p>\n<img decoding=\"async\" alt=\"Suchergebnisse anzeigen und Leistungsprobleme\" src=\"\/wp-content\/uploads\/2020\/03\/3b59b68ce934c4ec1630ec649e68ccdb.jpg\" style=\"display:block;margin: 0 auto;\" \/><\/p>\n<pre><code class=\"sql\">SELECT Color, COUNT(1) FROM Production.Product p\n  INNER JOIN Production.ProductSubcategory ps ON p.ProductSubcategoryID = ps.ProductSubcategoryID\nWHERE ps.ProductCategoryID = 1 --Fahrr\u00e4der\nGROUP BY Color\nORDER BY COUNT(1) DESC\n<\/code><\/pre>\n<p>\n<img decoding=\"async\" alt=\"Suchergebnisse anzeigen und Leistungsprobleme\" src=\"\/wp-content\/uploads\/2020\/03\/82e1e6ab0fae683d383e3fecf4e3a15a.jpg\" style=\"display:block;margin: 0 auto;\" \/><br \/>\nWas ist also das Problem mit dieser L\u00f6sung? Ganz einfach \u2013 sie ist schlecht skalierbar. Jeder Abschnitt des Filters erfordert eine separate Abfrage zur Z\u00e4hlung der Mengen, und diese Abfragen sind nicht sehr leicht. In Online-Shops kann es in einigen Kategorien mehrere Dutzend Filterabschnitte geben, was zu einem ernsthaften Leistungsproblem werden kann.<\/p>\n<p>Normalerweise schlagen mir nach diesen Aussagen einige L\u00f6sungen vor, n\u00e4mlich:<\/p>\n<ul>\n<li>Alle Z\u00e4hlungen in eine Anfrage zusammenfassen. Technisch ist das m\u00f6glich mit dem Schl\u00fcsselwort UNION, allerdings hilft das der Leistung nicht wirklich \u2014 die Datenbank muss dennoch jeden der Teile \"von Grund auf\" ausf\u00fchren.<\/li>\n<li>Mengen cachen. Das wird mir praktisch jedes Mal vorgeschlagen, wenn ich das Problem beschreibe. Der Knackpunkt ist, dass dies im Allgemeinen unm\u00f6glich ist. Angenommen, wir haben 10 \u201eFacetten\u201c, von denen jede 5 Werte hat. Dies ist eine sehr \u201emodeste\u201c Situation im Vergleich zu dem, was man in denselben 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. In unserem Beispiel gibt es insgesamt 50 Kriterien, die der Benutzer ausw\u00e4hlen kann, was bedeutet, dass es 250 m\u00f6gliche Kombinationen gibt. Um ein solches Datenarray zu f\u00fcllen, reicht weder der Speicher noch die Zeit aus. Man k\u00f6nnte einwenden, dass nicht alle Kombinationen realistisch sind und die Benutzer selten mehr als 5-10 Kriterien ausw\u00e4hlen. Ja, es ist m\u00f6glich, ein lazy loading und Caching der Menge nur f\u00fcr das zu machen, was jemals ausgew\u00e4hlt wurde, aber je mehr Auswahlm\u00f6glichkeiten es gibt, desto weniger effektiv wird dieses Cache und desto deutlicher werden die Probleme mit der Reaktionszeit (insbesondere wenn sich der Datensatz regelm\u00e4\u00dfig \u00e4ndert).<\/li>\n<\/ul>\n<p>\nGl\u00fccklicherweise gibt es f\u00fcr eine solche Aufgabe bereits seit langem ausreichend effiziente L\u00f6sungen, die vorhersehbar bei gro\u00dfen Datenmengen funktionieren. F\u00fcr jede dieser Optionen ergibt es Sinn, die Neuberechnung der Facetten und das Abrufen der Ergebnisseite in zwei parallele Anfragen an den Server zu unterteilen und die Benutzeroberfl\u00e4che so zu gestalten, dass das Laden der Facettendaten die Anzeige der Suchergebnisse \"nicht st\u00f6rt\".<\/p>\n<ul>\n<li>Rufen Sie die vollst\u00e4ndige Neuberechnung der \"Facetten\" so selten wie m\u00f6glich auf. Zum Beispiel sollten Sie nicht bei jeder \u00c4nderung der Suchkriterien alle berechnen, sondern stattdessen die Gesamtanzahl der Ergebnisse ermitteln, die den aktuellen Bedingungen entsprechen, und dem Benutzer anbieten, diese anzuzeigen \u2013 \u201e1425 Eintr\u00e4ge gefunden, anzeigen?\u201c Der Benutzer kann entweder weiterhin die Suchkriterien \u00e4ndern oder auf die Schaltfl\u00e4che \u201eanzeigen\u201c klicken. Nur im zweiten Fall werden alle Anfragen zur Ergebnisbeschaffung und zur Neuberechnung der Anzahl der \u201eFacetten\u201c ausgef\u00fchrt. Dabei ist es offensichtlich, dass man mit der Anfrage zur Ermittlung der Gesamtanzahl der Ergebnisse und deren Optimierung umgehen muss. Diese Vorgehensweise findet man in vielen kleinen Online-Shops. Offensichtlich ist dies kein Allheilmittel f\u00fcr das Problem, kann aber in einfachen F\u00e4llen ein guter Kompromiss sein.<\/li>\n<li>Verwenden Sie Suchmaschinen wie Solr, ElasticSearch, Sphinx und andere zur Ergebnissuche und zur Z\u00e4hlung der Facetten. All diese sind darauf ausgelegt, \u201eFacetten\u201c zu generieren, und tun dies dank des invertierten Indexes recht effektiv. Wie Suchmaschinen funktionieren, warum sie in solchen F\u00e4llen effektiver sind als allgemeine Datenbanken, welche Praktiken und Fallstricke es gibt \u2013 das ist ein Thema f\u00fcr einen eigenen Artikel. Hier m\u00f6chte ich darauf hinweisen, dass eine Suchmaschine nicht das Hauptdatenspeicher ersetzen kann; sie wird als Erg\u00e4nzung verwendet: Alle \u00c4nderungen in der Hauptdatenbank, die f\u00fcr die Suche von Bedeutung sind, werden mit dem 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 einer \u00c4nderung in der Hauptdatenbank und deren \u201eErscheinung\u201c in der Suche nicht kritisch ist, kann man einen Dienst erstellen, der alle paar Minuten nach k\u00fcrzlich ge\u00e4nderten Datens\u00e4tzen sucht und diese indexiert. Wenn die minimal m\u00f6gliche Reaktionszeit erforderlich ist, kann man etwas wie <noindex><a rel=\"nofollow\" href=\"https:\/\/microservices.io\/patterns\/data\/transactional-outbox.html\">transactional outbox<\/a><\/noindex> zum Senden von Updates an den Suchdienst implementieren.<\/li>\n<\/ul>\n<p><\/p>\n<h2>Das DBMS Tarantool ist ein attraktives, zukunftstr\u00e4chtiges Produkt zur Erstellung von hochbelasteten Anwendungen.<\/h2>\n<p><\/p>\n<ol>\n<li>Die Umsetzung von Paging auf der Serverseite ist eine ernsthafte Komplexit\u00e4t und sollte nur f\u00fcr schnell wachsende oder einfach gro\u00dfe Datens\u00e4tze sinnvoll eingesetzt werden. Es gibt kein absolut pr\u00e4zises Rezept, um \u00bbgro\u00df\u00ab oder \u00bbschnell wachsend\u00ab zu bewerten, aber ich w\u00fcrde mich an einen solchen Ansatz halten:\n<ul>\n<li>Wenn das Abrufen einer vollst\u00e4ndigen Datensammlung unter Ber\u00fccksichtigung der Serverzeit und der Netzwerk\u00fcbertragung innerhalb der Leistungsanforderungen liegt, macht ein Paging auf der Serverseite keinen Sinn.<\/li>\n<li>Es kann die Situation eintreten, dass in naher Zukunft keine Leistungsprobleme zu erwarten sind, da die Datenmenge gering ist, die Datensammlung jedoch st\u00e4ndig w\u00e4chst. Wenn ein bestimmter Datensatz in der Zukunft m\u00f6glicherweise die vorherige Anforderung nicht mehr erf\u00fcllt, ist es besser, das Paging gleich von Anfang an vorzusehen.<\/li>\n<\/ul>\n<\/li>\n<li>Wenn vonseiten des Gesch\u00e4fts keine strenge Forderung besteht, die Gesamtanzahl der Ergebnisse oder die Seitenzahlen anzuzeigen, und in Ihrem System kein Suchmaschinen-Engine vorhanden ist, ist es besser, diese Punkte nicht zu implementieren und die Variante #2 in Betracht zu ziehen.<\/li>\n<li>Wenn es eine klare Anforderung f\u00fcr facettiertes Suchen gibt, haben Sie zwei M\u00f6glichkeiten, die Leistung nicht zu beeintr\u00e4chtigen:\n<ul>\n<li>Nicht alle Mengen bei jeder \u00c4nderung der Suchkriterien neu zu berechnen.<\/li>\n<li>Suchmaschinen wie Solr, ElasticSearch, Sphinx und andere zu verwenden. Man sollte jedoch verstehen, dass sie die Hauptdatenbank nicht ersetzen k\u00f6nnen und als Erg\u00e4nzung zu dem Hauptspeicher f\u00fcr die L\u00f6sung von Suchanfragen verwendet werden sollten. <\/li>\n<\/ul>\n<\/li>\n<li>Auch im Falle des facettierten Suchens macht es Sinn, das Abrufen der Suchergebnisse 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 die Suche verwenden, sollte jede Code\u00e4nderung in diesem Bereich gut auf Leistung bei den entsprechenden Datenmengen getestet werden (\u00fcber die Menge in einer \u00bblebendigen\u00ab Datenbank). Es ist auch ratsam, die Ausf\u00fchrungszeiten von Abfragen auf allen Datenbankinstanzen zu \u00fcberwachen, insbesondere auf der \u00bblebenden\u00ab. Selbst wenn in der Entwicklungsphase mit den Abfragepl\u00e4nen alles gut war, kann sich die Situation mit wachsender Datenmenge merklich \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 5.0.1.1 - 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.\" \/>\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) 5.0.1.1\" \/>\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.\" \/>\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":"Ein typisches Szenario in all unseren gewohnten Anwendungen ist die Suche nach Daten anhand bestimmter Kriterien und deren Anzeige in einem leicht lesbaren Format.","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.","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","focus_keyword":null,"additional_keywords":null,"truseo_locale":null},"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}]}}