Optimizarea interogărilor pentru elementul următor și precedent

voturi
28

Caut cel mai bun mod de a prelua înregistrările următoare și anterioare ale unei înregistrări fără a executa o interogare completă. Am o soluție complet pusă în aplicare în loc, și ar dori să știe dacă există abordări mai bine pentru a face acest lucru acolo.

Să presupunem că suntem construirea unui site web pentru o zarzavagiu fictive. În plus față de paginile sale HTML, în fiecare săptămână, el vrea să publice o listă de oferte speciale pe site-ul său. El vrea aceste oferte de a locui într-un tabel de baze de date reale, iar utilizatorii trebuie să fie în măsură să sorteze ofertele în trei moduri.

Fiecare element are , de asemenea , să aibă o pagină de detaliu cu mai multe informații, textuale privind oferta și butoanele „ pentru următoarele“ „anterioare“ și. Accesând „anterioară“ și butoanele „next“ trebuie să indice intrările învecinate în funcție de sortare utilizatorul a ales pentru lista .

alt text http://www.pekkagaiser.com/stuff/Sort.gif?

Evident, butonul „Next“ pentru „Tomate, clasa I“ trebuie să fie „Mere, clasa 1“ în primul exemplu, „perele, clasa I“, în al doilea, și nici unul în al treilea.

Sarcina în vederea în detaliu este de a determina elementele următoare și anterioare , fără a efectua o interogare de fiecare dată , cu ordinea de sortare a listei ca singurele informații disponibile (Să presupunem că obținem că printr - un parametru GET ?sort=offeroftheweek_priceși va ignora implicațiile de securitate) .

Evident, pur și simplu, care trece ID-urile elementelor următoare și anterioare ca parametru este prima soluție care vine în minte. La urma urmei, știm deja de identitate de la acest punct. Dar, acest lucru nu este o opțiune aici - ar lucra în acest exemplu simplificat, dar nu și în multe dintre mele cazuri de utilizare din lumea reală.

Abordarea mea actuală în CMS meu este folosind ceva ce am numit „sortare cache“. Când o listă este încărcată, am păstra pozițiile de elemente în înregistrări într - un tabel cu numele sortingcache.

name (VARCHAR)             items (TEXT)

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

Evident, itemscoloana este foarte populat cu ID - uri numerice.

În pagina de detalii, am acum acces corespunzătoare sortingcacheînregistrare, adu itemscoloana, exploda, caută ID - ul elementului curent, și să se întoarcă vecinul anterior și următor.

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

Acest lucru este , evident , scump, lucrează pentru un număr limitat de înregistrări și creează date redundante, dar să presupunem că în lumea reală, interogarea pentru a crea liste este foarte scump (este), care rulează - l în fiecare vizualizare detaliu este în afara întrebarea, iar unele este nevoie de cache.

Intrebarile mele:

  • Crezi că aceasta este o bună practică pentru a afla înregistrările vecine, pentru diverse ordine de interogare?

  • Știi practici mai bune în ceea ce privește performanța și simplitatea? Știi ceva ce o face acest lucru complet depășite?

  • În teorie de programare, există un nume pentru această problemă?

  • Este numele de „cache de sortare“ este adecvat și ușor de înțeles pentru această tehnică?

  • Există recunoscute, modele comune pentru a rezolva această problemă? Cum se numesc ei?

Notă: Întrebarea mea nu este vorba despre construirea listă, sau cum pentru a afișa vizualizarea în detaliu. Acestea sunt doar exemple. Întrebarea mea este funcționalitatea de bază de a determina vecinii unei înregistrări atunci când o re-interogare este imposibilă, iar cel mai rapid și mai ieftin mod de a ajunge acolo.

Dacă ceva este neclar, vă rugăm să lăsați un comentariu și voi clarifica.

Pornind de o recompensă - poate că există unele mai multe informații despre acest lucru acolo.

Întrebat 22/02/2010 la 12:06
sursa de către utilizator
În alte limbi...                            


11 răspunsuri

voturi
-3

Deci, aveți două sarcini:

  1. construi lista sortată de articole (cu diferite ORDER SELECTS BY)
  2. arată detalii despre fiecare element (SELECT detalii din baza de date cu posibile cache).

