sql >> Database teknologi >  >> RDS >> Mysql

Optimering af BESTIL EFTER

Dette er en meget interessant forespørgsel. Under dens optimering kan du opdage og forstå en masse ny information om, hvordan MySQL fungerer. Jeg er ikke sikker på, at jeg får tid til at skrive alt i detaljer på én gang, men jeg kan gradvist opdatere.

Hvorfor er det langsomt

Der er grundlæggende to scenarier:et hurtigt og en langsom .

hurtigt scenarie du går i en foruddefineret rækkefølge over en tabel og sandsynligvis samtidig hurtigt hente nogle data efter id for hver række fra andre tabeller. I dette tilfælde stopper du med at gå, så snart du har nok rækker specificeret af din LIMIT-klausul. Hvor kommer ordren fra? Fra et b-træindeks, som du har på bordet eller rækkefølgen af ​​et resultatsæt i en underforespørgsel.

I en langsom scenario har du ikke den foruddefinerede rækkefølge, og MySQL skal implicit lægge alle data i en midlertidig tabel, sortere tabellen på et felt og returnere n rækker fra din LIMIT-klausul. Hvis nogen af ​​de felter, du indsætter i den midlertidige tabel, er af typen TEXT (ikke VARCHAR), forsøger MySQL ikke engang at beholde den tabel i RAM og tømmer og sorterer den på disken (derfor yderligere IO-behandling).

Første ting at rette

Der er mange situationer, hvor du ikke kan bygge et indeks, der giver dig mulighed for at følge dets rækkefølge (når du f.eks. BESTILLER EFTER kolonner fra forskellige tabeller), så tommelfingerreglen i sådanne situationer er at minimere de data, som MySQL vil lægge i den midlertidige tabel. Hvordan kan du gøre det? Du vælger kun identifikatorer for rækkerne i en underforespørgsel, og når du har id'erne, forbinder du id'erne til selve tabellen og andre tabeller for at hente indholdet. Det vil sige, at du laver et lille bord med en ordre og derefter bruger det hurtige scenarie. (Dette er lidt i modstrid med SQL generelt, men hver variant af SQL har sit eget middel til at optimere forespørgsler på den måde).

Tilfældigvis er din SELECT -- everything is ok here ser sjovt ud, da det er det første sted, hvor det ikke er ok.

SELECT p.* , u.name user_name, u.status user_status , c.name city_name, t.name town_name, d.name dist_name , pm.meta_name, pm.meta_email, pm.meta_phone , (SELECT concat("{", '"id":"', pc.id, '",', '"content":"', replace(pc.content, '"', '\\"'), '",', '"date":"', pc.date, '",', '"user_id":"', pcu.id, '",', '"user_name":"', pcu.name, '"}"') last_comment_json FROM post_comments pc LEFT JOIN users pcu ON (pcu.id = pc.user_id) WHERE pc.post_id = p.id ORDER BY pc.id DESC LIMIT 1) AS last_comment FROM ( SELECT id FROM posts p WHERE p.status = 'published' ORDER BY (CASE WHEN p.created_at >= unix_timestamp(now() - INTERVAL p.reputation DAY) THEN +p.reputation ELSE NULL END) DESC, p.id DESC LIMIT 0,10 ) ids JOIN posts p ON ids.id = p.id -- mind the join for the p data LEFT JOIN users u ON (u.id = p.user_id) LEFT JOIN citys c ON (c.id = p.city_id) LEFT JOIN towns t ON (t.id = p.town_id) LEFT JOIN dists d ON (d.id = p.dist_id) LEFT JOIN post_metas pm ON (pm.post_id = p.id) ;

Det er det første skridt, men allerede nu kan du se, at du ikke behøver at lave disse ubrugelige LEFT JOINS og json-serialiseringer for de rækker, du ikke har brug for. (Jeg sprang GROUP BY p.id over , fordi jeg ikke kan se, hvilken LEFT JOIN der kan resultere i flere rækker, foretager du ikke nogen aggregering).

endnu at skrive om:

  • indekser
  • omformuler CASE-klausulen (brug UNION ALL)
  • sandsynligvis fremtvinger et indeks


  1. MySQL &MariaDB Query Caching med ProxySQL &ClusterControl

  2. Hvordan skal jeg importere data fra CSV til en Postgres-tabel ved hjælp af pgAdmin 3?

  3. Tjek statistikmål i PostgreSQL

  4. Oracle til PostgreSQL:Grunde til at migrere