{"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\/ro\/blog\/administrirovanie\/vyvod-rezultatov-poiska-i-problemy-s-proizvoditelnostyu","title":{"rendered":"Rezultatele c\u0103ut\u0103rii \u0219i problemele de performan\u021b\u0103","gt_translate_keys":[{"key":"rendered","format":"text"}]},"content":{"rendered":"<p>Unul dintre scenariile tipice \u00een toate aplica\u021biile cu care suntem obi\u0219nui\u021bi este c\u0103utarea de date dup\u0103 anumite criterii \u0219i afi\u0219area acestora \u00eentr-un format u\u0219or de citit. De asemenea, pot exista func\u021bii suplimentare pentru sortare, grupare \u0219i paginare. Sarcina este, \u00een teorie, trivial\u0103, dar \u00een procesul de implementare mul\u021bi dezvoltatori fac o serie de gre\u0219eli care afecteaz\u0103 ulterior performan\u021ba. S\u0103 \u00eencerc\u0103m s\u0103 discut\u0103m diferitele op\u021biuni de solu\u021bionare a acestei probleme \u0219i s\u0103 formul\u0103m recomand\u0103ri pentru alegerea celei mai eficiente implement\u0103ri.<\/p>\n<p><img decoding=\"async\" alt=\"Rezultatele c\u0103ut\u0103rii \u0219i problemele de performan\u021b\u0103\" 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>Op\u021biunea de paginare #1<\/h2>\n<p>\nCea mai simpl\u0103 varianta care vine \u00een minte este afi\u0219area rezultatelor c\u0103ut\u0103rii pagin\u0103 cu pagin\u0103, \u00een cea mai clasic\u0103 form\u0103 a sa.<\/p>\n<p><img decoding=\"async\" alt=\"Rezultatele c\u0103ut\u0103rii \u0219i problemele de performan\u021b\u0103\" src=\"\/wp-content\/uploads\/2020\/03\/4957d9ad21a8a0c70390b224fe76cb33.jpg\" style=\"display:block;margin: 0 auto;\" \/><br \/>\nS\u0103 presupunem c\u0103 \u00een aplica\u021bie se folose\u0219te o baz\u0103 de date rela\u021bional\u0103. \u00cen acest caz, pentru a afi\u0219a informa\u021biile \u00eentr-un astfel de format, va fi necesar s\u0103 se execute dou\u0103 interog\u0103ri SQL:<\/p>\n<ul>\n<li>Ob\u021bine liniile pentru pagina curent\u0103.<\/li>\n<li>Num\u0103r\u0103 totalul liniilor care corespund criteriilor de c\u0103utare \u2014 acest lucru este necesar pentru a afi\u0219a paginile.<\/li>\n<\/ul>\n<p>\nS\u0103 analiz\u0103m prima interogare folosind baza de date de test MS SQL <noindex><a rel=\"nofollow\" href=\"https:\/\/github.com\/Microsoft\/sql-server-samples\/releases\/download\/adventureworks\/AdventureWorks2016_EXT.bak\">AdventureWorks <\/a><\/noindex>pentru serverul 2016. \u00cen acest scop, vom folosi tabela 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>\nInterogarea de mai sus va returna primele 50 de comenzi din list\u0103, sortate \u00een ordinea descresc\u0103toare a datei ad\u0103ug\u0103rii, adic\u0103 cele mai recente 50 de comenzi.<\/p>\n<p>Aceasta se execut\u0103 rapid pe baza de date de test, dar s\u0103 vedem planul de execu\u021bie \u0219i statisticile de intrare-ie\u0219ire:<\/p>\n<p><img decoding=\"async\" alt=\"Rezultatele c\u0103ut\u0103rii \u0219i problemele de performan\u021b\u0103\" src=\"\/wp-content\/uploads\/2020\/03\/248e4b64593c7000216b46777183d639.jpg\" style=\"display:block;margin: 0 auto;\" \/><\/p>\n<pre><code class=\"plaintext\">Tabel 'SalesOrderHeader'. Num\u0103rul de scan\u0103ri 1, citiri logice 698, citiri fizice 0, citiri anticipate 0, citiri logice lob 0, citiri fizice lob 0, citiri anticipate lob 0.<\/code><\/pre>\n<p>\n<i>Pentru a ob\u021bine statisticile de intrare\/ie\u0219ire pentru fiecare interogare, po\u021bi executa \u00een mediu de execu\u021bie comanda SET STATISTICS IO ON.<\/i><\/p>\n<p>Dup\u0103 cum se poate observa din planul de execu\u021bie, cea mai consumatoare de resurse este sortarea tuturor liniilor din tabelul original dup\u0103 data ad\u0103ug\u0103rii. Problema const\u0103 \u00een faptul c\u0103, cu c\u00e2t apar mai multe linii \u00een tabel, cu at\u00e2t sortarea devine mai 'greoaie'. \u00cen practic\u0103, se recomand\u0103 evitarea unor astfel de situa\u021bii, a\u0219a c\u0103 vom ad\u0103uga un index pe data ad\u0103ug\u0103rii \u0219i vom observa dac\u0103 s-a schimbat consumul de resurse:<\/p>\n<p><img decoding=\"async\" alt=\"Rezultatele c\u0103ut\u0103rii \u0219i problemele de performan\u021b\u0103\" src=\"\/wp-content\/uploads\/2020\/03\/a33e1eadf6f8f8b516b9bb834ad7ad86.jpg\" style=\"display:block;margin: 0 auto;\" \/><\/p>\n<pre><code class=\"plaintext\">Tabel 'SalesOrderHeader'. Num\u0103rul de scan\u0103ri 1, citiri logice 165, citiri fizice 0, citiri anticipate 5, citiri logice lob 0, citiri fizice lob 0, citiri anticipate lob 0.\n<\/code><\/pre>\n<p>\nEvident, a devenit mult mai bine. Dar au fost rezolvate toate problemele? S\u0103 schimb\u0103m interogarea pentru a c\u0103uta comenzile unde valoarea total\u0103 a produselor dep\u0103\u0219e\u0219te 100 de dolari:<\/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=\"Rezultatele c\u0103ut\u0103rii \u0219i problemele de performan\u021b\u0103\" src=\"\/wp-content\/uploads\/2020\/03\/e057bfc89e11a31c6d2855b93ec3c747.jpg\" style=\"display:block;margin: 0 auto;\" \/><\/p>\n<pre><code class=\"plaintext\">Tabela 'SalesOrderHeader'. Num\u0103rul de scan\u0103ri 1, citiri logice 1081, citiri fizice 0, citiri anticipative 0, citiri logice lob 0, citiri fizice lob 0, citiri anticipative lob 0.<\/code><\/pre>\n<p>\nAvem o situa\u021bie amuzant\u0103: planul interog\u0103rii nu este cu mult mai bun dec\u00e2t cel anterior, dar num\u0103rul efectiv de citiri logice este aproape dublu comparativ cu scanarea complet\u0103 a tabelei. Exist\u0103 o solu\u021bie - dac\u0103 din indexul existent facem unul compus \u0219i ad\u0103ug\u0103m ca al doilea c\u00e2mp suma total\u0103 a produselor, vom ob\u021bine din nou 165 de citiri logice:<\/p>\n<pre><code class=\"sql\">CREATE INDEX IX_SalesOrderHeader_OrderDate_SubTotal on Sales.SalesOrderHeader(OrderDate, SubTotal);\n<\/code><\/pre>\n<p>\nAceast\u0103 serie de exemple poate fi continuat\u0103 mult timp, dar cele dou\u0103 idei principale pe care vreau s\u0103 le exprim aici sunt:<\/p>\n<ul>\n<li>Ad\u0103ugarea oric\u0103rui nou criteriu sau ordine de sortare \u00een interogare poate influen\u021ba semnificativ viteza de execu\u021bie a acesteia.<\/li>\n<li>Dar dac\u0103 trebuie s\u0103 extragem doar o parte din date, nu toate rezultatele care corespund criteriilor de c\u0103utare, exist\u0103 multe modalit\u0103\u021bi de a optimiza o astfel de interogare.<\/li>\n<\/ul>\n<p>\nAcum s\u0103 trecem la a doua interogare men\u021bionat\u0103 la \u00eenceput - cea care num\u0103r\u0103 \u00eenregistr\u0103rile care satisfac criteriul de c\u0103utare. S\u0103 lu\u0103m acela\u0219i exemplu - c\u0103utarea comenzilor care dep\u0103\u0219esc 100 de dolari:<\/p>\n<pre><code class=\"sql\">SELECT COUNT(1) FROM Sales.SalesOrderHeader\nWHERE SubTotal &gt; 100\n<\/code><\/pre>\n<p>\nAv\u00e2nd indexul compus men\u021bionat mai sus, ob\u021binem:<\/p>\n<p><img decoding=\"async\" alt=\"Rezultatele c\u0103ut\u0103rii \u0219i problemele de performan\u021b\u0103\" src=\"\/wp-content\/uploads\/2020\/03\/7beab0c83a50e2d68a83a965df555ec9.jpg\" style=\"display:block;margin: 0 auto;\" \/><\/p>\n<pre><code class=\"plaintext\">Tabel 'SalesOrderHeader'. Num\u0103rul de scan\u0103ri 1, citiri logice 698, citiri fizice 0, citiri anticipate 0, citiri logice lob 0, citiri fizice lob 0, citiri anticipate lob 0.<\/code><\/pre>\n<p>\nFaptul c\u0103 interogarea parcurge \u00eentregul index nu este surprinz\u0103tor, deoarece c\u00e2mpul SubTotal nu este pe prima pozi\u021bie, deci interogarea nu poate s\u0103-l foloseasc\u0103. Problema se rezolv\u0103 ad\u0103ug\u00e2nd un alt index pe c\u00e2mpul SubTotal, ceea ce rezult\u0103 \u00een doar 48 de citiri logice.<\/p>\n<p>Mai pot fi aduse c\u00e2teva exemple de interog\u0103ri pentru num\u0103rarea cantit\u0103\u021bii, dar esen\u021ba va r\u0103m\u00e2ne aceea\u0219i: <b>ob\u021binerea unei por\u021bii de date \u0219i num\u0103rarea totalului sunt dou\u0103 interog\u0103ri fundamental diferite<\/b>, \u0219i fiecare necesit\u0103 m\u0103suri specifice pentru optimizare. \u00cen general, nu se va reu\u0219i g\u0103sirea unei combina\u021bii de indec\u0219i care s\u0103 func\u021bioneze la fel de bine pentru ambele interog\u0103ri.<\/p>\n<p>Prin urmare, una dintre cerin\u021bele importante de clarificat \u00een dezvoltarea unor astfel de solu\u021bii de c\u0103utare este dac\u0103 este cu adev\u0103rat important pentru afacere s\u0103 vad\u0103 num\u0103rul total de obiecte g\u0103site. De cele mai multe ori, r\u0103spunsul este nu. Iar navigarea dup\u0103 numerele specifice ale paginilor, din punctul meu de vedere, este o solu\u021bie cu un domeniu de aplicare foarte restr\u00e2ns, deoarece majoritatea scenariilor cu paginarea arat\u0103 ca \u201emergi la pagina urm\u0103toare\u201d.<\/p>\n<h2>Varianta de paginare #2<\/h2>\n<p>\nS\u0103 presupunem c\u0103 utilizatorilor nu le pas\u0103 de num\u0103rul total de obiecte g\u0103site. S\u0103 \u00eencerc\u0103m s\u0103 simplific\u0103m pagina de c\u0103utare:<\/p>\n<p><img decoding=\"async\" alt=\"Rezultatele c\u0103ut\u0103rii \u0219i problemele de performan\u021b\u0103\" src=\"\/wp-content\/uploads\/2020\/03\/66f5ee6dc1a52f14e55ae31e05c502b2.jpg\" style=\"display:block;margin: 0 auto;\" \/><br \/>\nPractic, s-a schimbat doar faptul c\u0103 nu mai exist\u0103 posibilitatea de a naviga dup\u0103 numerele specifice ale paginilor, iar acum aceast\u0103 tabel\u0103 nu trebuie s\u0103 \u0219tie c\u00e2te vor fi \u00een total. \u00cens\u0103 apare \u00eentrebarea \u2014 cum va \u0219ti tabela dac\u0103 exist\u0103 date pentru pagina urm\u0103toare (pentru a afi\u0219a corect linkul \u201eUrm\u0103torul\u201d)?<\/p>\n<p>R\u0103spunsul este foarte simplu: se poate citi din baza de date cu o \u00eenregistrare mai mult dec\u00e2t este necesar pentru a fi afi\u0219at\u0103, iar prezenta acestei \u201e\u00eenregistr\u0103ri suplimentare\u201d va ar\u0103ta dac\u0103 exist\u0103 un nou set de date. Astfel, pentru a ob\u021bine o pagin\u0103 de date, va fi necesar s\u0103 se efectueze doar o singur\u0103 interogare, ceea ce \u00eembun\u0103t\u0103\u021be\u0219te semnificativ performan\u021ba \u0219i faciliteaz\u0103 \u00eentre\u021binerea acestei func\u021bionalit\u0103\u021bi. \u00cen practic\u0103, am avut un caz \u00een care renun\u021barea la num\u0103rarea totalului \u00eenregistr\u0103rilor a accelerat returnarea rezultatelor de 4-5 ori.<\/p>\n<p>Pentru aceast\u0103 abordare exist\u0103 mai multe op\u021biuni de interfa\u021b\u0103 utilizator: comenzi \u201e\u00eenapoi\u201d \u0219i \u201e\u00eenaintare\u201d, ca \u00een exemplul de mai sus, un buton \u201e\u00eencarc\u0103 mai mult\u201d, care adaug\u0103 pur \u0219i simplu un nou set de rezultate, \u201ederulare infinit\u0103\u201d, care func\u021bioneaz\u0103 pe principiul \u201e\u00eencarc\u0103 mai mult\u201d, dar semnalul pentru a ob\u021bine urm\u0103torul set de date este derularea utilizatorului p\u00e2n\u0103 la cap\u0103tul tuturor rezultatelor afi\u0219ate. Indiferent de solu\u021bia vizual\u0103, principiul de selec\u021bie a datelor r\u0103m\u00e2ne acela\u0219i.<\/p>\n<h2>Nuante \u00een implementarea pagin\u0103rii<\/h2>\n<p>\n\u00cen toate exemplele de interog\u0103ri date mai sus, se folose\u0219te abordarea \u201eoffset + num\u0103r\u201d, c\u00e2nd \u00een interogare se indic\u0103 de la ce r\u00e2nd rezultat \u0219i c\u00e2te r\u00e2nduri trebuie returnate. Mai \u00eent\u00e2i, s\u0103 vedem cum ar fi mai bine s\u0103 organiz\u0103m transmiterea parametrilor \u00een acest caz. \u00cen practic\u0103, am \u00eent\u00e2lnit mai multe metode:<\/p>\n<ul>\n<li>Num\u0103rul de ordine al paginii solicitate (pageIndex), dimensiunea paginii (pageSize).<\/li>\n<li>Num\u0103rul de ordine al primei \u00eenregistr\u0103ri care trebuie returnat\u0103 (startIndex), num\u0103rul maxim de \u00eenregistr\u0103ri \u00een rezultat (count).<\/li>\n<li>Num\u0103rul de ordine al primei \u00eenregistr\u0103ri care trebuie returnat\u0103 (startIndex), num\u0103rul de ordine al ultimei \u00eenregistr\u0103ri care trebuie returnat\u0103 (endIndex).<\/li>\n<\/ul>\n<p>\nLa prima vedere, poate p\u0103rea at\u00e2t de elementar \u00eenc\u00e2t nu exist\u0103 nicio diferen\u021b\u0103. Dar nu este a\u0219a \u2014 cea mai convenabil\u0103 \u0219i versatil\u0103 op\u021biune este a doua (startIndex, count). Exist\u0103 c\u00e2teva motive pentru aceasta:<\/p>\n<ul>\n<li>Pentru abordarea cu citirea +1 \u00eenregistrare, men\u021bionat\u0103 mai sus, prima op\u021biune cu pageIndex \u0219i pageSize este extrem de incomod\u0103. De exemplu, dorim s\u0103 afi\u0219\u0103m 50 de \u00eenregistr\u0103ri pe pagin\u0103. Conform algoritmului men\u021bionat anterior, trebuie s\u0103 citim cu una mai mult dec\u00e2t este necesar. Dac\u0103 acest \u201e+1\u201d nu este luat \u00een calcul pe server, se dovede\u0219te c\u0103 pentru prima pagin\u0103 trebuie s\u0103 cerem \u00eenregistr\u0103rile de la 1 la 51, pentru a doua \u2014 de la 51 la 101 etc. Dac\u0103 specific\u0103m dimensiunea paginii 51 \u0219i cre\u0219tem pageIndex, atunci a doua pagin\u0103 va returna de la 52 la 102 etc. \u00cen consecin\u021b\u0103, \u00een prima variant\u0103, singurul mod de a implementa \u00een mod normal butonul de trecere la pagina urm\u0103toare este s\u0103 lu\u0103m \u00een considerare pe server citirea \u201e\u00een plus\u201d a unei \u00eenregistr\u0103ri, ceea ce va fi un detaliu foarte neclar.<\/li>\n<li>A treia variant\u0103 nu are sens deloc, deoarece pentru a efectua cereri \u00een majoritatea bazelor de date, va trebui oricum s\u0103 transmitem num\u0103rul, nu indexul ultimei \u00eenregistr\u0103ri. Fie s\u0103 sc\u0103dem startIndex din endIndex o opera\u021bie aritmetic\u0103 simpl\u0103, dar ea este de prisos aici.<\/li>\n<\/ul>\n<p>\nAcum ar trebui s\u0103 descriem dezavantajele implement\u0103rii pagin\u0103rii prin \u201eoffset + count\u201d:<\/p>\n<ul>\n<li>Ob\u021binerea fiec\u0103rei urm\u0103toare pagini va fi mai costisitoare \u0219i mai lent\u0103 dec\u00e2t cea anterioar\u0103, deoarece baza de date va trebui oricum s\u0103 parcurg\u0103 toate \u00eenregistr\u0103rile \u201ede la \u00eenceput\u201d conform criteriilor de c\u0103utare \u0219i sortare, dup\u0103 care s\u0103 se opreasc\u0103 pe fragmentul dorit.<\/li>\n<li>Nu toate SGBD-urile pot sus\u021bine aceast\u0103 abordare.<\/li>\n<\/ul>\n<p>\nExist\u0103 alternative, dar nici acestea nu sunt ideale. Prima dintre aceste abord\u0103ri se nume\u0219te \u201ekeyset paging\u201d sau \u201emetoda de c\u0103utare\u201d \u0219i const\u0103 \u00een urm\u0103toarele: dup\u0103 ob\u021binerea unei por\u021bii, putem re\u021bine valorile c\u00e2mpurilor din ultima \u00eenregistrare de pe pagin\u0103 \u0219i apoi s\u0103 le folosim pentru a ob\u021bine urm\u0103toarea por\u021bie. De exemplu, am efectuat o astfel de cerere:<\/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\u00cen ultima \u00eenregistrare am ob\u021binut valoarea datei comenzii '2014-06-29'. Atunci, pentru a ob\u021bine urm\u0103toarea pagin\u0103, ar putea fi util s\u0103 \u00eencerc\u0103m urm\u0103toarea abordare:<\/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>\nProblema este c\u0103 OrderDate nu este un c\u00e2mp unic, iar condi\u021bia specificat\u0103 mai sus va omite cu o mare probabilitate multe r\u00e2nduri necesare. Pentru a ad\u0103uga claritate acestei interog\u0103ri, trebuie s\u0103 ad\u0103ug\u0103m un c\u00e2mp unic la condi\u021bie (s\u0103 presupunem c\u0103 75074 este ultima valoare a cheii primare din prima por\u021biune):<\/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>\nAceast\u0103 variant\u0103 va func\u021biona corect, dar, \u00een general, va fi greu de optimizat, deoarece condi\u021bia con\u021bine operatorul OR. Dac\u0103, pe m\u0103sur\u0103 ce OrderDate cre\u0219te, valoarea cheii primare cre\u0219te, atunci condi\u021bia poate fi simplificat\u0103, p\u0103str\u00e2nd doar filtrul dup\u0103 SalesOrderID. Dar dac\u0103 \u00eentre valorile cheii primare \u0219i c\u00e2mpul dup\u0103 care este sortat rezultatul nu exist\u0103 o corela\u021bie strict\u0103, atunci \u00een majoritatea SGBD-urilor nu se va putea evita acest OR. O excep\u021bie pe care o cunosc este PostgreSQL, care suport\u0103 pe deplin compara\u021bia tuplelor, iar condi\u021bia men\u021bionat\u0103 mai sus poate fi scris\u0103 ca \u201eWHERE (OrderDate, SalesOrderID) &lt; (&#039;2014-06-29&#039;, 75074)\u201d. \u00cen cazul unui chei compuse cu aceste dou\u0103 c\u00e2mpuri, astfel de interog\u0103ri ar trebui s\u0103 fie destul de u\u0219oare.<\/p>\n<p>O a doua abordare alternativ\u0103 poate fi \u00eent\u00e2lnit\u0103, de exemplu, \u00een <noindex><a rel=\"nofollow\" href=\"https:\/\/www.elastic.co\/guide\/en\/elasticsearch\/reference\/6.8\/search-request-scroll.html\">ElasticSearch scroll API<\/a><\/noindex> sau <noindex><a rel=\"nofollow\" href=\"https:\/\/medium.com\/@gary.strange\/understanding-cosmosdb-continuation-tokens-hasmoreresults-and-connectionpolicy-requesttimeouts-3ed1fadfa81d\">Cosmos DB<\/a><\/noindex> \u2014 c\u00e2nd interogarea, pe l\u00e2ng\u0103 date, returneaz\u0103 un identificator special, cu ajutorul c\u0103ruia se poate ob\u021bine urm\u0103toarea por\u021biune de date. Dac\u0103 acest identificator are o durat\u0103 de via\u021b\u0103 nelimitat\u0103 (a\u0219a cum este \u00een Cosmos DB), atunci este o metod\u0103 excelent\u0103 de implementare a pagin\u0103rii cu tranzi\u021bii secven\u021biale \u00eentre pagini (varianta #2 men\u021bionat\u0103 mai sus). Posibilele sale dezavantaje: nu este sus\u021binut \u00een toate SGBD-urile; identificatorul ob\u021binut pentru urm\u0103toarea por\u021biune poate avea o durat\u0103 de via\u021b\u0103 limitat\u0103, ceea ce, \u00een general, nu se potrive\u0219te pentru interac\u021biunea utilizatorului (precum, de exemplu, ElasticSearch scroll API).<\/p>\n<h2>Filtrare complex\u0103<\/h2>\n<p>\nS\u0103 complic\u0103m pu\u021bin lucrurile. S\u0103 presupunem c\u0103 a ap\u0103rut cerin\u021ba de a implementa a\u0219a-numitul faceted search, foarte cunoscut din magazinele online. Exemplele anterioare bazate pe tabela comenzilor nu sunt foarte relevante \u00een acest caz, a\u0219a c\u0103 ne vom concentra pe tabela Product din baza de date AdventureWorks:<\/p>\n<p><img decoding=\"async\" alt=\"Rezultatele c\u0103ut\u0103rii \u0219i problemele de performan\u021b\u0103\" src=\"\/wp-content\/uploads\/2020\/03\/070890a3cb79de3edc69b60b5300efe7.jpg\" style=\"display:block;margin: 0 auto;\" \/><br \/>\nCare este ideea faceted search? S\u0103 arate pentru fiecare element de filtrare num\u0103rul de \u00eenregistr\u0103ri care corespund acestui criteriu. <i>\u021bin\u00e2nd cont de filtrele selectate \u00een toate celelalte categorii.<\/i>.<\/p>\n<p>De exemplu, dac\u0103 vom alege \u00een acest exemplu categoria Bikes \u0219i culoarea Black, tabela va afi\u0219a doar biciclete de culoare neagr\u0103, dar \u00een acela\u0219i timp:<\/p>\n<ul>\n<li>Pentru fiecare criteriu din grupul \u201eCategories\u201d va fi ar\u0103tat num\u0103rul de produse din aceast\u0103 categorie de culoare neagr\u0103.<\/li>\n<li>Pentru fiecare criteriu din grupul \u201eColors\u201d va fi ar\u0103tat num\u0103rul de biciclete de aceast\u0103 culoare.<\/li>\n<\/ul>\n<p>\nIat\u0103 un exemplu de ie\u0219ire a rezultatului pentru astfel de condi\u021bii:<\/p>\n<p><img decoding=\"async\" alt=\"Rezultatele c\u0103ut\u0103rii \u0219i problemele de performan\u021b\u0103\" src=\"\/wp-content\/uploads\/2020\/03\/7c8ec4d8b427f1f59f0129afebe74b08.jpg\" style=\"display:block;margin: 0 auto;\" \/><br \/>\nDac\u0103, \u00een plus, vom marca categoria \u201eClothing\u201d, tabela va ar\u0103ta \u0219i \u00eembr\u0103c\u0103minte de culoare neagr\u0103 disponibil\u0103. Num\u0103rul de produse de culoare neagr\u0103 din sec\u021biunea \u201eColor\u201d va fi, de asemenea, recalculat conform noilor condi\u021bii, \u00eens\u0103 \u00een sec\u021biunea \u201eCategories\u201d nu se va schimba nimic... Sper c\u0103 aceste exemple sunt suficiente pentru a \u00een\u021belege algoritmul familiar al func\u021bion\u0103rii faceted search.<\/p>\n<p>Acum s\u0103 ne imagin\u0103m cum ar putea fi implementat acest lucru \u00eentr-o baz\u0103 de date rela\u021bional\u0103. Fiecare grup de criterii, cum ar fi Category \u0219i Color, va necesita o interogare separat\u0103:<\/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=\"Rezultatele c\u0103ut\u0103rii \u0219i problemele de performan\u021b\u0103\" 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 --Bikes\nGROUP BY Color\nORDER BY COUNT(1) DESC\n<\/code><\/pre>\n<p>\n<img decoding=\"async\" alt=\"Rezultatele c\u0103ut\u0103rii \u0219i problemele de performan\u021b\u0103\" src=\"\/wp-content\/uploads\/2020\/03\/82e1e6ab0fae683d383e3fecf4e3a15a.jpg\" style=\"display:block;margin: 0 auto;\" \/><br \/>\nCe este \u00een neregul\u0103 cu aceast\u0103 solu\u021bie? Foarte simplu \u2014 aceasta nu se scalabilizeaz\u0103 bine. Fiecare sec\u021biune de filtrare necesit\u0103 o interogare separat\u0103 pentru a num\u0103ra cantit\u0103\u021bile \u0219i aceste interog\u0103ri nu sunt cele mai u\u0219oare. \u00cen magazinele online, \u00een unele categorii, pot fi \u0219i c\u00e2teva zeci de sec\u021biuni de filtrare, ceea ce poate reprezenta o problem\u0103 serioas\u0103 pentru performan\u021b\u0103.<\/p>\n<p>De obicei, dup\u0103 aceste afirma\u021bii, mi se ofer\u0103 anumite solu\u021bii, \u0219i anume:<\/p>\n<ul>\n<li>Combina\u021bi toate num\u0103r\u0103toarele \u00eentr-o singur\u0103 interogare. Tehnic este posibil prin utilizarea cuv\u00e2ntului cheie UNION, totu\u0219i, acest lucru nu va ajuta semnificativ la performan\u021b\u0103 \u2014 baza de date va trebui s\u0103 execute \u201ede la zero\u201d fiecare dintre fragmente.<\/li>\n<li>Cache-a\u021bi num\u0103r\u0103torile. Aceasta mi se sugereaz\u0103 practic de fiecare dat\u0103 c\u00e2nd descriu problema. Nuanta este c\u0103, \u00een general, acest lucru este imposibil. S\u0103 presupunem c\u0103 avem 10 \u201efa\u021bete\u201d, fiecare cu 5 valori. Aceasta este o situa\u021bie foarte \u201emodest\u0103\u201d \u00een compara\u021bie cu ceea ce se poate observa \u00een magazinele online. Selectarea unui element de fa\u021bet\u0103 influen\u021beaz\u0103 num\u0103r\u0103toarele \u00een celelalte 9, cu alte cuvinte, pentru fiecare combina\u021bie de criterii, num\u0103r\u0103toarele pot fi diferite. \u00cen total, \u00een exemplul nostru, sunt 50 de criterii pe care utilizatorul le poate selecta, a\u0219adar, vor exista 250 de combina\u021bii posibile. Complectarea unei astfel de matrice de date nu va fi suportat\u0103 de nici o memorie sau timp. Aici se poate obiecta \u0219i spune c\u0103 nu toate combina\u021biile sunt reale \u0219i utilizatorul rareori va selecta mai mult de 5-10 criterii. Da, se poate face o \u00eenc\u0103rcare lene\u0219\u0103 \u0219i cache-a num\u0103r\u0103toarele doar pentru cei care au fost selecta\u021bi vreodat\u0103, dar cu c\u00e2t vor fi mai multe op\u021biuni de selectat, cu at\u00e2t mai pu\u021bin eficient va fi acest cache \u0219i cu at\u00e2t mai vizibile vor fi problemele de timp de r\u0103spuns (mai ales dac\u0103 setul de date se schimb\u0103 regulat).<\/li>\n<\/ul>\n<p>\nDin fericire, o astfel de sarcin\u0103 are deja de mult timp solu\u021bii destul de eficiente, previzibile \u00een gestionarea unor volume mari de date. Pentru oricare dintre aceste op\u021biuni, are sens s\u0103 separa\u021bi recalcularea fa\u021betelor \u0219i ob\u021binerea paginii de rezultate \u00een dou\u0103 apeluri paralele c\u0103tre server \u0219i s\u0103 organiza\u021bi interfa\u021ba utilizatorului astfel \u00eenc\u00e2t \u00eenc\u0103rcarea datelor pe fa\u021bete s\u0103 \u201enu interfereze\u201d cu afi\u0219area rezultatelor c\u0103ut\u0103rii.<\/p>\n<ul>\n<li>Chem mai rar s\u0103 recalcula\u021bi \u201efacetele\u201d. De exemplu, nu recalcula\u021bi totul la fiecare modificare a criteriilor de c\u0103utare, ci, \u00een schimb, g\u0103si\u021bi num\u0103rul total de rezultate care se potrivesc condi\u021biilor actuale \u0219i oferi\u021bi utilizatorului op\u021biunea de a le afi\u0219a \u2014 \u201e1425 de \u00eenregistr\u0103ri g\u0103site, dori\u021bi s\u0103 le afi\u0219a\u021bi?\u201d Utilizatorul poate continua s\u0103 schimbe condi\u021biile de c\u0103utare sau poate ap\u0103sa butonul \u201eafi\u0219a\u021bi\u201d. Doar \u00een acest caz se vor executa toate solicit\u0103rile pentru a ob\u021bine rezultatele \u0219i pentru a recalcula cantit\u0103\u021bile de pe toate \u201efacetele\u201d. Este evident c\u0103 va trebui s\u0103 gestiona\u021bi solicitarea de ob\u021binere a num\u0103rului total de rezultate \u0219i optimizarea acesteia. Aceast\u0103 metod\u0103 poate fi \u00eent\u00e2lnit\u0103 \u00een multe magazine online mici. Este clar c\u0103 nu este o solu\u021bie universal\u0103, dar \u00een cazuri simple poate fi un compromis bun.<\/li>\n<li>Folosi\u021bi motoare de c\u0103utare pentru a g\u0103si rezultate \u0219i a calcula facetele, cum ar fi Solr, ElasticSearch, Sphinx \u0219i altele. Toate acestea sunt configurate pentru a construi \u201efacete\u201d \u0219i fac acest lucru destul de eficient, datorit\u0103 indexului invers. Cum func\u021bioneaz\u0103 motoarele de c\u0103utare, de ce sunt mai eficiente \u00een aceste cazuri dec\u00e2t bazele de date de utilizare general\u0103, ce practici \u0219i capcane exist\u0103 \u2014 aceasta este o tem\u0103 pentru un articol aparte. Aici vreau s\u0103 subliniez c\u0103 motorul de c\u0103utare nu poate \u00eenlocui depozitul principal de date, ci este folosit ca un supliment: orice modific\u0103ri \u00een baza principal\u0103, care sunt relevante pentru c\u0103utare, sunt sincronizate \u00een indexul de c\u0103utare; mecanismul de c\u0103utare interac\u021bioneaz\u0103 de obicei doar cu motorul de c\u0103utare \u0219i nu se refer\u0103 la baza principal\u0103. Unul dintre cele mai importante aspecte aici este cum s\u0103 organiza\u021bi aceast\u0103 sincronizare \u00eentr-un mod fiabil. Totul depinde de cerin\u021bele pentru \u201etimpul de reac\u021bie\u201d. Dac\u0103 timpul dintre modificarea din baza principal\u0103 \u0219i \u201eapari\u021bia\u201d acesteia \u00een c\u0103utare nu este critic, se poate crea un serviciu care caut\u0103 \u00eenregistr\u0103rile recent modificate \u0219i le indexeaz\u0103 o dat\u0103 la c\u00e2teva minute. Dac\u0103 este necesar un timp de reac\u021bie c\u00e2t mai mic posibil, se poate implementa ceva de genul <noindex><a rel=\"nofollow\" href=\"https:\/\/microservices.io\/patterns\/data\/transactional-outbox.html\">outbox tranzac\u021bional<\/a><\/noindex> pentru a trimite actualiz\u0103ri c\u0103tre serviciul de c\u0103utare.<\/li>\n<\/ul>\n<p><\/p>\n<h2>Conclusions<\/h2>\n<p><\/p>\n<ol>\n<li>Implementarea pagin\u0103rii pe partea serverului este o complexitate serioas\u0103, \u0219i are sens s\u0103 o aplici doar pentru seturi de date \u00een cre\u0219tere rapid\u0103 sau pur \u0219i simplu mari. Cum s\u0103 evaluezi \"mare\" sau \"\u00een cre\u0219tere rapid\u0103\" \u2014 nu exist\u0103 o re\u021bet\u0103 absolut precis\u0103, dar a\u0219 adopta urm\u0103toarea abordare:\n<ul>\n<li>Dac\u0103 ob\u021binerea \u00eentregii colec\u021bii de date, \u021bin\u00e2nd cont de timpul serverului \u0219i de transmiterea prin re\u021bea, se \u00eencadreaz\u0103 normal \u00een cerin\u021bele de performan\u021b\u0103 \u2014 nu are sens s\u0103 implementezi paginarea pe partea serverului.<\/li>\n<li>Poate fi o situa\u021bie \u00een care, pentru urm\u0103toarea perioad\u0103, nu se preconizeaz\u0103 probleme de performan\u021b\u0103, deoarece sunt pu\u021bine date, dar colec\u021bia de date cre\u0219te constant. Dac\u0103 un anumit set de date ar putea \u00eenceta \u00een viitor s\u0103 satisfac\u0103 punctul anterior \u2014 este mai bine s\u0103 planifici paginarea de la \u00eenceput.<\/li>\n<\/ul>\n<\/li>\n<li>Dac\u0103 din partea afacerii nu exist\u0103 o cerin\u021b\u0103 strict\u0103 pentru a afi\u0219a num\u0103rul total de rezultate sau pentru a afi\u0219a numerele paginilor, iar \u00een sistemul t\u0103u nu exist\u0103 un motor de c\u0103utare \u2014 mai bine s\u0103 nu implementezi aceste aspecte \u0219i s\u0103 consideri varianta #2.<\/li>\n<li>Dac\u0103 exist\u0103 o cerin\u021b\u0103 clar\u0103 pentru c\u0103utarea faceted, ai dou\u0103 op\u021biuni pentru a nu sacrifica performan\u021ba:\n<ul>\n<li>S\u0103 nu recalculezi toate cantit\u0103\u021bile la fiecare modificare a criteriilor de c\u0103utare.<\/li>\n<li>S\u0103 folose\u0219ti motoare de c\u0103utare precum Solr, ElasticSearch, Sphinx \u0219i altele. Dar trebuie s\u0103 \u00een\u021belegi c\u0103 acestea nu pot \u00eenlocui baza de date principal\u0103 \u0219i ar trebui utilizate ca un supliment la stocarea principal\u0103 pentru a rezolva problemele de c\u0103utare. <\/li>\n<\/ul>\n<\/li>\n<li>De asemenea, \u00een cazul c\u0103ut\u0103rii faceted, are sens s\u0103 separi ob\u021binerea paginii de rezultate ale c\u0103ut\u0103rii \u0219i num\u0103rarea cantit\u0103\u021bilor \u00een dou\u0103 cereri paralele. Num\u0103rarea cantit\u0103\u021bilor poate dura mai mult dec\u00e2t ob\u021binerea rezultatelor, \u00een timp ce rezultatele sunt mai importante pentru utilizator.<\/li>\n<li>Dac\u0103 folose\u0219ti o baz\u0103 de date SQL pentru c\u0103utare, orice modificare a codului referitoare la aceast\u0103 parte trebuie s\u0103 fie testat\u0103 bine \u00een ceea ce prive\u0219te performan\u021ba pe un volum de date corespunz\u0103tor (care dep\u0103\u0219e\u0219te volumul din baza \"vie\"). De asemenea, este de dorit s\u0103 folose\u0219ti monitorizarea timpului de execu\u021bie a cererilor pe toate instan\u021bele bazei, \u0219i \u00een special \u2014 pe cea \"viu\". Chiar dac\u0103 \u00een etapa de dezvoltare planurile cererilor erau bune, pe m\u0103sur\u0103 ce volumul de date cre\u0219te, situa\u021bia se poate schimba semnificativ.<\/li>\n<\/ol>\n<p>Sursa: <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.2.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\/ro\/blog\/administrirovanie\/vyvod-rezultatov-poiska-i-problemy-s-proizvoditelnostyu\" \/>\n\t<meta name=\"generator\" content=\"All in One SEO (AIOSEO) 5.0.2.1\" \/>\n\t\t<meta property=\"og:locale\" content=\"ro_RO\" \/>\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\/ro\/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\udd47Afi\u0219area rezultatelor c\u0103ut\u0103rii \u0219i problemele de performan\u021b\u0103 | ProHoster","description":"Unul dintre scenariile tipice \u00een toate aplica\u021biile pe care le folosim este c\u0103utarea datelor dup\u0103 criterii specifice \u0219i afi\u0219area acestora \u00eentr-un format u\u0219or de citit.","canonical_url":"https:\/\/prohoster.info\/ro\/blog\/administrirovanie\/vyvod-rezultatov-poiska-i-problemy-s-proizvoditelnostyu","robots":"max-image-preview:large","keywords":"","webmasterTools":{"miscellaneous":""},"schema":null,"og:locale":"ro_RO","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\/ro\/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\/ro\/wp-json\/wp\/v2\/posts\/75530","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/prohoster.info\/ro\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/prohoster.info\/ro\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/prohoster.info\/ro\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/prohoster.info\/ro\/wp-json\/wp\/v2\/comments?post=75530"}],"version-history":[{"count":0,"href":"https:\/\/prohoster.info\/ro\/wp-json\/wp\/v2\/posts\/75530\/revisions"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/prohoster.info\/ro\/wp-json\/wp\/v2\/media\/75531"}],"wp:attachment":[{"href":"https:\/\/prohoster.info\/ro\/wp-json\/wp\/v2\/media?parent=75530"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/prohoster.info\/ro\/wp-json\/wp\/v2\/categories?post=75530"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/prohoster.info\/ro\/wp-json\/wp\/v2\/tags?post=75530"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}