»Hjemmesiden er træg« ender næsten altid i databasen. Kontrollen tager nogle minutter, hvis den udføres i den rigtige rækkefølge: først det, der ses, mens forsinkelsen står på, så det, der har bygget sig op over uger.
Man må se i problemets øjeblik
Hovedreglen: den mest værdifulde information er tilgængelig præcis, når alt er langsomt. Første kommando til MySQL:
sudo mysqladmin processlist
mysql -e 'SHOW FULL PROCESSLIST'
Til PostgreSQL:
sudo -u postgres psql -c "SELECT pid, state, wait_event, now()-query_start AS dur, query
FROM pg_stat_activity WHERE state != 'idle' ORDER BY dur DESC;"
Du ser, hvad der kører lige nu, og hvor længe. Svaret ses som regel med det samme: én tung forespørgsel holder de andre, eller hundrede ens forespørgsler venter på, at en lås frigives.
Forbindelser
mysql -e "SHOW GLOBAL STATUS LIKE 'Threads_connected'"
mysql -e "SHOW GLOBAL STATUS LIKE 'Max_used_connections'"
mysql -e "SHOW VARIABLES LIKE 'max_connections'"
Ligger Max_used_connections tæt op ad max_connections, får applikationen periodisk afvisningen »for mange forbindelser«. At hæve grænsen er ikke det første, man gør: hver forbindelse optager hukommelse, og en hævet grænse på en server, der allerede mangler hukommelse, fremskynder blot vejen til swap. Først bør man forstå, hvorfor forbindelserne ikke frigives: som regel skyldes det en lang forespørgsel eller manglende pulje på applikationssiden.
Her er det også nyttigt at se sagen fra den anden side: en kraftig stigning i antallet af forbindelser kan skyldes, at bots i massevis besøger en tung side — søgningen, katalogfilteret, rapportgeneratoren — og ikke øget popularitet.
Langsomme forespørgsler
Uden en logfil over langsomme forespørgsler kommer man ikke videre. MySQL:
SET GLOBAL slow_query_log = 'ON';
SET GLOBAL long_query_time = 1;
Efter et døgn:
sudo mysqldumpslow -s t -t 10 /var/log/mysql/mysql-slow.log
PostgreSQL, i postgresql.conf:
log_min_duration_statement = 1000
Derefter udvidelsen pg_stat_statements, som med det samme viser den samlede tid for hver forespørgsel:
SELECT calls, round(total_exec_time) AS total_ms, query
FROM pg_stat_statements ORDER BY total_exec_time DESC LIMIT 10;
Bemærk: det vigtige er ikke den længste forespørgsel, men den med størst samlet tid. En forespørgsel på 50 millisekunder, der køres tusind gange i minuttet, skader mere end en tisekunders rapport én gang i døgnet.
Indstillingen man oftest glemmer
For MySQL og MariaDB er det InnoDB-bufferpuljens størrelse. Standardværdien er 128 megabyte og har ikke ændret sig i årtier. På en server, hvor databasen er flere gigabyte, betyder det konstant læsning fra disk i stedet for fra hukommelse.
mysql -e "SHOW VARIABLES LIKE 'innodb_buffer_pool_size'"
En fornuftig målestok for en dedikeret databaseserver er halvdelen af arbejdshukommelsen; for en server, hvor webserveren og PHP arbejder ved siden af, er det en fjerdedel, med blikket på at swappen ikke skal starte. Den ene indstilling giver som regel mere end en uges forespørgselsoptimering.
To ting der umærkeligt æder disken
MySQL's binærlogfiler. De er nødvendige til replikering og gendannelse til et tidspunkt, vokser uafbrudt og slettes som standard undertiden slet ikke. Kontrol:
sudo du -sh /var/lib/mysql/*bin.*
mysql -e "SHOW VARIABLES LIKE 'binlog_expire_logs_seconds'"
Har du ingen replikering og bruger ikke gendannelse til et tidspunkt, sættes opbevaringstiden til fornuftige nogle døgn.
WAL-logfiler i PostgreSQL ved en glemt replikeringsslot. Et mindre kendt og farligere tilfælde: er en replikeringsslot oprettet, og forbrugeren ikke længere forbinder til den, er PostgreSQL forpligtet til at gemme alle logfiler siden frakoblingen — og vil gemme dem, indtil disken slipper op.
sudo -u postgres psql -c "SELECT slot_name, active, restart_lsn FROM pg_replication_slots;"
En inaktiv slot, ingen har brug for, skal slettes. Det er en af de få situationer, hvor en database garanteret kører disken i bund.
Størrelse og vækst
SELECT table_schema, round(sum(data_length+index_length)/1024/1024) AS mb
FROM information_schema.tables GROUP BY table_schema ORDER BY mb DESC;
Det nyttige er ikke så meget det aktuelle tal som ændringstakten. En database, der er fordoblet på en måned uden tilsvarende stigning i besøg, betyder som regel, at en logtabel skrives og aldrig ryddes — sessioner, jobhistorik, en udvidelses hændelseslog.
Sikkerhed på to linjer
Når du alligevel har åbnet databasekonsollen, er to ting værd at kontrollere. Det første: er databasen tilgængelig udefra (ss -tulpn | grep 3306) — udadtil skal den næsten aldrig vende. Det andet: konti med adgang fra enhver adresse:
mysql -e "SELECT user, host FROM mysql.user"
Poster med host = '%' betyder »forbindelse tilladt hvorfra som helst«. Er porten desuden åben, står kun en adgangskode mellem databasen og internettet.
Sikkerhedskopier
Det sidste, der er værd at sige. At en sikkerhedskopifil findes, garanterer intet — kun en gennemført gendannelse gør. En dump, der aldrig er rullet tilbage, kan lige så godt være afkortet, taget med en låsefejl eller indeholde den forkerte database. Det skal kontrolleres på forhånd og på en separat maskine, ikke den dag kopien er nødvendig.
Til den daglige overvågning rækker tre tal: antallet af forbindelser, antallet af langsomme forespørgsler i døgnet og databasens størrelse. Alle tre ændrer sig langsomt og forudsigeligt, og enhver kraftig ændring er et blik værd. Hvordan de ser ud på én side, viser demonstrationen nedenfor.