Optimalisere spørringer for neste og forrige element

stemmer
28

Jeg leter etter den beste måten å hente neste og forrige poster av en post uten å kjøre en full spørring. Jeg har en fullt implementert løsning på plass, og vil gjerne vite om det er noen bedre måter å gjøre dette der ute.

La oss si at vi bygger et nettsted for en fiktiv greengrocer. I tillegg til sine HTML-sider, hver uke, ønsker han å publisere en liste av tilbud på sitt nettsted. Han ønsker de tilbud om å oppholde seg i en faktisk database tabell, og brukerne må være i stand til å sortere tilbudene på tre måter.

Hvert element har også å ha en detalj side med mer, tekstlig informasjon om tilbudet og forrige og neste knappene. Den forrige og neste knappene må peke til nabo oppføringer avhengig av sortering brukeren hadde valgt for listen .

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

Selvfølgelig, neste -knappen for tomater, klasse I må være Epler, klasse 1 i det første eksemplet, Pears, klasse I i den andre, og ingen i den tredje.

Oppgaven i detaljvinduet for å bestemme neste og forrige elementer uten å kjøre en spørring hver gang , med rekkefølgen på listen som den eneste tilgjengelige informasjonen (La oss si vi får det gjennom en GET parameter ?sort=offeroftheweek_price, og ignorere sikkerhets implikasjoner) .

Selvfølgelig, bare passerer ID-ene for neste og forrige elementer som parameter er den første løsningen som kommer til hjernen. Tross alt, vi vet allerede ID-er på dette punktet. Men, dette er ikke et alternativ her - det ville fungere i denne forenklede eksempel, men ikke i mange av mine virkelige verden bruksmåter.

Min nåværende tilnærming i min CMS bruker noe jeg har kalt sortering cache. Når en liste er lastet, jeg lagre elementet stillinger i poster i en tabell med navnet 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

selvsagt, det itemser kolonne virkelig befolket med numeriske IDer.

På detaljsiden, jeg tilgang nå den aktuelle sortingcacheposten, hente itemskolonne, eksploderer det, søke etter gjeldende element-ID, og returnere den forrige og neste nabo.

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

Dette er åpenbart dyrt, arbeider for et begrenset antall bare poster og skaper overflødige data, men la oss anta at i den virkelige verden, spørringen for å lage listene er svært dyrt (det er), kjører den i hver detalj utsikt er ute av spørsmålet, og noen caching er nødvendig.

Mine spørsmål:

  • Tror du dette er en god praksis for å finne ut nabo postene for varierende spørre bestillinger?

  • Vet du bedre praksis når det gjelder ytelse og enkelhet? Vet du noe som gjør dette helt foreldet?

  • I programmering teorien er det et navn på dette problemet?

  • Er navnet Sortering cache er hensiktsmessig og forståelig for denne teknikken?

  • Er det noen anerkjente, felles mønstre for å løse dette problemet? Hva heter de?

Merk: Mitt spørsmål er ikke om å bygge listen, eller hvordan du kan vise detaljvisningen. De er bare eksempler. Mitt spørsmål er grunnleggende funksjonalitet for å bestemme naboer av en rekord når en re-spørring er umulig, og den raskeste og billigste måten å komme dit.

Hvis noe er uklart, vennligst legg igjen en kommentar og jeg vil avklare.

Starte en dusør - kanskje det er litt mer info om dette der ute.

Publisert på 22/02/2010 klokken 11:06
kilden bruker
På andre språk...                            


11 svar

stemmer
-3

Så du har to oppgaver:

  1. bygge sortert liste over elementer (velger med forskjellig ORDER BY)
  2. vise detaljer om hvert element (velg Detaljer fra database med mulig caching).

Hva er problemet?

PS: Hvis sortert liste kan være for stor du trenger bare PAGER funksjonalitet implementert. Det kan være forskjellige implementasjoner, f.eks du kan ønske å legge til "LIMIT 5" i søket og gi "Vis neste 5" -knappen. Når denne knappen trykkes inn, tilstanden som "WHERE pris <0,89 LIMIT 5" er lagt.

Svarte 22/02/2010 kl. 14:04
kilden bruker

stemmer
16

Her er en idé. Du kan avlaste de dyre operasjoner til en oppdatering når dagligvare innstikk / oppdaterer nye tilbud i stedet for når sluttbrukeren velger de data å vise. Dette kan virke som en ikke-dynamisk måte å håndtere sortere data, men det kan øke hastigheten. Og, som vi vet, er det alltid en avveining mellom ytelse og andre koding faktorer.