Care este problema?

PS: în cazul în care lista ordonata poate fi prea mare, ai nevoie doar de funcționalitate PAGER puse în aplicare. Ar putea fi implementări diferite, de exemplu, ați putea dori să adăugați „limita de 5“ în interogare și să ofere „Afișați următoarele 5“ buton. Atunci când este apăsat acest buton „unde prețul <0,89 limita de 5“, se adaugă condiția ca.

Publicat 22/02/2010 la 15:04
sursa de către utilizator

voturi
16

Aici este o idee. Ai putea abate scumpe operațiunile la o actualizare atunci când inserțiile băcanul / actualizări oferte noi, mai degrabă decât atunci când utilizatorul final selectează datele pentru a vizualiza. Acest lucru poate părea ca un mod non-dinamic pentru a gestiona datele de sortare, dar poate crește viteza. Și, după cum știm, există întotdeauna un compromis între performanță și alți factori de codificare.

Creați un tabel pentru a organiza următoare și anterioară pentru fiecare ofertă și pentru fiecare opțiune de sortare. (Alternativ, puteți stoca în tabelul de ofertă, dacă veți avea întotdeauna trei opțiuni de sortare - viteza de interogare este un motiv bun pentru a Denormalizați baza de date)

Deci, v-ar avea aceste coloane:

  • Tip de sortare (nesortate, Pret, Clasa și Preț Descărca)
  • ID-ul ofertei
  • Anterior ID
  • ID-ul următor

În cazul în care informațiile detaliate pentru pagina de oferta detaliu este interogată din baza de date, NextID și PrevID ar fi o parte a rezultatelor. Deci, ai nevoie doar de o interogare pentru fiecare pagină cu detalii.

De fiecare dată când este introdus, actualizat sau șters o ofertă, va trebui să rulați un proces care validează integritatea / exactitatea tabelului sorttype.

Publicat 22/02/2010 la 20:20
sursa de către utilizator

voturi
1

Nu sunt sigur dacă am înțeles bine, așa că, dacă nu, trebuie doar să-mi spui;)

Să spunem, că Givens sunt interogarea pentru lista sortată și curentul de offset în această listă, adică avem $queryși un $n.

O soluție foarte evidentă pentru a minimiza interogările, ar fi să-i aducă toate datele dintr-o dată:

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

Această afirmație preia anterior, curent și următoarele elemente din baza de date în ordinea de sortare curentă și pune informațiile asociate în variabilele corespunzătoare.

Dar, așa cum această soluție este prea simplu, presupun că am înțeles greșit ceva.

Publicat 07/02/2011 la 20:31
sursa de către utilizator

voturi
2

Am avut coșmaruri cu asta, de asemenea. Abordarea ta actuală pare a fi cea mai bună soluție chiar și pentru listele de 10k articole. Caching ID - urile vizualizarea listă în sesiunea http și apoi utilizând că pentru afișarea (personalizat pentru utilizator curent) anterior / următor. Acest lucru funcționează bine mai ales atunci când există prea multe moduri de a filtra și sorta lista inițială de elemente în loc de doar 3. De
asemenea, prin stocarea toata lista ID - uri veți obține pentru a afișa un "you are at X out of Y"text uzabilitate creștere.
JIRA lui anterioară / următoare

Apropo, acest lucru este ceea ce JIRA face la fel de bine.

Pentru a răspunde direct la întrebările dumneavoastră:

  • Da, este o bună practică, deoarece cântare, fără nici o complexitate de cod adăugat atunci când filtrul / sortare și tipuri de elemente de ciori mai complexe. Eu o folosesc într-un sistem de producție cu 250K articole cu „infinit“ variante de filtru / sortare. Tunderea ID-urile cacheable la 1000 este, de asemenea, o posibilitate, deoarece utilizatorul va fi cel mai probabil, nu faceți clic pe precedentă sau următoare mai mult de 500 de ori (El va merge cel mai probabil, înapoi și rafinarea căutării sau paginate).
  • Nu știu de un mod mai bun. Dar, în cazul în care soiurile limitate în cazul în care și acesta a fost un loc public (cu nici o sesiune http), atunci aș cel mai probabil Denormalizați.
  • Habarnam.
  • Da, cache-ul de sortare sună bine. În proiectul meu eu o numesc „anterior / pe rezultatele de căutare“ sau „navigare pe rezultatele de căutare“.
  • Habarnam.
