”Sajten är trög” landar nästan alltid i databasen. Kontrollen tar några minuter om den görs i rätt ordning: först det som syns just under fördröjningen, sedan det som byggts upp under veckor.

Man måste titta i problemets ögonblick

Huvudregeln: den mest värdefulla informationen finns tillgänglig exakt när allt är långsamt. Första kommandot för MySQL:

sudo mysqladmin processlist
mysql -e 'SHOW FULL PROCESSLIST'

För 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 vad som körs just nu och hur länge. Svaret syns oftast direkt: en tung fråga håller de övriga, eller så väntar hundra likadana frågor på att ett lås ska släppas.

Anslutningar

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ätt intill max_connections får applikationen periodvis avslaget ”för många anslutningar”. Att höja gränsen är inte det första man gör: varje anslutning tar minne, och en höjd gräns på en server som redan saknar minne påskyndar bara vägen till swap. Först bör man förstå varför anslutningarna inte frigörs: vanligen beror det på en lång fråga eller på att applikationen saknar en pool.

Här är det också nyttigt att se saken från andra hållet: en kraftig ökning av antalet anslutningar kan bero på att botar massvis besöker en tung sida — sökningen, katalogfiltret, rapportgeneratorn — och inte på ökad popularitet.

Långsamma frågor

Utan en logg över långsamma frågor går det inte att komma vidare. MySQL:

SET GLOBAL slow_query_log = 'ON';
SET GLOBAL long_query_time = 1;

Efter ett dygn:

sudo mysqldumpslow -s t -t 10 /var/log/mysql/mysql-slow.log

PostgreSQL, i postgresql.conf:

log_min_duration_statement = 1000

Sedan tillägget pg_stat_statements, som direkt visar den sammanlagda tiden för varje fråga:

SELECT calls, round(total_exec_time) AS total_ms, query
FROM pg_stat_statements ORDER BY total_exec_time DESC LIMIT 10;

Observera: det viktiga är inte den längsta frågan utan den med störst sammanlagd tid. En fråga på 50 millisekunder som körs tusen gånger i minuten skadar mer än en tiosekundersrapport en gång per dygn.

Inställningen man oftast glömmer

För MySQL och MariaDB är det InnoDB-buffertpoolens storlek. Standardvärdet är 128 megabyte och har inte ändrats på decennier. På en server där databasen är flera gigabyte betyder det ständig läsning från disk i stället för från minne.

mysql -e "SHOW VARIABLES LIKE 'innodb_buffer_pool_size'"

Ett rimligt riktmärke för en dedikerad databasserver är hälften av arbetsminnet; för en server där webbservern och PHP arbetar bredvid är det en fjärdedel, med blicken på att swappen inte ska starta. Den enda inställningen ger oftast mer än en veckas frågeoptimering.

Två saker som omärkligt äter disken

MySQL binärloggar. De behövs för replikering och återställning till en tidpunkt, växer oavbrutet och raderas som standard ibland inte alls. Kontroll:

sudo du -sh /var/lib/mysql/*bin.*
mysql -e "SHOW VARIABLES LIKE 'binlog_expire_logs_seconds'"

Har du ingen replikering och använder inte återställning till en tidpunkt sätts lagringstiden till rimliga några dygn.

WAL-loggar i PostgreSQL vid en bortglömd replikeringsslot. Ett mindre känt och farligare fall: har en replikeringsslot skapats och konsumenten inte längre ansluter till den är PostgreSQL skyldigt att spara alla loggar sedan frånkopplingen — och kommer att spara dem tills disken tar slut.

sudo -u postgres psql -c "SELECT slot_name, active, restart_lsn FROM pg_replication_slots;"

En inaktiv slot som ingen behöver ska raderas. Det är en av få situationer där en databas garanterat kör disken i botten.

Storlek och tillväxt

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 nyttiga är inte så mycket det aktuella talet som förändringstakten. En databas som fördubblats på en månad utan motsvarande ökning av besök betyder vanligen att någon loggtabell skrivs och aldrig rensas — sessioner, jobbhistorik, ett tilläggs händelselogg.

Säkerhet på två rader

När du ändå öppnat databaskonsolen är två saker värda att kontrollera. Den första: är databasen nåbar utifrån (ss -tulpn | grep 3306) — utåt ska den nästan aldrig vetta. Den andra: konton med åtkomst från vilken adress som helst:

mysql -e "SELECT user, host FROM mysql.user"

Poster med host = '%' betyder ”anslutning tillåten varifrån som helst”. Är porten dessutom öppen står bara ett lösenord mellan databasen och internet.

Säkerhetskopior

Det sista värt att säga. Att en säkerhetskopiefil finns garanterar ingenting — bara en genomförd återställning gör det. En dump som aldrig rullats tillbaka kan lika gärna vara avkortad, tagen med ett låsfel eller innehålla fel databas. Det ska kontrolleras i förväg och på en separat maskin, inte den dag kopian behövs.

För den dagliga bevakningen räcker tre tal: antalet anslutningar, antalet långsamma frågor per dygn och databasens storlek. Alla tre ändras långsamt och förutsägbart, och varje kraftig förändring är värd en blick. Hur de ser ut på en sida visar demonstrationen nedan.