Optimizarea interogărilor MySQL. Selectați numai câmpurile cerute de script. Instrumente suport: Percona Toolkit pentru identificarea indicilor duplicați

În munca de zi cu zi, întâmpinați erori destul de similare atunci când scrieți interogări.

În acest articol aș dori să dau exemple despre cum să NU scrieți interogări.

  • Selectați toate câmpurile
    SELECT * FROM tabel

    Când scrieți interogări, nu utilizați o selecție a tuturor câmpurilor - „*”. Enumerați doar câmpurile de care aveți cu adevărat nevoie. Acest lucru va reduce cantitatea de date preluate și trimise. De asemenea, nu uitați să acoperiți indici. Chiar dacă aveți nevoie de toate câmpurile din tabel, este mai bine să le enumerați. În primul rând, îmbunătățește lizibilitatea codului. Când utilizați un asterisc, este imposibil să știți ce câmpuri sunt în tabel fără a-l privi. În al doilea rând, în timp, numărul de coloane din tabelul dvs. se poate schimba, iar dacă astăzi există cinci coloane INT, atunci într-o lună se pot adăuga câmpuri TEXT și BLOB, ceea ce va încetini selecția.

  • Cereri într-un ciclu.
    Trebuie să înțelegeți clar că SQL este un limbaj de operare set. Uneori, programatorilor care sunt obișnuiți să gândească în termeni de limbaje procedurale le este greu să-și schimbe gândirea la limbajul seturilor. Acest lucru se poate face pur și simplu prin adoptarea unei reguli simple - „nu executați niciodată interogări într-o buclă”. Exemple de cum se poate face acest lucru:

    1. Mostre
    $news_ids = get_list("SELECTEAZĂ ID-ul știrilor FROM știrile_azi ");
    while($news_id = get_next($news_ids))
    $news = get_row("SELECT titlul, body FROM news WHERE news_id = ". $news_id);

    Regula este foarte simplă - cu cât sunt mai puține solicitări, cu atât mai bine (deși există excepții de la aceasta, ca orice regulă). Nu uitați de constructul IN(). Codul de mai sus poate fi scris într-o singură interogare:
    SELECTează titlul, corpul FROM today_news INNER JOIN news USING(news_id)

    2. Inserturi
    $log = parse_log();
    while($record = next($log))
    interogare("INSERT INTO logs SET value = ". $log["value"]);!}

    Este mult mai eficient să concatenați și să executați o interogare:
    INSERT INTO jurnalele (valoare) VALUES (...), (...)

    3. Actualizări
    Uneori trebuie să actualizați mai multe rânduri într-un singur tabel. Dacă valoarea actualizată este aceeași, atunci totul este simplu:
    UPDATE news SET title="test" WHERE id IN (1, 2, 3).!}

    Dacă valoare modificabilă diferit pentru fiecare înregistrare, atunci acest lucru se poate face cu următoarea solicitare:
    UPDATE SET ȘTIRI
    titlu = CAZ
    WHEN news_id = 1 THEN "aa"
    WHEN news_id = 2 THEN "bb" END
    WHERE news_id IN (1, 2)

    Testele noastre arată că o astfel de solicitare este de 2-3 ori mai rapidă decât mai multe solicitări separate.

  • Efectuarea de operațiuni pe câmpuri indexate
    SELECTează user_id FROM utilizatorii WHERE blogs_count * 2 = $valoare

    Această interogare nu va folosi indexul, chiar dacă coloana blogs_count este indexată. Pentru ca un index să fie utilizat, nu trebuie efectuate transformări pe câmpul indexat din interogare. Pentru astfel de solicitări, mutați funcțiile de conversie în altă parte:
    SELECTează user_id FROM utilizatori WHERE blogs_count = $valoare / 2;

    Exemplu similar:
    SELECTează user_id FROM utilizatorii WHERE TO_DAYS(CURRENT_DATE) - TO_DAYS(înregistrat)<= 10;

    Nu va folosi un index pe câmpul înregistrat, în timp ce
    SELECT user_id FROM utilizatorii WHERE inregistrati >= DATE_SUB(CURRENT_DATE, INTERVAL 10 DAY);
    voi.

  • Preluare rânduri numai pentru a le număra numărul
    $rezultat = mysql_query("SELECT * FROM table", $link);
    $num_rows = mysql_num_rows($rezultat);
    Dacă trebuie să selectați numărul de rânduri care îndeplinesc o anumită condiție, utilizați interogarea tabelului SELECT COUNT(*) FROM în loc să selectați toate rândurile doar pentru a număra numărul de rânduri.
  • Preluare rânduri suplimentare
    $rezultat = mysql_query("SELECT * FROM table1", $link);
    while($rând = mysql_fetch_assoc($rezultat) && $i< 20) {

    }
    Dacă aveți nevoie doar de n rânduri de preluare, utilizați LIMIT în loc să renunțați la rândurile suplimentare din aplicație.
  • Folosind ORDER BY RAND()
    SELECT * FROM tabel ORDER BY RAND() LIMIT 1;

    Dacă tabelul are mai mult de 4-5 mii de rânduri, atunci ORDER BY RAND() va funcționa foarte lent. Ar fi mult mai eficient să rulați două interogări:

    Dacă tabelul are auto_increment" cheia principala si fara goluri:
    $rnd = rand(1, query("SELECT MAX(id) FROM table"));
    $row = interogare("SELECT * FROM tabel WHERE id = ".$rnd);

    Sau:
    $cnt = interogare ("SELECT COUNT (*) FROM table");
    $row = interogare("SELECT * FROM LIMIT tabel ".$cnt.", 1");
    care, totuși, poate fi lent și dacă există un număr foarte mare de rânduri în tabel.

  • Utilizare cantitate mareÎNSCRIEȚI-VĂ
    SELECTAȚI
    v.video_id
    un nume,
    g.gen
    DIN
    videoclipuri AS v
    LEFT JOIN
    link_actors_videos AS la ON la.video_id = v.video_id
    LEFT JOIN
    actori AS a ON a.actor_id = la.actor_id
    LEFT JOIN
    link_genre_video AS lg ON lg.video_id = v.video_id
    LEFT JOIN
    genuri AS g ON g.genre_id = lg.genre_id

    Trebuie amintit că atunci când conectați tabele unu-la-mai multe, numărul de rânduri din selecție va crește cu fiecare JOIN următoare. Pentru astfel de cazuri, este mai rapid să împărțiți o astfel de interogare în mai multe simple.

  • Folosind LIMIT
    SELECTAȚI… FROM LIMIT de tabel $start, $per_page

    Mulți oameni cred că o astfel de interogare va returna $per_page de înregistrări (de obicei 10-20) și, prin urmare, va funcționa rapid. Va funcționa rapid pentru primele câteva pagini. Dar dacă numărul de înregistrări este mare și trebuie să executați o interogare SELECT... FROM table LIMIT 1000000, 1000020, atunci pentru a executa o astfel de interogare, MySQL va selecta mai întâi 1000020 de înregistrări, va arunca primul milion și va returna 20. Aceasta poate să nu fie rapid deloc. Nu există modalități banale de a rezolva problema. Mulți pur și simplu limitează cantitatea pagini disponibile un număr rezonabil. De asemenea, puteți accelera cereri similare folosind indici de acoperire sau soluții de la terți(de exemplu sfinxul).

  • Nu se utilizează ON DUPLICATE KEY UPDATE
    $row = query("SELECT * FROM tabel WHERE id=1");

    Dacă($rând)
    interogare ("UPDATE table SET coloana = coloana + 1 WHERE id=1")
    altfel
    interogare("INSERT INTO table SET coloana = 1, id=1");

    O construcție similară poate fi înlocuită cu o singură interogare, cu condiția să existe o cheie primară sau unică pentru câmpul id:
    INSERT INTO table SET coloana = 1, id=1 ON DUPLICATE KEY UPDATE coloana = coloana + 1

