Ova uputa pokriva praktičnu optimizaciju MySQL / MariaDB baze na produkcijskim webovima — konfiguraciju, indekse, upite i monitoring. Primjenjivo na cPanel / DirectAdmin okruženja, VPS i dedicirane servere s Craft CMS, Laravel ili WordPress aplikacijama.
1. Provjeri trenutno stanje prije bilo kakve promjene
Ništa ne mijenjaj prije nego snimiš baseline. Bez baselinea ne znaš je li promjena pomogla ili odmogla.
-- osnovni status
SHOW GLOBAL STATUS LIKE 'Threads_connected';
SHOW GLOBAL STATUS LIKE 'Slow_queries';
SHOW GLOBAL STATUS LIKE 'Questions';
SHOW GLOBAL STATUS LIKE 'Uptime';
-- buffer pool hit ratio (InnoDB)
SHOW GLOBAL STATUS LIKE 'Innodb_buffer_pool_reads';
SHOW GLOBAL STATUS LIKE 'Innodb_buffer_pool_read_requests';
Buffer pool hit ratio = 1 - (Innodb_buffer_pool_reads / Innodb_buffer_pool_read_requests). Cilj je >99%. Ispod 95% znači premali innodb_buffer_pool_size.
2. Ključne postavke u my.cnf
Lokacija: /etc/my.cnf ili /etc/mysql/mariadb.conf.d/50-server.cnf. Nakon izmjene restartaj servis: systemctl restart mariadb.
[mysqld]
# InnoDB — glavna pomoć za performanse
innodb_buffer_pool_size = 4G # 50-70% RAM-a na dedicated MySQL
innodb_log_file_size = 512M
innodb_flush_log_at_trx_commit = 2 # brže, uz mali rizik pri crashu
innodb_flush_method = O_DIRECT
innodb_file_per_table = 1
# Konekcije
max_connections = 200
thread_cache_size = 32
table_open_cache = 4000
# Query cache (OBAVEZNO isključiti u MySQL 5.7+, MariaDB 10.3+)
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 = 0 # 1 samo privremeno, generira puno logova
3. Pronađi spore upite
Nakon što slow log radi 24-48h, analiziraj:
mysqldumpslow -s t -t 20 /var/log/mysql/slow.log
Ili čitljivije s alatom pt-query-digest (Percona Toolkit):
pt-query-digest /var/log/mysql/slow.log | less
Prvih 5 upita po ukupnom vremenu najčešće čini 80% problema.
4. Analiziraj upit pomoću EXPLAIN
EXPLAIN SELECT * FROM entries WHERE sectionId = 12 AND postDate < NOW();
Obrati pozornost na:
type:ALLznači full table scan — loše. Cilj jeref,range,const,eq_ref.rows: procijenjeni broj redaka koje MySQL mora pročitati.Extra:Using filesortiUsing temporarysu skupi — često se rješavaju indeksom koji poklapaORDER BY/GROUP BY.
5. Indeksi — gdje i kakvi
- Indeksiraj kolone u
WHERE,JOIN,ORDER BY,GROUP BY. - Composite index: redoslijed kolona mora odgovarati upitu.
(sectionId, postDate)radi zaWHERE sectionId = ? AND postDate < ?, ali ne radi za samupostDate. - Izbjegavaj indekse na kolonama s niskom kardinalnošću (npr. boolean kolone same za sebe).
- Previše indeksa usporava
INSERT/UPDATE. Prati unused index-e:
SELECT * FROM sys.schema_unused_indexes; -- MySQL 5.7+
6. Tipični antipatterni
- SELECT * u aplikacijskom kodu — uvijek vadi samo potrebne kolone.
- N+1 upiti — u ORM-u (Craft, Eloquent) koristi eager loading (
with,.eagerly()). - LIKE '%xxx' — leading wildcard onemogućuje indeks. Razmotri FULLTEXT ili ElasticSearch / MeiliSearch.
- ORDER BY RAND() na velikim tablicama — skupo. Zamijeni s random offsetom ili kešom.
- Implicitna konverzija tipova —
WHERE user_id = '123'kada je kolona INT. Može onemogućiti indeks.
7. Konfiguracija za CMS-ove
Craft CMS
- Aktiviraj template cache (
{% cache %}) za dijelove koji se ne mijenjaju često. - Postavi
CRAFT_RUN_QUEUE_AUTOMATICALLY=falseu.envi pokreni queue kroz cron / supervisor da HTTP zahtjevi ne čekaju. - Garbage collection tablica (
sessions,queue,revisions) — krozphp craft gc.
Laravel
- Database-level query caching kroz
->remember()ili Redis (tagged cache). config:cache,route:cache,view:cacheu produkciji.- Octane ili FrankenPHP kad TTFB postane usko grlo.
WordPress / WooCommerce
- Object cache (Redis ili Memcached) — najveća razlika.
- Izbaci neiskorištene pluginove. Svaki dodaje upite u
wp_options. - Autoload cleanup:
SELECT option_name, LENGTH(option_value) FROM wp_options WHERE autoload='yes' ORDER BY 2 DESC LIMIT 20;
8. Održavanje
# analiziraj i optimiziraj tablice (jednom mjesečno)
mysqlcheck --all-databases --analyze
mysqlcheck --all-databases --optimize
# provjeri veličine tablica
SELECT table_name,
ROUND(data_length/1024/1024, 1) AS data_mb,
ROUND(index_length/1024/1024, 1) AS idx_mb
FROM information_schema.tables
WHERE table_schema = DATABASE()
ORDER BY data_length DESC LIMIT 20;
9. Kada je došao trenutak za upgrade infrastrukture
Ako nakon svega iznad i dalje imaš probleme, usko grlo je negdje drugdje:
- CPU stalno >70% — nedostaje jezgri, razmotri vertikalni upgrade ili razdvajanje web/db.
- Buffer pool hit ratio <95% uz maksimalan RAM — vrijeme za veći server ili sharding.
- Disk I/O wait >10% — NVMe upgrade ili odvajanje logova od data foldera.
10. Sigurnosne napomene
- Nikad ne testiraj nove konfiguracije direktno na produkciji. Staging server s istim podacima je must.
- Prije svake
my.cnfpromjene — kopija trenutne datoteke. - Kod dramatičnih promjena
innodb_log_file_size— MySQL treba uredno zaustaviti prije restarta jer stare log datoteke moraju biti čiste.
Kad zapne ili nije jasno što mijenjati, javite se podršci — imamo access log, slow query log i monitoring podatke za svaki server kojeg hostamo i možemo konkretno pokazati gdje je problem.