MySQLTuner - optymalizacja MySQL i MariaDB na VPS
Opublikowano: 10 kwietnia 2026 · Kategoria: VPS / Bazy danych
Domyślna konfiguracja MySQL jest celowo ostrożna - zaprojektowana do działania na serwerach z minimalną pamięcią i nie wykorzystuje zasobów, które masz na VPS. Wynikiem są wolne zapytania, zbyt mało połączeń i nadmierne odczyty z dysku zamiast z RAM. MySQLTuner to narzędzie, które analizuje Twój serwer i mówi wprost co zmienić i dlaczego. Artykuł pokazuje jak interpretować raport i implementować rekomendacje.
Pobranie i uruchomienie MySQLTuner
# Pobierz najnowszą wersję MySQLTuner wget http://mysqltuner.pl/ -O mysqltuner.pl wget https://raw.githubusercontent.com/major/MySQLTuner-perl/master/basic_passwords.txt -O basic_passwords.txt wget https://raw.githubusercontent.com/major/MySQLTuner-perl/master/vulnerabilities.csv -O vulnerabilities.csv # Uruchomienie (jako root lub uzytkownik z pelnym dostepem) perl mysqltuner.pl --user root --pass "TwojHaslo" # Lub z pliku .my.cnf (bezpieczniejsze) # [client] # user=root # password=TwojHaslo # plik: ~/.my.cnf (chmod 600) perl mysqltuner.pl # Opcje zaawansowane perl mysqltuner.pl \ --buffers \ # szczegoly buforow --idxstats \ # statystyki indeksow --dbstat \ # statystyki baz --outputfile /tmp/mysqltuner-report.txt # zapis do pliku
Instalacja MySQLTuner z repozytorium pakietów lub z GitHuba
MySQLTuner to pojedynczy skrypt Perl, więc nie trzeba go instalować: wystarczy pobrać plik
mysqltuner.pl jak wyżej. Jeśli wolisz mieć go jako pakiet systemowy (aktualizacje
razem z resztą systemu), jest dostępny w repozytoriach Debiana i Ubuntu oraz w EPEL dla AlmaLinux
i Rocky Linux. Wersja z repozytorium bywa starsza niż ta z GitHuba, a starsze wersje gorzej rozpoznają
nowe wydania MySQL i MariaDB, dlatego przy nowym serwerze bazy lepiej uruchomić najnowszy skrypt.
# Debian / Ubuntu sudo apt update && sudo apt install mysqltuner sudo mysqltuner # AlmaLinux / Rocky Linux (repozytorium EPEL) sudo dnf install epel-release sudo dnf install mysqltuner # Najnowsza wersja prosto z GitHuba (MySQLTuner download) git clone --depth 1 https://github.com/major/MySQLTuner-perl.git cd MySQLTuner-perl perl mysqltuner.pl --user root --pass "TwojHaslo" # Wymagania: interpreter Perl (jest w praktycznie każdej dystrybucji Linuksa) perl -v
MySQLTuner online, w Dockerze i na Windowsie
MySQLTuner online. Oficjalny projekt nie ma wersji online. Skrypt musi połączyć
się z serwerem bazy i odczytać jego zmienne oraz liczniki, więc uruchamiasz go na serwerze albo
na maszynie, która ma dostęp do bazy przez sieć. Serwisy "online", do których wkleja
się wynik SHOW GLOBAL STATUS i SHOW GLOBAL VARIABLES, dostają
pełny obraz konfiguracji Twojej bazy, dlatego na produkcji bezpieczniej użyć lokalnego
skryptu.
Połączenie zdalne. Gdy uruchamiasz MySQLTuner na innej maszynie niż baza, skrypt
nie widzi pamięci RAM ani swapu serwera bazy. Trzeba je podać ręcznie przez
--forcemem i --forceswap (w MB), inaczej rekomendacje dotyczące buforów
będą policzone dla złej maszyny.
MySQLTuner w Dockerze. Najprostsza droga to uruchomienie skryptu na hoście i
połączenie z kontenerem przez opublikowany port. Jako --forcemem podaj limit pamięci
kontenera (a nie RAM całego hosta), bo to on wyznacza, ile może zająć bufor InnoDB. Użyj adresu
127.0.0.1, a nie localhost: klient MySQL przy
localhost próbuje połączyć się przez gniazdo uniksowe, którego na hoście nie ma.
# Baza na innym serwerze (VPS 4 GB RAM, 1 GB swap)
perl mysqltuner.pl --host 10.0.0.5 --port 3306 \
--user tuner --pass "TwojHaslo" \
--forcemem 4096 --forceswap 1024
# Baza w kontenerze z portem 3306 opublikowanym na hoście
docker inspect --format '{{.HostConfig.Memory}}' nazwa_kontenera # limit w bajtach, 0 = brak limitu
perl mysqltuner.pl --host 127.0.0.1 --port 3306 \
--user root --pass "TwojHaslo" --forcemem 2048 MySQLTuner na Windowsie. Skrypt jest pisany pod Linuksa i Uniksa (część danych
o systemie czyta z narzędzi systemowych), więc na Windowsie najwygodniej uruchomić go w WSL albo
z dowolnej maszyny z Linuksem, łącząc się z bazą zdalnie z parametrem
--forcemem.
Interpretacja raportu - sekcje wyjścia
MySQLTuner dzieli raport na sekcje. Kluczowe linie to te z [!!] (ostrzeżenie) i [OK] (w porządku). Na końcu sekcja
Recommendations zbiera konkretne zmiany do zastosowania.
-------- General Statistics -------- [--] Skipped version check for MySQLTuner script [OK] Currently running supported MySQL version 8.0.36-MySQL -------- Storage Engine Statistics -------- [OK] InnoDB is enabled [!!] InnoDB buffer pool / data size: 128.0M/4.5G # KLUCZOWE! [!!] InnoDB buffer pool instances: 1 (recommended: 4+) [OK] InnoDB Read buffer efficiency: 94.28% # powinno byc >99% -------- Performance Metrics -------- [!!] Slow queries: 2% (1K/52K) # powyzej 1% = problem [OK] Highest connection usage: 42% of max_connections [!!] Threads created per second: 12 (should be < 5) # thread cache za maly [OK] Table cache hit rate: 91% -------- Recommendations -------- General recommendations: Add skip-name-resolve to my.cnf innodb_buffer_pool_size (>= 3G) innodb_buffer_pool_instances = 4 thread_cache_size = 50 slow_query_log = 1
Najczęstsze ostrzeżenia [!!] i co z nimi zrobić
| Ostrzeżenie w raporcie | Co oznacza | Co zrobić |
|---|---|---|
| Maximum possible memory usage | Przy komplecie połączeń bufory per połączenie mogą przekroczyć RAM | Obniż max_connections do realnego szczytu albo zmniejsz bufory per połączenie
(sort_buffer_size, join_buffer_size) |
| Joins performed without indexes | Zapytania łączą tabele bez indeksu na kolumnie złączenia | Znajdź je w slow query log i dodaj indeksy; podnoszenie
join_buffer_size tylko maskuje problem |
| Temporary tables created on disk | Tabele tymczasowe nie mieszczą się w pamięci | Podnieś razem tmp_table_size i max_heap_table_size;
kolumny TEXT/BLOB w wyniku i tak trafią na dysk, wtedy pomaga zmiana zapytania |
| Key buffer used / MyISAM | Część tabel nadal używa silnika MyISAM | Zamień tabele na InnoDB (ALTER TABLE ... ENGINE=InnoDB, po kopii
zapasowej), wtedy key_buffer_size można zmniejszyć |
| Aborted connections | Klienci zrywają połączenia albo podają złe hasło | Sprawdź log błędów, timeouty aplikacji i próby logowania z zewnątrz; baza nie powinna nasłuchiwać publicznie |
| Name resolution is active | Każde połączenie wymaga odpytania DNS | Dodaj skip-name-resolve i nadawaj uprawnienia po adresach IP, nie nazwach
hostów |
Kluczowe parametry i ich optymalizacja
| Parametr | Domyślna wartość | Rekomendacja (VPS 8 GB) | Efekt |
|---|---|---|---|
innodb_buffer_pool_size | 128 MB | 4-6 GB (70% RAM) | Więcej danych w RAM, mniej I/O dysku |
innodb_buffer_pool_instances | 1 | 4-8 (1 per GB pool) | Mniejszy contention na mutex pool |
max_connections | 151 | 300-500 | Więcej równoczesnych połączeń |
thread_cache_size | 9 | 50-100 | Mniej tworzenia nowych wątków (CPU) |
query_cache_type | 0 (OFF) w MySQL 5.7 i MariaDB 10.1.7+ | 0 (OFF, read-heavy: 1) | Brak mutex contention przy write-heavy |
innodb_log_file_size | 48 MB | 256-512 MB | Mniej checkpoint flushes, lepszy write perf |
tmp_table_size / max_heap_table_size | 16 MB | 64-256 MB | Mniej temporary tables na dysku |
# /etc/mysql/mysql.conf.d/mysqld.cnf - przykład dla VPS 8 GB RAM # [mysqld] # --- InnoDB --- # innodb_buffer_pool_size = 4G # innodb_buffer_pool_instances = 4 # innodb_log_file_size = 256M # innodb_flush_log_at_trx_commit = 2 # 1=bezpieczne, 2=szybsze (ryzyko utraty 1s transakcji) # innodb_flush_method = O_DIRECT # # --- Polaczenia --- # max_connections = 300 # thread_cache_size = 50 # wait_timeout = 300 # interactive_timeout = 300 # # --- Tymczasowe tabele --- # tmp_table_size = 128M # max_heap_table_size = 128M # # --- Query cache (MySQL 5.7 - dla MySQL 8.0 usun) --- # query_cache_type = 0 # query_cache_size = 0 # # --- Slow query log --- # slow_query_log = 1 # slow_query_log_file = /var/log/mysql/slow.log # long_query_time = 1 # log_queries_not_using_indexes = 1 # # --- Siec --- # skip-name-resolve # bind-address = 127.0.0.1 # Zastosuj bez restartu (czesciowo) mysql -u root -p -e "SET GLOBAL innodb_buffer_pool_size = 4*1024*1024*1024;" mysql -u root -p -e "SET GLOBAL max_connections = 300;" # Pelny restart (dla innodb_log_file_size wymagany) sudo systemctl restart mysql
Nowsze wersje: MySQL 8.0.30+ i MariaDB 10.5+
Część parametrów z przykładu wyżej zmieniła się w nowszych wydaniach i stara konfiguracja przeniesiona 1:1 może dać ostrzeżenia w logu albo zostać po cichu zignorowana:
- MySQL 8.0.30 i nowsze: rozmiar dziennika redo ustawia
innodb_redo_log_capacity, ainnodb_log_file_sizeiinnodb_log_files_in_groupsą przestarzałe. Na nowej instalacji ustawiaj jeden parametr, np.innodb_redo_log_capacity = 512M. - MySQL 8.0: query cache nie istnieje, więc wpisy
query_cache_*w my.cnf powodują błąd startu serwera. Usuń je przed aktualizacją z 5.7. - MariaDB 10.5 i nowsze: InnoDB używa jednej instancji puli buforów, a
innodb_buffer_pool_instancesjest przestarzały (w 10.6 usunięty), więc rekomendacja "4+ instancje" ze starszych wersji MySQLTuner nie ma tu zastosowania.
SHOW VARIABLES vs SHOW STATUS - diagnostyka
# SHOW VARIABLES - aktualna konfiguracja SHOW GLOBAL VARIABLES LIKE 'innodb_buffer_pool_size'; SHOW GLOBAL VARIABLES LIKE 'max_connections'; SHOW GLOBAL VARIABLES LIKE '%cache%'; # SHOW STATUS - statystyki runtime (od ostatniego restartu) SHOW GLOBAL STATUS LIKE 'Innodb_buffer_pool_read%'; -- Innodb_buffer_pool_read_requests = total reads tried from buffer -- Innodb_buffer_pool_reads = reads that needed disk I/O -- Ratio hit: 1 - (Reads / Read_requests) powinno byc >99% SHOW GLOBAL STATUS LIKE 'Threads%'; -- Threads_created = ile watkow bylo tworzonych (powinno byc niskie) -- Threads_running = aktualnie aktywne watki SHOW GLOBAL STATUS LIKE 'Slow_queries'; SHOW GLOBAL STATUS LIKE 'Questions'; -- total queries SHOW GLOBAL STATUS LIKE 'Qcache%'; -- Qcache_hits / (Qcache_hits + Qcache_inserts) = skutecznosc query cache -- Uzyteczne widoki diagnostyczne (MySQL 8.0+ / MariaDB) SELECT * FROM sys.statement_analysis LIMIT 10; -- top slow queries SELECT * FROM sys.user_summary; -- aktywnosc per user SELECT * FROM information_schema.INNODB_BUFFER_POOL_STATS;
Weryfikacja po zmianach
# Zresetuj statystyki statusu
FLUSH STATUS;
# Poczekaj 24h, potem sprawdz skutecznosc bufora
SELECT
(1 - (
variable_value / (
SELECT variable_value
FROM information_schema.GLOBAL_STATUS
WHERE variable_name = 'Innodb_buffer_pool_read_requests'
)
)) * 100 AS buffer_hit_ratio_pct
FROM information_schema.GLOBAL_STATUS
WHERE variable_name = 'Innodb_buffer_pool_reads';
-- Cel: > 99%
# Uruchom MySQLTuner po 24-48h i porownaj z poprzednim raportem
perl mysqltuner.pl >> /tmp/after-tuning.txt
# Sprawdz slow query log
mysqldumpslow -s t -t 10 /var/log/mysql/slow.log
# -s t = sortuj po query_time
# -t 10 = top 10 Jak bezpiecznie stosować rekomendacje MySQLTuner
MySQLTuner liczy rekomendacje z liczników od ostatniego restartu i nie zna Twojej aplikacji. Dlatego jego wynik to lista hipotez do sprawdzenia, a nie gotowy my.cnf. Kilka zasad, które oszczędzają nocnych restartów:
- Uruchamiaj po co najmniej 24 h normalnego ruchu. Raport zaraz po restarcie bazy opiera się na kilku minutach statystyk i potrafi zalecić odwrotne zmiany niż raport z pełnej doby.
- Zmieniaj jeden parametr naraz. Po zmianie odczekaj kolejną dobę i porównaj
raporty (
--outputfilez datą w nazwie). Przy kilku zmianach jednocześnie nie wiadomo, która pomogła, a która zaszkodziła. - Pilnuj sumy pamięci. Bufory per połączenie mnożą się przez liczbę połączeń.
Podniesienie
max_connectionsisort_buffer_sizenaraz może przy szczycie ruchu skończyć się zabiciem procesu bazy przez OOM killer. - Zrób kopię my.cnf przed edycją i sprawdź składnię przed restartem (
mysqld --validate-configw MySQL 8.0.16+), żeby literówka nie zatrzymała bazy. - Na hostingu współdzielonym nie dostosujesz serwera. Konfigurację MySQL ustawia tam dostawca. MySQLTuner ma sens na własnym VPS lub serwerze dedykowanym; na współdzielonym zostaje optymalizacja zapytań i indeksów.
Gotowe ustawienia my.cnf dla VPS 2 GB i 4 GB RAM (innodb_buffer_pool_size, max_connections, tmp_table_size) znajdziesz w artykule MySQL - optymalizacja i konfiguracja my.cnf.