Citit

Utilizarea bazelor de date facilitează foarte mult viața și munca unei persoane cu date, permițându-le să obțină termene scurte informatie necesara din baza de date sau scrieți în ea. Cu toate acestea, lucrul cu date necesită o abordare adecvată; programatorul ar trebui să țină cont de unele aspecte ale interacțiunii cu bazele de date. În special despre care vorbim despre MySQL. În continuare, să ne uităm la un rezumat al sfaturilor pentru optimizarea interacțiunii cu bazele de date Date MySQL.

Faceți ca interogările MySQL să fie prietenos

Mecanismul de stocare în cache a interogărilor încorporat în serverul MySQL poate îmbunătăți semnificativ performanța. Majoritatea serverelor de baze de date MySQL includ un mecanism de stocare în cache. Multe interogări identice la baza de date într-o perioadă scurtă de timp pot crea pierderi semnificative de performanță; mecanismul de stocare în cache este capabil să memoreze astfel de interogări, returnând date din cache. Există interogări pe care MySQL nu le poate stoca în cache și este recomandat să faceți aceste interogări puțin diferit.

// acest interogare MySQL nu va putea stoca în cache $res = mysql_query("SELECT username FROM user WHERE signup_date >= CURDATE()"); // o poți face diferit $azi = data("Y-m-d"); $res = mysql_query("SELECT username FROM user WHERE signup_date >= "$today"");

Cert este că la prima solicitare a fost folosită funcția CURDATE(), particularitatea funcționării acesteia nu permite plasarea rezultatelor interogării în cache. Valoarea datei poate fi pre-scrisă în șirul de interogare, aceasta va elimina utilizarea funcției CURDATE() în interogare.
Prin analogie, există și alte funcții care nu sunt memorate în cache Server MySQL, printre care RAND(), NOW() precum și alte funcții ale căror rezultate sunt nedeterministe.

Vedeți cum rulează interogarea dvs. folosind sintaxa EXPLAIN

Puteți vedea cum MySQL efectuează interogarea folosind sintaxa EXPLAIN. Utilizarea acestuia poate ajuta la identificarea punctelor slabe în performanța interogărilor, precum și în structura tabelului. EXPLAIN va returna date ca rezultat al interogării care va arăta ce indici sunt utilizați, cum sunt selectate datele din tabele, cum sunt sortate etc. Pentru a face acest lucru, trebuie doar să adăugați la începutul interogării SELECT cuvânt cheie EXPLICAȚI, după care va fi afișat un tabel cu date.

Când aveți nevoie de o înregistrare, setați LIMITĂ 1

Există destul de multe cazuri când trebuie să verificați prezența a cel puțin unei înregistrări dintr-un tabel, în acest caz este recomandat să adăugați la interogare parametrul LIMIT 1. Acest lucru o va face mai optimă, deoarece Motorul bazei de date va opri preluarea datelor după ce a găsit prima înregistrare în loc să preia toate datele. Economisiți resurse.

// interogați orașul cu codul Shymkent din baza de date $res = mysql_query("SELECT * FROM location WHERE city = "Shymkent""); if (mysql_num_rows($res) > 0)( ) // adăugați LIMIT 1 pentru a optimiza interogarea $res = mysql_query("SELECT * FROM location WHERE city = "Shymkent" LIMIT 1"); dacă (mysql_num_rows($res) > 0)( )

Indexați câmpurile pe care le căutați

Într-un caz particular, un index înseamnă un index al câmpurilor în care căutați, acest lucru va îmbunătăți viteza de căutare. Apropo, un index obișnuit nu poate funcționa cu condiții sub formă de expresii regulate:

// orașul index LIKE 'shym%' va funcționa aici // indexul nu va fi folosit aici orașul LIKE '%shymkent%'

Pentru a face un index pentru condiții cu expresii obisnuite ar trebui să utilizați sau să vă gândiți la sistemul dvs. de indexare.

Indexați câmpurile prin care sunt unite tabelele

Dacă utilizați mai multe îmbinări de tabele, vă recomandăm să vă asigurați că câmpurile implicate în îmbinare sunt indexate în ambele tabele. Această problemă afectează modul în care MySQL va produce optimizare internă uniuni de câmpuri de tabel. Câmpurile de unire trebuie să fie de același tip și codare. Acestea. de exemplu, dacă un câmp are tip DECIMAL, iar celălalt este INT, atunci MySQL nu va putea folosi indexul.

Găsiți o alternativă la ORDER BY RAND()

Utilizarea sortării aleatorii este într-adevăr foarte convenabilă și mulți programatori începători au aceeași părere despre aceasta. Cu toate acestea, există capcane aici și unele foarte semnificative; folosind o metodă similară de eșantionare în interogările dvs., lăsați un blocaj în performanță. Aici se recomandă să se recurgă la cod suplimentarîn loc să folosiți ORDER BY RAND(), ca alternativă pentru a scăpa de punct slabîn performanță, care își va aminti pe măsură ce volumul de date crește.

Utilizați selectarea anumitor câmpuri în loc de SELECT *

Nu fi leneș să indicați specific câmpuri obligatoriiîntr-o interogare la preluare, în loc să utilizați „*” - preluarea tuturor câmpurilor, adevărul este că cu cât se citesc mai multe date din tabel, cu atât interogarea dvs. devine mai lentă.

Adăugați un câmp ID pentru toate tabelele

Fiecare tabel, dacă este făcut bine, ar trebui să aibă un câmp ID tastați INT, care este cheia primară (PRIMARY_KEY) și AUTO_INCREMENT. În plus, trebuie să specificați parametrul UNSIGNED pentru câmp, ceea ce înseamnă că valoarea va fi întotdeauna pozitivă.
MySQL are operațiuni interne care pot folosi cheia primară, aceasta intră în joc pentru configurații complexe de baze de date, cum ar fi clustere, paralelizare etc.
În plus, dacă există mai multe tabele și trebuie să efectuați o interogare unită, atunci ID-urile tabelelor vă vor fi utile.

