Am avut un incident acum câteva luni pe un MySQL 8.0.32, 16 vCPU, 64 GB RAM, InnoDB, cu vreo 400 GB de date și un vârf de 2.500 conexiuni active. Simptomul: la fiecare ~40 de minute, SHOW PROCESSLIST arăta 30–50 de query-uri în Sending data pe același tabel, iar replica de citire rămânea în urmă cu 90 de secunde. Nimeni nu putea spune care query era vinovatul, pentru că slow_query_log era pornit cu long_query_time = 2, iar agonia era făcută din zeci de query-uri de 300–600 ms care se suprapuneau.
Primul pas a fost să nu pornesc general_log. Pe un server cu 2.500 conexiuni, general_log scrie fiecare statement pe disc și transformă I/O-ul într-un bottleneck. În schimb, am folosit performance_schema, care era deja activat (performance_schema=ON, performance_schema_consumer_events_statements_history_long=ON). Am confirmat că istoricul lung ține 10.000 de evenimente pe thread:
SELECT NAME, ENABLED FROM performance_schema.setup_consumers
WHERE NAME LIKE 'events_statements%';
Apoi am extras top-ul după timp total, nu după timp mediu:
SELECT DIGEST_TEXT,
COUNT_STAR AS execs,
ROUND(SUM_TIMER_WAIT/1e12, 2) AS total_sec,
ROUND(AVG_TIMER_WAIT/1e9, 1) AS avg_ms,
SUM_ROWS_EXAMINED AS rows_examined
FROM performance_schema.events_statements_summary_by_digest
WHERE SCHEMA_NAME = 'app'
ORDER BY SUM_TIMER_WAIT DESC
LIMIT 20;
Rezultatul a fost clar: un SELECT ... WHERE status = ? AND created_at > ? ORDER BY id DESC LIMIT 50 cu 180.000 de execuții pe oră și rows_examined mediu de 12.000. Indexul exista pe (status, created_at), dar ORDER BY id DESC forța un filesort pentru că id nu era în index. Am adăugat un index covering (status, created_at, id) și am rescris query-ul să filtreze pe created_at cu o fereastră explicită, nu cu NOW() - INTERVAL. Timpul mediu a scăzut de la 340 ms la 4 ms, iar lag-ul replicii a dispărut.
De ce am ales calea asta: performance_schema consumă memorie (buffer-ele de istoric), dar nu atinge discul per statement și îți dă timp agregat pe digest, exact ce lipsește din slow log când ai multe query-uri mici. Costul e că events_statements_history_long se pierde la restart și că nu vezi parametrii concreți ai fiecărei execuții — pentru asta am folosit events_statements_history per thread, dar doar pe sesiuni țintă.
Unde se strică: dacă performance_schema e dezactivat la boot, nu-l poți activa complet din runtime fără restart; dacă ai DIGEST_TEXT trunchiat la 1024 caractere, query-urile lungi se amestecă între ele; iar pe MariaDB, events_statements_summary_by_digest nu are aceleași coloane. Am pățit și să confund SUM_TIMER_WAIT (picosecunde) cu microsecunde și să raportez de un milion de ori mai mult.
Voi cum separați query-urile „mici dar dese” de cele „rare dar grele” când slow log-ul nu ajută?


0 条评论
还没有评论,写下第一条。