İçindekiler
top komutunu açtığınızda en üstte mysqld ya da mariadbd görüyorsanız ve işlemci kullanımı sürekli yüksekse, veritabanı bir şeylerle boğuşuyor demektir. Çoğu zaman sebep, binlerce kez çalışan ve her seferinde bütün tabloyu tarayan birkaç kötü sorgudur.
Bu sorguları tahmin etmeye çalışmayın. Veritabanı onları sizin için kaydedebilir.
1. Yavaş sorgu günlüğünü açın
Sunucuyu yeniden başlatmadan, çalışırken açabilirsiniz:
SET GLOBAL slow_query_log = 1;
SET GLOBAL long_query_time = 1;
SET GLOBAL log_queries_not_using_indexes = 0;
SHOW VARIABLES LIKE 'slow_query_log_file';
long_query_time = 1 ayarı, bir saniyeden uzun süren sorguları kaydeder. Daha ince bir analiz için 0.5 ya da 0.2 gibi değerler de kullanılabilir. Kalıcı olması için aynı ayarları yapılandırma dosyasına da yazın:
# /etc/my.cnf.d/slow.cnf
[mysqld]
slow_query_log = 1
slow_query_log_file = /var/log/mariadb/slow.log
long_query_time = 1
Bir süre, tercihen yoğun saatleri kapsayacak kadar bekleyin.
2. Günlüğü özetleyin
Ham günlük binlerce satır olabilir. mysqldumpslow aracı benzer sorguları gruplar ve en çok zaman harcayanları sıralar:
mysqldumpslow -s t -t 10 /var/log/mariadb/slow.log
-s t toplam süreye göre sıralar, -t 10 ilk on sorguyu gösterir. Daha ayrıntılı bir analiz için Percona Toolkit'teki pt-query-digest aracı da çok kullanışlıdır.
Çıktıda şöyle bir satır görebilirsiniz:
Count: 18420 Time=2.31s (42550s) Rows_examined=184000
SELECT * FROM siparisler WHERE musteri_eposta = 'S' ORDER BY tarih DESC LIMIT N
Bu sorgu 18.420 kez çalışmış, ortalama 2,3 saniye sürmüş ve her seferinde 184.000 satır taramış. Toplam maliyeti 11 saati aşıyor. Suçluyu bulduk.
3. EXPLAIN ile sorguyu okuyun
EXPLAIN SELECT * FROM siparisler
WHERE musteri_eposta = 'ali@ornek.com'
ORDER BY tarih DESC LIMIT 10;
+------+-------------+------------+------+---------------+------+---------+------+--------+-----------------------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+------+-------------+------------+------+---------------+------+---------+------+--------+-----------------------------+
| 1 | SIMPLE | siparisler | ALL | NULL | NULL | NULL | NULL | 184000 | Using where; Using filesort |
+------+-------------+------------+------+---------------+------+---------+------+--------+-----------------------------+
Bakılacak sütunlar:
- type:
ALL, bütün tablonun tarandığı anlamına gelir; en kötü durum budur.ref,rangeya daconstgörmek istersiniz. - key: Kullanılan indeks.
NULLise hiçbir indeks kullanılmıyor. - rows: Taranacağı tahmin edilen satır sayısı.
- Extra:
Using filesortveUsing temporary, sıralama ya da gruplama için ek iş yapıldığını gösterir.
4. Doğru indeksi ekleyin
Sorgu musteri_eposta ile filtreliyor ve tarih ile sıralıyor. Her ikisini kapsayan bir birleşik indeks:
ALTER TABLE siparisler ADD INDEX idx_eposta_tarih (musteri_eposta, tarih);
Sütun sırası önemlidir: Önce eşitlikle filtrelenen sütun (musteri_eposta), sonra sıralanan ya da aralıkla filtrelenen sütun (tarih) gelir. Şimdi EXPLAIN tekrar çalıştırıldığında type: ref, key: idx_eposta_tarih ve rows: 12 gibi bir sonuç görürsünüz. Using filesort ifadesi de kaybolur. Sorgu 2,3 saniyeden birkaç milisaniyeye iner.
Büyük tablolarda ALTER TABLE uzun sürebilir. MariaDB ve MySQL'in güncel sürümleri çoğu indeks eklemeyi tabloyu kilitlemeden (online) yapar, yine de yoğun saatlerin dışında çalıştırmak daha güvenlidir.
Sık görülen kötü kalıplar
İndeksli sütuna fonksiyon uygulamak:
-- İndeks kullanılamaz
WHERE DATE(tarih) = '2026-09-13'
-- İndeks kullanılır
WHERE tarih >= '2026-09-13' AND tarih < '2026-09-14'
Başında joker karakter olan LIKE:
WHERE ad LIKE '%ahmet%' -- tam tarama
WHERE ad LIKE 'ahmet%' -- indeks kullanılabilir
Metin içinde arama gerekiyorsa FULLTEXT indeks ya da ayrı bir arama motoru düşünün.
Gereksiz SELECT *: Yalnızca ihtiyacınız olan sütunları seçmek, özellikle büyük metin sütunları olan tablolarda ciddi fark yaratır.
Döngü içinde sorgu (N+1): Yüz siparişi listeleyip her biri için ayrı bir müşteri sorgusu çalıştırmak, 101 sorgu demektir. Tek bir JOIN ya da WHERE id IN (...) sorgusu aynı işi görür.
İndeks her şeyin ilacı değil
Her indeks, ekleme ve güncelleme işlemlerini biraz yavaşlatır ve disk alanı tüketir. Her sütuna indeks eklemek yerine, yavaş sorgu günlüğünün gösterdiği gerçek sorgulara göre indeks ekleyin. Kullanılmayan indeksleri bulmak için MySQL'de sys.schema_unused_indexes görünümü işinize yarar.
İşiniz bitince long_query_time değerini makul bir seviyede bırakın ve günlüğü logrotate ile döndürün. Açık kalan yavaş sorgu günlüğü, sonraki sorunlar için de en iyi erken uyarı sisteminizdir.
Yorumlar
Henüz yorum yok. İlk yorumu siz yazın.