ENUM ca alternativă la VARCHAR

Să ne imaginăm că doriți să adăugați un câmp la un tabel care ar trebui să conțină un anumit set de valori. În mod tradițional, mulți programatori setează tipul VARCHAR pentru câmpuri. Cu toate acestea, există un alt tip de câmp care este mult mai rapid și mai compact. Valori în acest tip stocat în același mod ca TINYINT, dar afișat ca tip șir.

Folosiți NOT NULL în loc de NULL

Câmpurile NULL ocupă mai mult spatiuîn înregistrare, pentru că este necesar să rețineți acest lucru Valoare NULL. Tabelele MyISAM, câmpurile cu NULL sunt stocate în așa fel încât fiecare câmp să ocupe 1 un pic în plus, care este rotunjit la cel mai apropiat octet. Dacă utilizarea lui NULL într-un câmp nu este importantă, atunci este recomandat să folosiți NOT NULL.

Utilizați declarații pregătite

$res = "UPDATE hosts SET ip = INET_ATON ("($_SERVER["REMOTE_ADDR"])") WHERE id = $host_id";

Folosiți tabele statice

Un tabel static este un tabel obișnuit în baza de date, cu excepția faptului că fiecare câmp din tabel are o dimensiune fixă. Dacă tabelul are coloane care nu au o lungime fixă, de exemplu, acestea ar putea fi: VARCHAR, TEXT, BLOB, acesta încetează să mai fie static și va fi procesat de MySQL puțin diferit. Tabele statice sau pot fi numite și tabele marime fixa lucrează mai repede decât cele non-statice. Înregistrările din astfel de tabele vor fi vizualizate mai rapid; dacă trebuie să selectați rândul dorit, MySQL va calcula rapid poziția acestuia. Dacă câmpul nu are o dimensiune fixă, atunci în acest caz căutarea se face prin index. Există și alte avantaje ale utilizării tabelelor statice; faptul este că aceste tabele sunt mai ușor de stocat în cache și pot fi, de asemenea, restaurate după o blocare a bazei de date.

Utilizați separarea verticală

Partiționare verticală – implică împărțirea unui tabel în coloane pentru a crește performanța tabelului. De exemplu, dacă aveți câmpuri în tabel care sunt utilizate foarte rar sau sunt câmpuri cu lungime variabilă, apoi pot fi plasate într-o masă separată, descarcând astfel masa, crescând astfel viteza de lucru cu aceasta.

Separați interogările mari INSERT și DELETE

Executarea unui volum mare de interogări de acest fel poate duce la blocarea tabelului, ducând la funcționarea incorectă a aplicației în ansamblu. Se pot genera cereri paralele către serverul web recurs suplimentar la masă. Dacă un tabel este blocat de o solicitare anterioară, solicitările ulterioare se aliniază într-o coadă și, ca urmare, acest lucru se manifestă sub forma încetinirii site-ului sau chiar a unui blocaj al serverului.
Dacă trebuie să faceți o mulțime de solicitări, încercați să le controlați trimițându-le în loturi mici, în loc să aruncați totul în baza de date. Acest lucru poate dura mai mult pentru a finaliza solicitarea dvs., dar acest lucru va avea un impact mai mic asupra altor utilizatori.
Exemplu:

În timp ce (1)( mysql_query("DELETE FROM jurnalele WHERE log_date<= "2015-07-20" LIMIT 1000"); if (mysql_affected_rows() == 0){ // записи удалены успешно break; } usleep(50000); // делаем небольшую паузу }

Încercați să utilizați câmpuri mici

După cum știți, datele bazei de date sunt stocate pe hard disk, acesta poate fi adesea unul dintre punctele slabe ale unei aplicații web. Cert este că înregistrările de dimensiuni mici sunt mai de preferat, pentru că... utilizarea acestora reduce volumul de lucru pe hard disk. Dacă sunteți sigur că un anumit tabel va stoca câteva rânduri, atunci o soluție rațională ar fi să folosiți tipuri de câmpuri cu valori minime posibile. De exemplu, dacă cheia principală este de tip INT și veți stoca doar o cantitate mică de date în tabel, atunci este mai bine să o faceți de tip MEDIUMINT, SMALLINT sau chiar TINYINT.

Alegeți tipul de mese care se potrivesc sarcinilor dvs

Două tipuri de tabele cunoscute astăzi sunt MyISAM și InnoDB, fiecare dintre ele având propriile sale avantaje și dezavantaje. De exemplu, MyISAM este bun la citirea datelor din tabele în cantități mari, dar este mai lent când scrie. De asemenea, funcționează bine la interogări precum SELECT COUNT(*).
Mecanismul de stocare a datelor InnoDB este mai complex decât cel MyISAM, cu toate acestea, acceptă blocarea rândurilor, care este o latură pozitivă la scalare. Prin urmare, este imposibil să spuneți că unul este mai bun decât celălalt și nu este corect; trebuie să alegeți un tip în funcție de nevoile dvs.

Lucrul cu o bază de date este adesea cel mai slab punct al performanței multor aplicații web. Și nu doar DBA-urile trebuie să-și facă griji pentru asta. Programatorii trebuie să aleagă structura corectă a tabelului, să scrie interogări optimizate și să scrie cod bun. Următoarele sunt metode pentru optimizarea MySQL pentru programatori.

1. Optimizați interogările pentru cache de interogări

Majoritatea serverelor MySQL au interogarea cache activată. Una dintre cele mai bune modalități de a îmbunătăți performanța este pur și simplu furnizarea de cache în baza de date în sine. Când o interogare se repetă de mai multe ori, rezultatul acesteia este preluat din cache, ceea ce este mult mai rapid decât accesarea directă a bazei de date. Problema principală este că mulți oameni folosesc pur și simplu interogări care nu pot fi stocate în cache:

// cererea nu va fi memorată în cache$r = mysql_query( „SELECTează numele de utilizator FROM user WHERE signup_date >= CURDATE()”); // si asa va fi! $azi = data("A-m-d"); $r = mysql_query( „SELECT username FROM user WHERE signup_date >= „$today””);

Motivul este că prima interogare folosește funcția CURDATE(). Acest lucru se aplică tuturor funcțiilor precum NOW(), RAND() și altora al căror rezultat este nedeterminist. Dacă rezultatul unei funcții se poate modifica, atunci MySQL nu memorează în cache o astfel de interogare. ÎN în acest exemplu acest lucru poate fi prevenit prin calcularea datei înainte de executarea interogării.

2. Folosiți EXPLAIN pentru interogările dvs. SELECT

// creează o instrucțiune pregătită if ($stmt = $mysqli ->prepare( „SELECTARE nume de utilizator FROM utilizator WHERE state=?”)) { // leagă valori$stmt ->bind_param("s" , $state ); // executa $stmt ->execute(); // leagă rezultatul$stmt ->bind_result($nume utilizator ); // Obțineți date$stmt ->fetch(); printf("%s este de la %s\n" , $nume utilizator , $state ); $stmt ->close(); )

13. Cereri nebufferate

De obicei, atunci când se face o solicitare, scriptul se oprește și așteaptă rezultatul execuției sale. Puteți modifica acest lucru utilizând interogări fără tampon.
Există o descriere bună în documentația funcției mysql_unbuffered_query():

„mysql_unbuffered_query() trimite o interogare SQL către MySQL fără să recupereze sau să tamponeze automat rândurile rezultate, așa cum o face mysql_query(). Pe de o parte, aceasta economisește o cantitate semnificativă de memorie pentru interogările SQL care produc seturi mari de rezultate. Pe de altă parte, puteți începe să lucrați la setul de rezultate de tăiere după ce primul rând a fost preluat: nu trebuie să așteptați să ruleze interogarea SQL completă."

Totuși există anumite restricții. Va trebui să citiți toate înregistrările sau să apelați mysql_free_result() înainte de a putea rula o altă interogare. De asemenea, nu puteți utiliza mysql_num_rows() sau mysql_data_seek() pe rezultatul funcției.

14. Stocați IP-ul în UNSIGNED INT

Mulți programatori stochează adrese IP într-un câmp de tip VARCHAR(15), fără să știe că poate fi stocat în formă întreagă. INT ocupă 4 octeți și are o dimensiune fixă ​​a câmpului.
Asigurați-vă că utilizați UNSIGNED INT deoarece IP-ul poate fi scris ca un număr nesemnat pe 32 de biți.
Utilizați INET_ATON() în cererea dvs. pentru a converti o adresă IP într-un număr și INET_NTOA() pentru a o converti înapoi. Aceleași funcții există în PHP - ip2long() și long2ip() (în PHP, aceste funcții pot returna și valori negative. Notă de la The_Lion).

$r = "UPDATE users SET ip = INET_ATON ("($_SERVER["REMOTE_ADDR"])") WHERE user_id = $user_id";

15. Tabelele cu dimensiuni fixe (statice) sunt mai rapide

Dacă fiecare coloană dintr-un tabel are o dimensiune fixă, atunci tabelul se numește „static” sau „dimensiune fixă”. Exemplu de coloane cu lungime nefixă: VARCHAR, TEXT, BLOB. Dacă includeți un astfel de câmp într-un tabel, acesta nu va mai fi reparat și va fi procesat diferit de MySQL.
Folosirea unor astfel de tabele va crește eficiența, deoarece... MySQL poate căuta mai rapid înregistrările din ele. Când să alegi linia dorită tabel, MySQL își poate calcula poziția foarte rapid. Dacă dimensiunea înregistrării nu este fixă, aceasta este căutată după index.
Aceste tabele sunt, de asemenea, mai ușor de memorat și restaurat după o blocare a bazei de date. De exemplu, dacă convertiți VARCHAR(20) în CHAR(20), intrarea va ocupa 20 de octeți, indiferent de conținutul său real.
Folosind metoda „diviziunii pe verticală”, puteți muta coloanele cu lungimi variabile de rând într-un tabel separat.

16. Separarea verticală

Partiționarea verticală se referă la împărțirea unui tabel în coloane pentru a îmbunătăți performanța.
Exemplul 1. Dacă adresele sunt stocate în tabelul utilizatorilor, atunci nu este un fapt că veți avea nevoie de ele foarte des. Puteți împărți tabelul și puteți stoca adrese masa separata. Astfel, masa de utilizator va fi redusă în dimensiune. Productivitatea va crește.
Exemplul 2: aveți un câmp „last_login” într-un tabel. Este actualizat de fiecare dată când utilizatorul se autentifică pe site. Dar toate modificările aduse tabelului îi șterg memoria cache. Prin stocarea acestui câmp într-un alt tabel, veți menține la minimum modificările aduse tabelului utilizatorilor.
Dar dacă utilizați constant join-uri pe aceste mese, va duce la performanțe slabe.

17. Separați interogările mari DELETE și INSERT

Dacă trebuie să faceți o cerere mare de ștergere sau inserare a datelor, trebuie să aveți grijă să nu rupeți aplicația. Performanţă mare cerere poate încuia masa și duce la defecțiune intreaga aplicatie.
Apache poate face mai multe procese paralele simultan. Prin urmare, funcționează mai eficient dacă scripturile sunt executate cât mai repede posibil.
Dacă blocați mesele termen lung(de exemplu, timp de 30 de secunde sau mai mult), apoi, cu trafic mare pe site, poate apărea o coadă mare de procese și solicitări, ceea ce poate duce la muncă lentă site-ul sau chiar o prăbușire a serverului.
Dacă aveți interogări ca aceasta, utilizați LIMIT pentru a le rula în rafale mici.

în timp ce (1) ( mysql_query( „ȘTERGERE DIN jurnalele WHERE data_log<= "2009-10-01" LIMIT 10000" ); if (mysql_affected_rows() == 0 ) ( // a eliminat pauză ; ) // scurtă pauză somn (50000); )

18. Coloanele mici sunt mai rapide

Pentru o bază de date, lucrul cu hard disk-ul este poate cel mai slab punct. Înregistrările mici și compacte sunt de obicei mai bune în ceea ce privește performanța, deoarece... reduce munca pe disc.
Documentația MySQL are o listă de cerințe de stocare a datelor pentru toate tipurile de date.
Dacă tabelul dvs. va stoca câteva rânduri, atunci nu are sens să faceți cheia principală de tip INT; ar putea fi mai bine să o faceți MEDIUMINT, SMALLINT sau chiar TINYINT. Dacă nu trebuie să stocați ora, utilizați DATE în loc de DATETIME.
Cu toate acestea, ai grijă ca lucrurile să nu iasă ca Slashdot.

19. Alegeți tipul de masă potrivit

20. Folosiți ORM

21. Fii atent la conexiunile persistente

Conexiunile persistente sunt concepute pentru a reduce costul stabilirii comunicării cu MySQL. Odată ce o conexiune este creată, aceasta rămâne deschisă după finalizarea scriptului. Data viitoare, acest script va folosi aceeași conexiune.
mysql_pconnect() în PHP
Dar acest lucru sună bine doar în teorie. Din experiența mea personală (și a altora), utilizarea acestei funcții nu este justificată. Veți avea probleme serioase cu limitele de conectare, limitele de memorie și așa mai departe.
Apache creează multe fire paralele. Acesta este motivul principal pentru care conexiunile persistente nu funcționează atât de bine pe cât ne-am dori. Înainte de a utiliza mysql_pconnect(), consultați administratorul de sistem.

→ Optimizarea interogărilor MySQL

MySQL are o gamă largă de funcții pentru diferite sortări ( COMANDA PENTRU), grupuri ( A SE GRUPA CU), asociațiile ( LEFT JOIN sau ÎNSCRIEȚI DREPT) și așa mai departe. Toate sunt cu siguranță convenabile, dar în condiții de solicitări unice. De exemplu, dacă personal trebuie să descoperi ceva în baza de date folosind o grămadă de tabele și link-uri, atunci, pe lângă funcțiile de mai sus, poți și chiar trebuie să folosești operatori condiționali DACĂ. Principala greșeală a programatorilor începători este dorința de a aplica astfel de interogări în codul de lucru al site-ului. În acest caz, o interogare complexă este cu siguranță frumoasă, dar dăunătoare. Chestia este că orice operator de sortare, grupare, alăturare sau imbricat nu poate fi executat în RAM și utilizează hard diskul pentru a crea tabele temporare. Și hard disk-ul, după cum știți, este blocajul serverului.

Reguli pentru optimizarea interogărilor mysql

1. Evitați interogările imbricate

Aceasta este cea mai gravă greșeală. Procesul părinte va aștepta întotdeauna finalizarea procesului copil și în acest moment păstrează o conexiune la baza de date, folosește discul și încarcă iowait. Două solicitări paralele la baza de date și efectuează filtrarea necesară în interpretul serverului ( Perl, PHP etc.) va fi executat cu un ordin de mărime mai rapid decât cel imbricat.

Exemple în perl ce sa nu faci:

My $sth = $dbh->prepare("SELECT elementID,elementNAME,groupID FROM tbl WHERE groupID IN(2,3,7)"); $sth->execute(); while (my @row = $sth->fetchrow_array()) ( my $groupNAME = $dbh->selectrow_array("SELECT groupNAME FROM groups WHERE groupID = $row"); ### Să presupunem că trebuie să colectați numele de grupurile ### și adăugați-le la sfârșitul matricei de date push @row => $groupNAME; ### Faceți altceva... )

sau in niciun caz ca acesta:

My $sth = $dbh->prepare("SELECT elementID,elementNAME,groupID FROM tbl WHERE groupID IN(SELECT groupID FROM grupuri WHERE groupNAME = "First" OR groupNAME = "Secund" SAU groupNAME = "Seventh")");

Dacă este nevoie de astfel de acțiuni, în toate cazurile este mai bine să utilizați un hash, o matrice sau orice altă cale de filtrare.

Un exemplu în perl, așa cum fac de obicei:

%grupurile mele; my $sth = $dbh->prepare("SELECT groupID,groupNAME FROM groups WHERE groupID IN(2,3,7)"); $sth->execute(); while (@row-ul meu = $sth->fetchrow_array()) ( $groups($row) = $row; ) ### Acum să facem preluarea principală fără subinterogarea my $sth2 = $dbh->prepare("SELECT elementID ,elementNAME,groupID FROM tbl WHERE groupID IN(2,3,7)"); $sth2->execute(); while (@row-ul meu = $sth2->fetchrow_array()) ( push @row => $groups($row); ### Să facem altceva... )

2. Nu sortați, grupați sau filtrați în baza de date

Dacă este posibil, nu utilizați operatorii ORDER BY, GROUP BY sau JOIN în interogările dvs. Toți folosesc tabele temporare. Dacă sortarea sau gruparea este necesară doar pentru afișarea elementelor, de exemplu alfabetic, este mai bine să efectuați aceste acțiuni în variabilele interpretor.

Exemple Perl despre cum să nu sortați:

My $sth = $dbh->prepare("SELECT elementID,elementNAME FROM tbl WHERE groupID IN(2,3,7) ORDER BY elementNAME"); $sth->execute(); în timp ce (@row-ul meu = $sth->fetchrow_array()) (printează qq($row => $row); )

Un exemplu în perl despre cum de obicei sortez:

Lista mea de $ = $dbh->selectall_arrayref("SELECT elementID,elementNAME FROM tbl WHERE groupID IN(2,3,7)"); foreach (sortare ( $a-> cmp $b-> ) @$list)( print qq($_-> => $_->); )

Este mult mai rapid în acest fel. Diferența este vizibilă mai ales dacă există o mulțime de date. În cazul în care trebuie să sortați după perl pentru mai multe câmpuri, puteți aplica sortarea Schwartz. Dacă este necesară sortarea aleatorie ORDER BY RAND() - utilizați sortarea aleatorie în perl.

3. Folosiți indici

În timp ce sortarea în baza de date poate fi abandonată în unele cazuri, WHERE este puțin probabil să fie posibilă. Prin urmare, pentru câmpurile care vor fi comparate, este necesar să se stabilească indecși. Sunt ușor de făcut.

Cu aceasta cerere:

ALTER TABLE `any_db`.`any_tbl` ADD INDEX `text_index`(`text_fld`(255));

Unde 255 este lungimea cheii. Pentru unele tipuri de date nu este necesar. Consultați documentația MySQL pentru detalii.

Cum să optimizați interogările MySQL?


Pentru un site obișnuit, care nu este deosebit de vizitat, nu există o mare diferență dacă interogările MySQL la baza de date sunt optimizate sau nu. Dar pentru serverele de producție cu încărcare mare, diferența dintre SQL corect și incorect este uriașă, iar în timpul execuției acestea pot afecta semnificativ comportamentul și fiabilitatea serviciilor. În acest articol, voi analiza cum să scriu interogări rapide și factorii care le fac să încetinească.

De ce MySQL?

Astăzi se vorbește mult despre Dig Data și despre alte tehnologii noi. Soluțiile NoSQL și cloud sunt grozave, dar o mulțime de software popular (cum ar fi WordPress, phpBB, Drupal) încă rulează pe MySQL. Migrarea la cele mai recente soluții poate duce la mai mult decât modificarea configurației pe servere. În plus, eficiența MySQL este încă la nivel, în special versiunea Percona.

Nu faceți greșeala comună de a arunca din ce în ce mai mult hardware pentru a rezolva problema interogărilor lente și a încărcării mari a serverului - este mai bine să mergeți la rădăcina problemei. Creșterea puterii procesoarelor și a hard disk-urilor și adăugarea de memorie RAM este, de asemenea, un anumit tip de optimizare, însă nu despre asta vom vorbi în acest articol. De asemenea, prin optimizarea site-ului și rezolvarea problemei cu hardware-ul, încărcarea va crește doar exponențial. Prin urmare, aceasta este doar o soluție pe termen scurt.

O bună înțelegere a SQL este cel mai important instrument pentru un dezvoltator web; vă va permite să optimizați și să utilizați în mod eficient bazele de date relaționale. În acest articol ne vom concentra pe o bază de date populară cu sursă deschisă folosită adesea împreună cu PHP, MySQL.

Pentru cine este acest articol?

Pentru dezvoltatorii web, arhitecții și dezvoltatorii de baze de date și administratorii de sistem familiarizați cu MySQL. Dacă nu ați folosit MySQL înainte, acest articol poate să nu vă fie de mare folos, dar voi încerca totuși să fiu cât mai informativ și util, chiar și pentru cei care cunosc MySQL.

Mai întâi faceți backup

Vă recomand să faceți următorii pași pe baza MySQL-ului cu care lucrați, dar asigurați-vă că faceți o copie de rezervă. Dacă nu aveți o bază de date cu care să lucrați, vă voi oferi exemple pentru crearea propriei baze de date, acolo unde este cazul.

Efectuarea de copii de rezervă MySQL este ușoară folosind utilitarul mysqldump:

$ mysqldump myTab > myTab-backup.sql Puteți afla mai multe despre mysqldump.

Ce face o interogare lentă?

Iată o listă generală a factorilor care afectează viteza solicitărilor și încărcarea serverului:

  • indici de tabel;
  • condiția WHERE (și folosind funcții interne MySQL, cum ar fi IF sau DATE);
  • sortați după ORDER BY;
  • repetarea frecventă a cererilor identice;
  • tipul mecanismului de stocare a datelor (InnoDB, MyISAM, Memory, Blackhole);
  • nefolosind versiunea Percona;
  • configurații server (my.cnf / my.ini);
  • ieșiri mari de date (mai mult de 1000 de rânduri);
  • conexiune instabilă;
  • configurație distribuită sau cluster;
  • Design slab al mesei.
Vom aborda toate aceste probleme în continuare. De asemenea, instalați Percona dacă nu utilizați deja acest înlocuitor încorporat pentru MySQL standard - va oferi un impuls uriaș puterii bazei de date.

Ce sunt indicii?

Indecșii sunt utilizați în MySQL pentru a căuta rânduri cu valorile de coloană specificate, cum ar fi clauza WHERE. Fără indexuri, MySQL trebuie, începând cu primul rând, să citească întreg tabelul căutând valori relevante. Cu cât masa este mai mare, cu atât costurile sunt mai mari.

Dacă un tabel are indici pe coloanele care vor fi folosite în interogare, MySQL va găsi rapid locațiile informațiilor de care are nevoie, fără a scana întregul tabel. Acest lucru este mult mai rapid decât căutarea fiecărei linii secvenţial.

Conexiune instabilă?

Când aplicația dvs. se conectează la o bază de date și este configurată o conexiune stabilă, aceasta va fi utilizată de fiecare dată fără a fi nevoie să deschideți o nouă conexiune de fiecare dată. Aceasta este soluția optimă pentru mediul de lucru.

Reducerea repetarii frecvente a cererilor identice

Cel mai rapid și mai eficient mod pe care l-am găsit pentru a face acest lucru este de a crea o stocare a interogărilor și a rezultatelor acestora folosind Memcached sau Redis. Cu Memcache puteți stoca cu ușurință în cache rezultatul interogării dvs., de exemplu, astfel:

connect("localhost",11211); $cacheResult = $cache->get("nume-cheie"); if($cacheResult)( //nu este nevoie de o interogare $result = $cacheResult; ) else ( //rulați interogarea $mysqli = mysqli("p:localhost","nume utilizator","parolă","tabel" ); //adăugați p: pentru stocare pe termen lung $sql = "SELECT * FROM posts LEFT JOIN userInfo folosind (UID) WHERE posts.post_type = "post" || posts.post_type = "articol" ORDER BY coloana LIMIT 50" ; $rezultat = $mysqli->interogare($sql); $memc->set("nume-cheie", $result->fetch_array(), MEMCACHE_COMPRESSED,86400); ) //Parola $cacheResult la șablonul $template- >assign("postări", $cacheResult); ?> Acum o interogare grea folosind LEFT JOIN va fi executată doar o dată la 86.400 de secunde (adică o dată pe zi), ceea ce va reduce semnificativ încărcarea serverului MySQL, lăsând resurse pentru alte conexiuni.

Notă: Adăugați p: la începutul argumentului gazdă MySQLi pentru a crea o conexiune persistentă.

Configurație distribuită sau în cluster

Când datele devin din ce în ce mai multe, iar viteza serviciului tău scade, panica te poate pune stăpânire. O soluție rapidă ar putea fi distribuirea resurselor (sharding). Cu toate acestea, nu vă recomand să faceți acest lucru decât dacă aveți foarte experiență, deoarece distribuția face în mod inerent structurile de date complexe.

Design slab al mesei

Crearea de scheme de baze de date nu este o muncă dificilă dacă urmați regulile de aur de a lucra cu constrângeri și de a ști ce va funcționa. De exemplu, stocarea imaginilor în celulele BLOB este foarte confuză - stocarea căii fișierului într-o celulă VARCHAR este o soluție mult mai bună.

Asigurarea designului potrivit pentru utilizarea corectă este esențială în crearea aplicației dvs. Stocați date diferite în tabele diferite (de exemplu, categorii și articole) și asigurați-vă că relațiile mai multe la unu și unu la mai multe pot fi asociate cu ușurință cu ID-uri. Utilizarea FOREIGN KEY în MySQL este ideală pentru stocarea datelor în cascadă în tabele.

Când creați un tabel, rețineți următoarele:

  • Creați tabele eficiente pentru a vă rezolva problemele, în loc să umpleți tabelele cu date și relații inutile.
  • Nu vă așteptați ca MySQL să vă execute logica de afaceri sau programarea - datele ar trebui să fie gata pentru ca limbajul dvs. de scripting să insereze un rând. De exemplu, dacă trebuie să sortați o listă în ordine aleatorie, faceți-o într-o matrice PHP, fără a utiliza ORDER BY din arsenalul MySQL.
  • Utilizați tipuri de index UNIQUE pentru seturi de date unice și utilizați ON DUPLICATE KEY UPDATE pentru a menține data actualizată, de exemplu pentru a ști când a fost modificat ultima dată un rând.
  • Utilizați tipul de date INT pentru a stoca numere întregi. Dacă nu specificați o dimensiune a tipului de date, MySQL o va face pentru dvs.
Bazele optimizării

Pentru a optimiza eficient, trebuie să aplicăm trei abordări aplicației dvs.:

  1. Analiză (înregistrarea interogărilor lente, studierea sistemului, analiza interogărilor și proiectarea bazei de date)
  2. Cerințe de execuție (câți utilizatori)
  3. Limitări ale tehnologiei (viteza hardware, utilizarea incorectă a MySQL)
Analiza se poate face în mai multe moduri. Mai întâi ne vom uita la cele mai evidente modalități de a căuta sub capota MySQL-ului dvs. unde rulează interogări. Primul instrument de optimizare din arsenalul tău este EXPLAIN. Dacă adăugați această declarație înainte de interogarea SELECT, rezultatul interogării va fi astfel:

După cum puteți vedea, coloanele stochează informații importante despre cerere. Coloanele cărora ar trebui să le acordați cea mai mare atenție sunt posibil_keys și Extra.

Coloana posibil_keys va afișa indecșii la care MySQL a avut acces pentru a executa interogarea. Uneori trebuie să atribuiți indecși pentru ca interogarea să ruleze mai rapid. Coloana Extra va indica dacă a fost folosit un alt WHERE sau ORDER BY. Cel mai important lucru de observat este dacă Utilizarea Filesort este în ieșire.

Ce face Utilizarea Filesort este menționat în ajutorul MySQL:

MySQL trebuie să facă o trecere suplimentară pentru a afla cum să returneze rândurile în formă sortată. Această sortare are loc pe toate rândurile în funcție de tipul de îmbinare și stochează cheia de sortare și indicatorul de rând pentru toate rândurile care se potrivesc cu clauza WHERE. Cheile sunt sortate și rândurile sunt returnate în ordinea corectă.
O trecere suplimentară vă va încetini aplicarea și ar trebui evitată cu orice preț. Un alt rezultat critic al Extra pe care ar trebui să-l evităm este Utilizarea temporară. Se spune că MySQL a trebuit să creeze un tabel temporar pentru a executa interogarea. Evident, aceasta este o utilizare groaznică a MySQL. În acest caz, rezultatul interogării ar trebui să fie stocat în Redis sau Memcache și să nu fie executat din nou de utilizatori.

Pentru a evita problema cu Utilizarea Filesort trebuie să ne asigurăm că MySQL folosește INDEX. În prezent, există mai multe chei specificate în posibil_keys din care să alegeți, dar MySQL poate selecta doar un index pentru interogarea finală. De asemenea, indecșii pot fi alcătuiți din mai multe coloane și puteți introduce și indicii pentru optimizatorul MySQL, arătând spre indecșii pe care i-ați creat.

Aluzie index

Optimizatorul MySQL va folosi statistici bazate pe interogări de tabel pentru a selecta cel mai bun index pe care să ruleze interogarea. Funcționează destul de simplu, bazat pe logica statistică încorporată, așa că, având în vedere mai multe opțiuni, nu face întotdeauna alegerea corectă fără ajutorul unor indicii. Pentru a vă asigura că a fost folosită cheia corectă (sau incorectă), utilizați cuvintele cheie FORCE INDEX, USE INDEX și IGNORE INDEX în interogarea dvs. Puteți citi mai multe despre indicarea indexului în ajutorul MySQL.

Pentru a afișa cheile de tabel, utilizați comanda SHOW INDEX. Puteți specifica mai multe indicii pentru utilizare de către optimizator.

Pe lângă EXPLAIN, există și cuvântul cheie DESCRIBE. Cu DESCRIBE, puteți vizualiza informațiile din tabel după cum urmează:

Adăugarea unui index

Pentru a adăuga indecși în MySQL, trebuie să utilizați sintaxa CREATE INDEX. Există mai multe tipuri de indici. FULLTEXT este folosit pentru căutarea cu text integral, iar UNIQUE este folosit pentru a stoca date unice.

Pentru a adăuga un index la tabelul dvs., utilizați următoarea sintaxă:

Mysql> CREATE INDEX idx_bookname ON `carti` (bookname(10)); Aceasta va crea un index pe tabelul de cărți care va folosi primele 10 litere ale coloanei care stochează titlurile cărților și este de tip varchar. În acest caz, orice căutare cu o interogare WHERE pe un titlu de carte cu o potrivire de până la 10 caractere va produce același rezultat ca scanarea întregului tabel de la început până la sfârșit.

Indici compoziți

Indecii au un impact mare asupra vitezei de execuție a interogărilor. Doar atribuirea unei chei unice principale nu este suficientă - cheile compuse sunt un caz real de utilizare în reglarea MySQL, care uneori necesită unele verificări A/B folosind EXPLAIN.

De exemplu, dacă trebuie să facem referire la două coloane în condiția unei clauze WHERE, o cheie compusă ar fi o soluție ideală.

Mysql> CREATE INDEX idx_composite ON utilizatori (nume utilizator, activ); Odată ce am creat o cheie bazată pe coloana nume de utilizator, care stochează numele utilizatorului și coloanele active de tip ENUM, care determină dacă contul său este activ. Acum totul este optimizat pentru o interogare care va folosi WHERE pentru a găsi un nume de utilizator valid cu un cont activ (activ = 1).

Cât de rapid este MySQL-ul tău?

Să activăm profilarea pentru a arunca o privire mai atentă asupra interogărilor MySQL. Acest lucru se poate face prin rularea comenzii set profiling=1, după care trebuie să rulați show profiles pentru a vedea rezultatul.

Dacă utilizați PDO, rulați următorul cod:

$db->query("set profile=1"); $db->query("selectați titlul, corpul, etichetele din postări"); $rs = $db->query("arată profiluri"); $db->query("set profiling=0"); // dezactivează profilarea după executarea interogării $records = $rs->fetchAll(PDO::FETCH_ASSOC); // obțineți rezultatele profilării $errmsg = $rs->errorInfo(); //Prinți câteva erori aici Același lucru se poate face folosind mysqli:

$db = new mysqli($gazdă,$nume utilizator,$parolă,$nume db); $db->query("set profile=1"); $db->query("selectați titlul, corpul, etichetele din postări"); if ($rezultat = $db->interogare(„Afișează profiluri”, MYSQLI_USE_RESULT)) ( în timp ce ($rând = $rezultat->fetch_row()) ( var_dump($rând); ) $rezultat->close(); ) if ($rezultat = $db->query("arată profilul pentru interogarea 1", MYSQLI_USE_RESULT)) ( în timp ce ($rând = $rezultat->fetch_row()) ( var_dump($rând); ) $rezultat->close( ); ) $db->query("set profiling=0"); Aceasta vă va returna datele profilate care conțin timpul de execuție a interogării în al doilea element al matricei asociative.

Array(3) ( => string(1) "1" => string(10) "0.00024300" => string(17) "select headline, body, tags from posts" ) Această interogare a durat 0,00024300 de secunde pentru a fi finalizată. Este destul de rapid, așa că să nu ne facem griji. Dar când cifrele devin mari, trebuie să ne uităm mai profund. Accesați aplicația dvs. pentru a practica cu un exemplu de lucru. Verificați constanta DEBUG din configurația bazei de date și apoi începeți să explorați sistemul activând ieșirea profilării utilizând funcțiile var_dump sau print_r. În acest fel, puteți trece de la o pagină la alta din aplicația dvs., obținând profiluri convenabile ale sistemului.

Audit complet al bazei de date a site-ului dvs

Pentru a efectua un audit complet al solicitărilor dvs., activați înregistrarea. Unii dezvoltatori de site-uri web se tem că înregistrarea în jurnal are un impact semnificativ asupra execuției și încetinește și mai mult solicitările. Cu toate acestea, practica arată că diferența este nesemnificativă.

Pentru a activa înregistrarea în MySQL 5.1.6, utilizați variabila globală log_slow_queries, puteți, de asemenea, să marcați un fișier pentru înregistrare folosind variabila slow_query_log_file. Acest lucru se poate face executând următoarea interogare:

Setați global log_slow_queries = 1; setează global slow_query_log_file = /dev/slow_query.log; Puteți specifica acest lucru și în fișierele de configurare /etc/my.cnf sau my.ini ale serverului dumneavoastră.

După efectuarea modificărilor, nu uitați să reporniți serverul MySQL cu comanda necesară, de exemplu service mysql restart dacă utilizați Linux.

În versiunile MySQL după 5.6.1, variabila log_slow_queries este depreciată și se folosește în schimb slow_query_log. De asemenea, pentru o depanare mai convenabilă, puteți activa ieșirea tabelului setând variabila log_output la TABLE, cu toate acestea, această funcție este disponibilă numai din MySQL 5.6.1.

Log_output = TABLE; log_queries_not_using_indexes = 1; timp_interogare_lung = 1; Variabila long_query_time specifică numărul de secunde după care interogarea este considerată lentă. Valoarea este 10, iar minimul este 0. De asemenea, puteți specifica milisecunde folosind o fracție; acum am indicat o secundă. Și acum fiecare cerere care va fi executată mai mult de 1 secundă este înregistrată în jurnalele din tabel.

Înregistrarea se va face în tabelele mysql.slow_log și mysql.general_log ale bazei de date MySQL. Pentru a dezactiva înregistrarea, schimbați log_output la NONE.

Conectarea pe un server de producție

Pe un server de producție care deservește clienții, este mai bine să utilizați înregistrarea doar pentru o perioadă scurtă și să monitorizați încărcarea pentru a nu crea încărcare inutilă. Dacă serviciul dvs. este supraîncărcat și este nevoie de atenție imediată, încercați să izolați problema rulând SHOW PROCESSLIST sau accesând tabelul information_schema.PROCESSLIST rulând SELECT * FROM information_schema.PROCESSLIST;.

Înregistrarea tuturor solicitărilor pe un server de producție vă poate oferi o mulțime de informații și este un instrument bun în scopuri de cercetare atunci când revizuiți un proiect, dar jurnalele pe perioade lungi nu vă vor oferi prea multe informații utile în comparație cu jurnalele pe o perioadă de până la 48 de ore (încercați să monitorizați sarcinile de vârf pentru a avea șansa de a explora mai bine execuția interogărilor).

Notă: dacă aveți un site care se confruntă cu valuri de trafic și puțin sau deloc trafic uneori, cum ar fi un site de sport în afara sezonului, atunci utilizați aceste informații pentru a crea și a studia înregistrarea în jurnal.

Înregistrarea cererilor multiple

Nu numai că este important să fii conștient de interogările care durează mai mult de o secundă pentru a se executa, dar trebuie să fii conștient și de interogările care sunt executate de sute de ori. Chiar dacă interogările sunt executate rapid, într-un sistem ocupat ele pot consuma toate resursele.

Acesta este motivul pentru care trebuie să fiți întotdeauna în gardă după ce faceți modificări într-un proiect live - acesta este momentul cel mai critic pentru funcționarea oricărei baze de date.

Cache cald și rece

Numărul de solicitări și încărcarea serverului au un impact puternic asupra execuției și pot afecta, de asemenea, timpul de execuție al solicitărilor. Când dezvoltați, ar trebui să faceți o regulă ca fiecare solicitare să nu dureze mai mult de o fracțiune de milisecundă (0.0xx sau mai rapid) pentru a se finaliza pe un server gratuit.

Utilizarea Memcache are un efect puternic asupra încărcării serverelor și va elibera resurse care execută cereri. Asigurați-vă că utilizați Memcached în mod eficient și că ați testat aplicația cu un cache cald (date încărcate) și un cache rece.

Pentru a evita rularea pe un server de producție cu un cache gol, este o idee bună să aveți un script care să colecteze toată memoria cache necesară înainte de a porni serverul, astfel încât un aflux mare de clienți să nu reducă timpul de pornire a sistemului.

Remedierea interogărilor lente

Acum că înregistrarea este configurată, este posibil să fi găsit câteva interogări lente pe site-ul dvs. Să le reparăm! Ca exemplu, voi arăta mai multe probleme comune și puteți vedea logica pentru remedierea lor.

Dacă nu ați găsit încă o interogare lentă, verificați setările long_query_time dacă utilizați această metodă de înregistrare. În caz contrar, după ce ați verificat toate interogările de profilare (set profiling=1), faceți o listă de interogări care durează mai mult decât o fracțiune de milisecundă (0.000x secunde) și începeți de acolo.

Probleme comune

Iată cele mai frecvente șase probleme pe care le-am găsit la optimizarea interogărilor MySQL:

ORDER BY și sortare fișiere

Prevenirea sortării fișierelor nu este uneori posibilă din cauza clauzei ORDER BY. Pentru optimizare, stocați rezultatul în Memcache sau efectuați sortarea în logica aplicației.

Folosind ORDER BY cu WHERE și LEFT JOIN

ORDER BY face interogările foarte lente. Dacă este posibil, încercați să nu utilizați ORDER BY. Dacă aveți nevoie de sortare, atunci utilizați sortarea după indici.

Utilizarea ORDER BY pe coloanele temporare

Doar nu o face. Dacă trebuie să combinați rezultatele, faceți-o în logica aplicației; Nu utilizați filtrarea sau sortarea pe tabelul temporar de interogare MySQL. Acest lucru necesită o mulțime de resurse.

Se ignoră indexul FULLTEXT

Utilizarea LIKE este cea mai bună modalitate de a încetini căutarea textului integral.

Selectarea inutilă a unui număr mare de rânduri

Uitarea de LIMIT în interogarea dvs. poate crește foarte mult timpul necesar pentru preluarea din baza de date, în funcție de dimensiunea tabelelor.

Utilizarea excesivă a JOIN în loc de a crea tabele sau vizualizări compuse

Când utilizați mai mult de trei sau patru operatori LEFT JOIN într-o singură interogare, întrebați-vă: este totul corect aici? Continuați cu excepția cazului în care aveți un motiv întemeiat, de exemplu - interogarea nu este folosită des pentru ieșire în panoul de administrare sau rezultatul rezultat poate fi stocat în cache. Dacă trebuie să efectuați o interogare cu un număr mare de operațiuni de îmbinare a tabelelor, atunci este mai bine să vă gândiți la crearea de tabele compuse din coloanele necesare sau la utilizarea vizualizărilor.

Asa de

Am discutat despre elementele de bază ale optimizării și despre instrumentele necesare pentru a face treaba. Am examinat sistemul folosind profilarea și declarația EXPLAIN pentru a vedea ce se întâmplă cu baza de date și cum am putea îmbunătăți designul.

Am analizat, de asemenea, câteva exemple și capcane clasice în care puteți cădea când utilizați MySQL. Folosind indicii de index, ne putem asigura că MySQL va selecta indecșii necesari, mai ales când se fac selecții multiple pe același tabel. Pentru a continua studiul subiectului, vă sfătuiesc să priviți spre proiectul Percona.

  • Serghei Savenkov

    un fel de recenzie „scurtă”... de parcă s-ar grăbi undeva