Publicat 07/02/2011 la 21:04
sursa de către utilizator

voturi
2

În general, am Denormalizați datele de indicii. Acestea pot fi stocate în aceleași rânduri, dar am prelua aproape întotdeauna ID-urile mele de rezultat, apoi face o excursie separată pentru date. Acest lucru face ca cache datele foarte simplu. Nu este atât de important în PHP în cazul în care latenta este scăzută și de lățime de bandă mare, dar o astfel de strategie este foarte util atunci când aveți o latență ridicată, aplicarea în bandă îngustă, cum ar fi un site web AJAX în cazul în care o mare parte a site-ului este redat în JavaScript.

Am cache întotdeauna listele de rezultate, și s-au separat rezultatele. Dacă ceva afectează rezultatele unei interogări de listă, cache-ul din lista rezultatelor este reîmprospătată. Dacă ceva afectează rezultatele ei înșiși, aceste rezultate deosebite sunt reîmprospătate. Acest lucru îmi permite să actualizeze fie unul, fără a fi nevoie de a se regenera totul, rezultând în cache eficientă.

Deoarece listele mele de rezultate se schimbă rar, generez toate listele în același timp. Acest lucru poate face ca răspunsul inițial ușor mai lent, dar simplifică cache reconfortantă (toate listele sunt stocate într-o singură intrare cache).

Pentru că am lista intreaga cache, este banal pentru a găsi elemente vecine, fără revizitarea baza de date. Cu puțin noroc, datele pentru aceste elemente vor fi, de asemenea, în cache. Acest lucru este util mai ales atunci când sortarea datelor în JavaScript. Dacă am deja o copie în cache pe client, pot apela instantaneu.

Pentru a răspunde la întrebările dumneavoastră în mod specific:

  • Da, este o idee fantastica pentru a afla vecinii înainte de timp, sau orice informații pe care clientul ar putea avea acces la viitor, mai ales în cazul în care costul este redus acum, iar costul pentru a recalcula este ridicat. Apoi, este pur și simplu un compromis de suplimentare de pre-calcul și de stocare față de viteză.
  • În ceea ce privește performanța și simplitatea, să evite lucrurile de vânzări legate împreună, care sunt în mod logic lucruri diferite. Indexează și datele sunt diferite, sunt susceptibile de a fi modificate la momente diferite (de exemplu, adăugarea unui nou de origine va afecta indicii, dar nu și datele existente), și, astfel, ar trebui să fie accesate separat. Acest lucru poate fi ușor mai puțin eficiente dintr-un singur fir punct de vedere, dar, de fiecare dată când cravată ceva împreună, ai pierdut cachingul eficiența și asychronosity (cheia de scalare este asychronosity).
  • Termenul pentru obținerea de date înainte de timp este pre preluarea în avans. Preluarea în avans se poate întâmpla în momentul de acces sau în fundal, dar înainte este, de fapt nevoie de datele preluate în avans. De asemenea, cu pre-calcul. Este un compromis al costurilor acum, costul de stocare și costul pentru a obține atunci când este necesar.
  • „Cache de sortare“ este un nume apt.
  • Nu știu.

De asemenea, atunci când cache lucruri, le cache la nivelul cel mai generic posibil. Unele lucruri ar putea fi specifice de utilizator (cum ar fi rezultate pentru o interogare de căutare), în cazul în care alții ar putea fi utilizator agnostic, cum ar fi navigarea un catalog. Ambele pot beneficia de cache. Catalog de Interogarea ar putea fi frecvente și de a salva un pic de fiecare dată, iar interogarea de căutare poate fi costisitoare și de a salva o mulțime de câteva ori.

Publicat 09/02/2011 la 08:00
sursa de către utilizator

voturi
0

Sunt la fel de multe moduri de a face acest lucru pentru piele pisica proverbiala. Deci, aici sunt un cuplu de-al meu.