Lag en tabell for å holde neste og forrige for hvert tilbud og hver sorteringsalternativ. (Alternativt kan du lagre dette i tilbudet bordet hvis du alltid vil ha tre sorteringsalternativer - søket hastighet er en god grunn til å Avnormaliser databasen)

Så du ville ha disse kolonnene:

  • Sorter Type (Usortert, Prisen, klasse og Pris synkende)
  • tilbudet ID
  • Forrige ID
  • Neste ID

Når detaljert informasjon om tilbudet detaljsiden er spørres fra databasen, ville NextID og PrevID være en del av resultatene. Så du ville bare trenger en spørring for hver detalj side.

Hver gang et tilbud er satt inn, oppdatert eller slettet, vil du trenger for å kjøre en prosess som validerer integritet / nøyaktigheten av sorttype tabellen.

Svarte 22/02/2010 kl. 19:20
kilden bruker

stemmer
1

Jeg er ikke sikker på om jeg forsto rett, så hvis ikke, bare fortell meg;)

La oss si, at Givens er spørringen for den sorterte listen og gjeldende forskyvning i den listen, altså har vi en $queryog en $n.

En svært opplagt løsning for å minimere spørsmål, ville være å hente alle data på en gang:

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

Utsagnet henter den tidligere, nåværende og de nærmeste elementer fra databasen i den gjeldende sorterings orden og setter den tilhørende informasjon inn i de tilsvarende variabler.

Men som denne løsningen er for enkelt, jeg antar jeg misforstått noe.

Svarte 07/02/2011 kl. 19:31
kilden bruker

stemmer
2

Jeg har hatt mareritt med dette også. Din nåværende tilnærming synes å være den beste løsningen selv for lister over 10k elementer. Caching ID-ene for listevisning i http økten, og deretter bruke den for visning av (personlig til gjeldende bruker) forrige / neste. Dette fungerer godt, spesielt når det er for mange måter å filtrere og sortere den opprinnelige listen over elementer i stedet for bare 3.
Også ved å lagre hele IDer listen du får til å vise en "you are at X out of Y"brukervennlighet fremmende tekst.
JIRA forrige / neste

Forresten, dette er hva JIRA gjør også.

Å direkte svare på dine spørsmål:

  • Ja, det er god praksis fordi det skalerer uten noen ekstra kode kompleksitet når filter / sortering og elementtypene gale mer kompleks. Jeg bruker den i et produksjonssystem med 250k artikler med "uendelig" filter / sorterings variasjoner. Trimming kan bufres IDer til 1000 er også en mulighet siden brukeren vil mest sannsynligvis aldri klikke på forrige eller neste mer enn 500 ganger (Han vil mest sannsynlig gå tilbake og avgrense søket eller nummereres).
  • Jeg vet ikke om en bedre måte. Men hvis de sorterer der begrenset, og dette var et offentlig område (uten http sesjon) da ville jeg mest sannsynlig Avnormaliser.
  • Vet ikke.
  • Ja, det høres sortering cache bra. I mitt prosjekt kaller jeg det "forrige / neste på søkeresultatene" eller "navigasjon på søkeresultatene".
  • Vet ikke.
Svarte 07/02/2011 kl. 20:04
kilden bruker

stemmer
2

Generelt, jeg Avnormaliser data fra indeksene. De kan lagres i de samme rekkene, men jeg nesten alltid hente mine resultat IDer, deretter lage en egen tur for dataene. Dette gjør caching av data svært enkel. Det er ikke så viktig i PHP der latency er lav og båndbredden høy, men en slik strategi er svært nyttig når du har en høy latency, lav båndbredde program, for eksempel en AJAX nettsted hvor mye av området er gjengitt i Javascript.

Jeg har alltid cache listene over resultatene, og resultatene selv separat. Hvis noe påvirker resultatene av en liste spørring, er bufferen av liste resultater uthvilt. Hvis noe påvirker resultatene i seg selv, de bestemte resultatene er uthvilt. Dette tillater meg å oppdatere enten en uten å regenerere alt, noe som resulterer i effektiv caching.

Siden mine lister over resultatene sjelden endres, jeg generere alle listene samtidig. Dette kan gjøre det første svaret litt tregere, men det forenkler cache forfriskende (alle listene blir lagret i en enkelt cache oppføring).

Fordi jeg har hele listen bufret, er det trivielt å finne nabo elementer uten borti databasen. Med litt flaks, vil dataene for disse elementene også bli lagret. Dette er spesielt nyttig når du sorterer data i Javascript. Hvis jeg allerede har en kopi lagret på klienten, kan jeg ty umiddelbart.

