O sa incerc pe local sa testez, mie frica de o modificare asa serioasa, pentru ca in aceste tabele sunt salvate statistici despre banii oamenilor, e foarte important sa nu bulesc nimic Merci!
Ca idee, din toata experienta mea cu MySQL-ul, nu exista o solutie unica sau care e clar cea mai performanta, tot timpul trebuie sa incerci, testezi. Din pacate trial & error e calea de urmat cu MySQL.
Sper sa pot face modificarile de care zici cu datele existente,pt ca la acest volum de date, am incercat sa adaug o coloana noua, si se blocheaza socket-ul, nu ma lasa.
pregatesti structura la o tabela noua si muti datele in ea daca dureaza prea mult schimbarile. complicat daca e live si scrie tot timpul in ea, nu prea poti fara downtime.
Probabil e redundantă întrebarea, dar nu a mai pus-o nimeni: ai indecși pe toate coloanele pe care trebuie să ai? 5 secunde la atâtea intrări în tabel înseamnă că probabil ai, dar ca să fim siguri…
Da, am indexi pe coloanele care sunt in clauza where.
La partea cu indecsii trebuie precizat de fiecare data si faptul ca s-a rulat explain si ca se folosesc acei indecsi. Am invatat multe despre optimizari din planul de explain.
Cei de la MariaDB au un column store care ar fi ideal pentru astfel de operatii. Nu am reusit sa aflu daca e si pentru MySQL. Dar ar fi un upgrade destul de simplu ma gandesc[1], caci MariaDB e un fork compatibil cu MySQL. Si te-ar lasa sa faci query-uri mai flexibile decat un tabel cu chestii pre-calculate.
[1] Mai simplu decat sa adaugi un sistem de baze de date nou, cel putin.
chiar daca ai indecsi in plus sau nefolositi, nu-ti incetinesc query-urile (selecturile cel putin). MySQL decide el daca foloseste sau nu un index existent.
N-a observat nimeni că face GROUP BY date
iar date
nu este o coloană în tabel ci o valoare generată de DATE_FORMAT()
?
Eu zic să-ncerci să mai adaugi o coloană de tip DATE
în care să ții doar data și pe care să faci GROUP BY
.
Am încercat un query simplu cu GROUP BY created_on
versus GROUP BY DATE_FORMAT(created_at)
pe un set de date de aproximativ 4.300.000 de intrări (dintr-un proiect pe care lucrez) și durata a fost de cel puțin 3 ori mai mică.
O altă chestie ce mi-a redus timpii cu 30% a fost modificarea câmpurilor extrase. Din ce văd în codul tău, tu nu ai decât 3 valori de bază, restul fiind derivate din cele 3:
COUNT(1) AS clicks
AVG(quality) AS avg_quality
SUM(price) AS sum_price
iar query-ul tău ar putea arăta așa:
SELECT
clicks,
ROUND(clicks * avg_quality, 0) AS clicks_calitate,
ROUND(avg_quality * 100, 2) AS quality,
ROUND(sum_price, 3) AS earnings,
ROUND((sum_price / (clicks * avg_quality)), 3) AS ecpc,
ROUND((sum_price * 1000 / (clicks * avg_quality)), 3) AS ecpm,
created_on
FROM (
SELECT
COUNT(1) AS clicks,
AVG(quality) AS avg_quality,
SUM(price) AS sum_price,
created_on
FROM reports
WHERE
/* condițiile tale */
GROUP BY created_on
ORDER BY created_on ASC
) AS aggregate_data
EDIT
Mi-am amintit încă o chestie ce s-ar putea să ajute…
Sper să nu greșesc, dar MySQL parcurge indecșii iar dacă unul se potrivește condițiilor, dă fuga la tabelă pentru a citi restul valorilor din câmpurile pe care le ceri. Dacă creezi un index care să cuprindă doar cele 4 coloane pe care le folosești (user_id, created_on, price, quality
), MySQL nu va mai pierde timpul pentru a citi tot rândul din tabelă, ci va folosi valorile direct din index.
Am testat acum pentru un user care e in top ca inregistrari, si pentru el dashboardul (adica procedura aia stocata) ruleaza 1.3 minute, iar raportul pe ultima zi (primul query ce l-am pus) in jur de 1,17 minute E foarte lent, acum am un call cu clientul sa iau o decizie. E grav.
Partitionarea tabelei ai luat in calcul? https://dev.mysql.com/doc/refman/5.7/en/partitioning-types.html
Așa-ți trebuie dacă folosești Laravel!!!
Am recitit mesajele tale pentru că mi se părea absurd ca rezultatele filtrate pentru o singură zi (maxim 300.000 de intrări zilnice, după cum ai zis) să dureze atât. Și mi-a picat fisa:
->whereDate('created_at','>=',$start_date)
este de fapt
WHERE DATE(`created_at`) >= '$start_date'
nu? De-aia ai întrebat într-un mesaj anterior, așa-i?
Ceea ce înseamnă că ți-ai băgat picioarele-n orice index aveai definit pe coloana aia!!! Normal că merge prost query-ul!
Fă
$start_date .= ' 00:00:00';
$end_date .= ' 23:59:59';
și renunță la ->whereDate()
. Nu văd cum ar rula mai mult de o secundă query-ul cu raportul zilnic după ce faci asta!
Pana la urma am migrat pe MariaDB, a facut cineva niste optimizari prin ceva .bin-uri (eu nu le am cu astea) si a iesit foarte rapid. De exemplu aveam un user cu vreo 2 mln intrari, si dura o procedura 2 minute, acum dureaza fix 10s. E super. Merci toata lumea pentru integrare!