Dacă interogarea inițială este scump, care spun că este, apoi să creați un alt tabel, eventual, o tabelă de memorie popularea cu rezultatele scumpe și rareori rula interogarea principală.

Acest al doilea tabel poate fi interogate în fiecare vizualizare și sortarea este la fel de simplu ca setarea ordinea de sortare corespunzătoare.

După cum este necesar repopula al doilea tabel cu rezultatele din primul tabel, păstrând astfel datele proaspete, dar reducerea la minimum utilizarea costisitoare interogare.

De asemenea, dacă doriți să evitați chiar conectarea la db, atunci ai putea stoca toate datele într-o matrice PHP și păstrați-l folosind memcached. acest lucru ar fi foarte rapid și cu condiția ca listele nu au fost prea mare va fi eficientă a resurselor. și poate fi ușor sortate.

DC

Publicat 11/02/2011 la 05:19
sursa de către utilizator

voturi
0

Presupuneri de baza:

  • Speciale sunt pe săptămână
  • Ne putem aștepta ca site-ul pentru a schimba rar ... probabil, de zi cu zi?
  • Putem controla actualizările la baza de date cu eter un API sau răspunde prin declanșatoare

În cazul în care site-ul se schimbă pe o bază de zi cu zi, am sugera că toate paginile sunt generate peste noapte static. O interogare pentru fiecare iterează sortare-comanda prin intermediul și face toate paginile aferente. Chiar dacă există elemente dinamice, șansele sunt că le puteți adresa prin includerea elementelor de pagină statică. Acest lucru ar oferi servicii pagina optimă și fără sarcină de bază de date. De fapt, ai putea genera, eventual, pagini separate și prev / următoarele elemente care sunt incluse în paginile. Acest lucru poate fi mai nebun cu 200 de moduri de sortare, dar cu 3 eu sunt un mare fan al ei.

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

Dacă, din anumite motive, acest lucru nu este posibil, aș recurge la memorizarea. Memcache este popular pentru acest tip de lucru (joc de cuvinte!). Când ceva este împins la baza de date, puteți emite un declanșator pentru a actualiza memoria cache cu valorile corecte. Faceți acest lucru în același mod în care, dacă ar fi ca și în cazul în care elementul actualizat exista în 3 liste legate - reconecta, după caz ​​(this.next.prev = this.prev, etc). Din faptul că, atâta timp cât memoria cache nu supraîncărcați, vei trage valori simple din memorie într-un mod cheie primară.

Această metodă va dura ceva de codificare suplimentare pe selectați și actualizare / inserare metode, dar ar trebui să fie destul de minim. În final, veți fi în căutarea în sus [id of tomatoes class 1].price.next. În cazul în care cheia se află în memoria cache, de aur. Dacă nu, introduceți în memoria cache și afișare.

  • Crezi că aceasta este o bună practică pentru a afla înregistrările vecine , pentru diverse ordine de interogare? Da. Este înțelept să efectueze uite-Aheads cu privire la cererile viitoare așteptate.
  • Știi practici mai bune în ceea ce privește performanța și simplitatea? Știi ceva ce o face acest lucru complet depășite? Să sperăm că cele de mai sus
  • În teorie de programare, există un nume pentru această problemă? Optimizare?
  • Este numele de „cache de sortare“ este adecvat și ușor de înțeles pentru această tehnică? Nu sunt sigur de un nume potrivit specific. Este cache, este un cache de soiuri, dar eu nu sunt sigur că - mi spui că aveți un „cache de sortare“ ar transmite înțelegere instantanee.
  • Există recunoscute, modele comune pentru a rezolva această problemă? Cum se numesc ei? Caching?

Ne pare rău răspunsurile mele sunt un fel de steril inutil, dar cred că soluțiile mele narative ar trebui să fie destul de util.

Publicat 11/02/2011 la 18:13
sursa de către utilizator

voturi
0

Ai putea salva numerele rândurilor din listele ordonate în punctele de vedere , și ați putea ajunge la elementele anterioare și următoare din lista cu numerele de rând (current_rownum + 1) (current_rownum-1) și.

Publicat 12/02/2011 la 14:01
sursa de către utilizator