For å svare på dine spørsmål spesifikt:

  • Ja, det er en fantastisk idé å finne ut naboene på forhånd, eller hva informasjonen kunden er sannsynlig å få tilgang til neste, spesielt hvis prisen er lav nå, og det koster å omberegne er høy. Da er det bare en trade off ekstra pre-beregning og lagring versus hastighet.
  • Når det gjelder ytelse og enkelhet, unngå knytte ting sammen som er logisk forskjellige ting. Indekser og data er forskjellige, vil sannsynligvis bli endret på ulike tidspunkter (for eksempel å legge til en ny datum vil påvirke indeksene, men ikke den eksisterende data), og således skal åpnes separat. Dette kan være litt mindre effektive fra en single-threaded ståsted, men hver gang du knytte noe sammen, mister du caching effektivitet og asychronosity (nøkkelen til skalering er asychronosity).
  • Begrepet for å få data på forhånd er forhåndshenting. Pre-henting kan skje samtidig med tilgang eller i bakgrunnen, men før forhåndshentet data er faktisk nødvendig. Likeledes med forhåndsberegning. Det er en avveining av kostnader nå, lagring kostnader, og kostnadene for å få når det trengs.
  • "Sortering cache" er et passende navn.
  • Jeg vet ikke.

Også når du cache ting, cache dem på det mest generiske mulig nivå. Noen ting kan være brukerspesifikke (for eksempel resultater for et søk), der andre kan være bruker agnostiker, for eksempel leser en katalog. Begge kan ha nytte av caching. Katalogen søket kan være hyppig og spare litt hver gang, og søket kan være dyrt og spare mye noen ganger.

Svarte 09/02/2011 kl. 07:00
kilden bruker

stemmer
0

Det er så mange måter å gjøre dette som huden ordspråklig katten. Så her er et par av mine.

Hvis det opprinnelige søket er dyrt, som du sier det er, og deretter opprette en annen tabell muligens en minnebord fyller det med resultatene av dyre og sjelden kjører hovedspørringen.

Denne andre tabellen kan så spørres på hver visning og sortering er så enkelt som å sette riktig sortering.

Som er nødvendig for å repopulere det andre tabell med resultater fra den første tabell, og dermed holde dataene frisk, men minimere bruken av den kostbare spørringen.

Alternativt, hvis du ønsker å unngå selv å koble til db så kan du lagre alle data i en php array og lagre den ved hjelp memcached. dette vil være svært rask og gitt listene var ikke så stor ville bli ressurseffektive. og kan lett sorteres.

DC

Svarte 11/02/2011 kl. 04:19
kilden bruker

stemmer
0

Grunnleggende forutsetninger:

  • Specials er ukentlig
  • Vi kan forvente at området for å endre sjelden ... sannsynligvis daglig?
  • Vi kan kontrollere oppdateringer til databasen med eter en API eller svare via triggere

Hvis området endres på en daglig basis, foreslår jeg at alle sidene er statisk generert over natten. En spørring for hver sort-order gjentas gjennom og gjør alle relaterte sider. Selv om det er dynamiske elementer, oddsen er at du kan løse dem ved å inkludere de statiske sideelementer. Dette vil gi optimal side service og ingen database belastning. Faktisk kunne du muligens generere egne sider og prev / neste elementene som inngår i sidene. Dette kan være galere med 200 måter å sortere, men med 3 jeg er en stor fan av det.

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

Hvis for noen grunn dette ikke er mulig, vil jeg ty til utenatlæring. Memcache er populær for denne typen ting (pun!). Når noe er skjøvet til databasen, kan du sende en utløser for å oppdatere din cache med de riktige verdiene. Gjør dette på samme måte som du ville om som om den oppdaterte element eksistert i 3 lenkede lister - kobler på nytt etter behov (this.next.prev = this.prev, etc). Fra det, så lenge bufferen ikke overfylle, vil du være å trekke enkle verdier fra minnet i en primærnøkkel mote.

Denne metoden vil ta litt ekstra koding på utvalgte og oppdatering / innleggs metoder, men det bør være ganske minimal. Til slutt, vil du være på utkikk opp [id of tomatoes class 1].price.next. Hvis denne nøkkelen er i bufferen, gyllen. Hvis ikke, må du sette inn cache og display.

  • Tror du dette er en god praksis for å finne ut nabo postene for varierende spørre bestillinger? Ja. Det er lurt å utføre look-aheads på forventede kommende forespørsler.
  • Vet du bedre praksis når det gjelder ytelse og enkelhet? Vet du noe som gjør dette helt foreldet? Forhåpentligvis ovenfor
  • I programmering teorien er det et navn på dette problemet? Optimalisering?
  • Er navnet "Sortering cache" er hensiktsmessig og forståelig for denne teknikken? Jeg er ikke sikker på en bestemt passende navn. Det er caching, det er en cache slags, men jeg er ikke sikker på at å fortelle meg at du har en "sortering cache" ville formidle umiddelbar forståelse.
  • Er det noen anerkjente, felles mønstre for å løse dette problemet? Hva heter de? Caching?

Beklager mine tailing svarene er slags unyttig, men jeg tror mine narrative løsninger bør være ganske nyttig.

Svarte 11/02/2011 kl. 17:13
kilden bruker

stemmer
0

Du kan lagre radnumrene av de bestilte lister til visninger , og du kan nå de forrige og neste elementene i listen under (current_rownum-1) og (current_rownum + 1) radnumrene.

Svarte 12/02/2011 kl. 13:01
kilden bruker

stemmer
0

Problemet / datastructur heter toveis graf eller du kan si at du har flere lenkede lister.

Hvis du tenker på det som en lenket liste, kan du bare legge til felt i elementer tabell for hver sortering og forrige / neste tasten. Men DB Person vil drepe deg for det, det er som GOTO.

Hvis du tenker på det som et (bi-) retnings graf, går du med Jessica svar. Hovedproblemet er at ordre oppdateringer er dyre operasjoner.

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

Hvis du endrer en elementer stilling til den nye ordren A, C, B, D, må du oppdatere 4 rader.

Svarte 13/02/2011 kl. 01:20
kilden bruker

stemmer
4

Jeg har en idé noe som ligner på Jessica. Men i stedet for å lagre linker til neste og forrige sortere elementer, lagrer du sorteringsrekkefølge for hver slags type. For å finne forrige eller neste post, bare få raden med Sortx = currentSort ++ eller Sortx = currentSort--.

Eksempel:

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

Denne løsningen vil gi svært korte spørre ganger, og vil ta opp mindre diskplass enn Jessica idé. Men som jeg er sikker på at du skjønner, kostnaden med å oppdatere en rad med data er betydelig høyere, siden du må beregne og lagre alle sorter pålegg. Men likevel, avhengig av situasjonen, hvis data oppdateringer er sjeldne, og spesielt hvis de alltid skje i bulk, da denne løsningen kan være den beste.

dvs

once_per_day
  add/delete/update all records
  recalculate sort orders

Håper dette er nyttig.

Svarte 13/02/2011 kl. 02:30
kilden bruker

stemmer
0

Beklager hvis jeg har misforstått, men jeg tror du vil beholde den ordnede listen mellom brukeren får tilgang til serveren. I så fall kan svaret godt ligge i caching strategi og teknologi i stedet for i databasespørring / schema optimalisering.

Min tilnærming ville være å serialisere () i matrisen når sin første hentet frem, og deretter cache som i et separat lagringsområde; enten det er memcached / APC / harddisk / mongoDb / etc. og beholde sin cache plassering detaljer for hver bruker individuelt gjennom sine sesjonsdata. Den faktiske lagring backend ville naturligvis være avhengig av størrelsen på array, som du ikke gå inn i mange detaljer om, men memcached skalaer stor over flere servere og mongo enda lenger på et litt større latency kostnad.

Du kan heller ikke angi hvor mange slags permutasjoner det er i den virkelige verden; for eksempel trenger du å cache separate lister per bruker, eller kan du globalt cache per slags permutasjon og deretter filtrere ut det du ikke trenger via PHP ?. I eksempelet du gir, ville jeg rett og slett cache både permutasjoner og lagre hvilken av de to jeg trengte å unserialize () i sesjonsdata.

Når brukeren returnerer til området, sjekk Time To Live verdien av de lagrede data og gjenbruke det hvis fortsatt gyldig. Jeg vil også ha en trigger kjører på INSERT / UPDATE / DELETE for spesielle tilbud som bare setter et tidsstempel felt i en egen tabell. Dette vil umiddelbart indikere om cache var foreldet og spørringen trengte å være re-run for en svært lav spørring kostnader. Det flotte med å bare bruke avtrekkeren for å stille et enkelt felt, er at det er ingen grunn til bekymring om beskjæring gamle / utrangerte verdier ut av det bordet.

Hvorvidt dette er hensiktsmessig vil avhenge av størrelsen på dataene som returneres, hvor ofte det ble endret, og hva caching teknologi er tilgjengelig på serveren.

Svarte 13/02/2011 kl. 14:47
kilden bruker

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