voturi
0

Problema / datastructur este numit grafic bi-direcțională sau ai putea spune că ai mai multe liste legate.

Dacă stai să te gândești ca o listă înlănțuită, ați putea adăuga doar câmpuri la tabelul de elemente pentru fiecare sortare și cheie prev / next. Dar DB persoana pe care o va ucide pentru asta, e ca GOTO.

Dacă stai să te gândești ca (bi-) grafic direcțională, te duci cu răspunsul lui Jessica. Problema principală este că actualizările de ordine sunt operații costisitoare.

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

Dacă schimbați poziția un produs pentru noua ordine A, C, B, D, va trebui să actualizeze 4 rânduri.

Publicat 13/02/2011 la 02:20
sursa de către utilizator

voturi
4

Am o idee oarecum similară cu a lui Jessica. Cu toate acestea, în loc de a stoca link-uri către elementele de sortare următoare și anterioară, stocați ordinea de sortare pentru fiecare tip de sortare. Pentru a găsi înregistrarea anterioară sau următoare, pentru a primi doar rândul cu SortX = currentSort ++ sau SortX = currentSort--.

Exemplu:

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

Această soluție ar genera ori de interogare foarte scurt, și ar ocupa mai puțin spațiu pe disc decât ideea lui Jessica. Cu toate acestea, așa cum sunt sigur îți dai seama, costul de actualizare un rând de date este semnificativ mai mare, din moment ce trebuie să recalculeze și stoca toate ordinele de sortare. Dar, totuși, în funcție de situație, în cazul în care actualizările de date sunt rare și mai ales dacă se întâmplă întotdeauna în vrac, atunci această soluție ar putea fi cel mai bun.

și anume

once_per_day
  add/delete/update all records
  recalculate sort orders

Sper că acest lucru este util.

Publicat 13/02/2011 la 03:30
sursa de către utilizator

voturi
0

Ne cerem scuze dacă am înțeles greșit, dar cred că doriți să păstrați lista ordonată între utilizator accesează la server. În acest caz, răspunsul ar putea consta în strategia de caching și tehnologii, mai degrabă decât în ​​baza de date de optimizare interogare / schemă.

Abordarea mea ar fi să serialize () matrice dată prima sa recuperat, și apoi cache, care într-o zonă de depozitare separată; indiferent că este vorba memcached / APC / hard-disk / MongoDB / etc și păstrează detaliile sale cache de localizare pentru fiecare utilizator individual, prin datele lor sesiune. Backend de stocare reală ar fi în mod natural depinde de mărimea matrice, care nu intra in mai multe detalii despre, dar cântare Memcached mare pe mai multe servere și Mongo chiar mai departe, la un cost de latență ușor mai mare.

De asemenea, nu indică cât de multe permutări de sortare există în lumea reală; de exemplu, aveți nevoie pentru a cache liste separate pentru fiecare utilizator, sau poate la nivel global cache pe permutare sortare și apoi se filtrează ceea ce nu aveți nevoie prin intermediul PHP ?. În exemplul da, aș cache pur și simplu ambele permutări și magazinul care din cele două am nevoie să unserialize (), în datele de sesiune.

Când utilizatorul se întoarce la site-ul, verificați Time To Live valoarea datelor stocate în memoria cache și îl utilizați din nou, dacă încă valabil. Mi-ar trebui, de asemenea, un declanșator care rulează pe INSERT IGNORE / UPDATE / DELETE pentru ofertele speciale pe care pur și simplu setează un câmp într-un tabel timestamp-ul separat. Acest lucru ar indica imediat dacă cache-ul a fost stătut și interogarea necesară pentru a fi re-rula pentru un cost foarte scăzut de interogare. Cea mai mare lucru despre folosind doar declanșatorul pentru a seta un singur câmp este că nu este nevoie să vă faceți griji cu privire la tăierea valori vechi / redundante din acel tabel.

Dacă acest lucru este adecvat va depinde de dimensiunea datelor returnate, cât de frecvent a fost modificată, și ce tehnologii cache sunt disponibile pe server.

Publicat 13/02/2011 la 15:47
sursa de către utilizator

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