Yavaş Sorguları Tespit Etmek için Slow Query Log Kullanımı
Veritabanı performans sorunları çoğu zaman sessiz sedasız büyür. Uygulama yavaşlar, kullanıcılar şikayet etmeye başlar, sen de sunucuya bakarsın her şey normal görünür. İşte tam bu noktada slow query log devreye giriyor. MySQL ve MariaDB’nin bu yerleşik özelliği, belirlediğin eşik değerinin üzerinde süren sorguları kayıt altına alarak sana net bir yol haritası sunuyor. Bu yazıda slow query log’u nasıl aktif edeceğini, doğru şekilde yapılandıracağını ve üretilen logları nasıl analiz edeceğini gerçek dünya senaryolarıyla ele alacağız.
Slow Query Log Nedir ve Neden Önemlidir
Bir e-ticaret sitesi düşün. Sabah saatlerinde her şey yolunda giderken öğleden sonra sipariş sayfası 8-10 saniyede açılmaya başlıyor. Uygulama sunucusu gayet iyi çalışıyor, ağ trafiği normal, ama veritabanı sunucusu sessizce ızdırap çekiyor. Bu senaryoda slow query log olmasaydı hangi sorgunun sorun çıkardığını bulmak için saatler harcaman gerekirdi.
Slow query log, MySQL ve MariaDB’nin yerleşik bir tanı aracıdır. Belirttiğin long_query_time değerini aşan her sorguyu, o sorgunun ne kadar sürdüğünü, kaç satır incelediğini ve kaç satır döndürdüğünü kayıt altına alır. Bu bilgiler sayesinde indeks eksikliğini, kötü yazılmış JOIN’leri veya gereksiz tam tablo taramalarını kolayca tespit edebilirsin.
Özellikle şu durumlarda slow query log kritik önem taşır:
- Uygulama katmanında belirgin bir sorun olmadığı halde veritabanı yüklü görünüyorsa
- Belirli saatlerde ya da belirli kullanıcı işlemlerinde performans düşüşü yaşanıyorsa
- Yeni bir özellik veya güncelleme sonrasında sorgu süreleri artmışsa
- Production ortamına geçmeden önce sorgu optimizasyonu yapılacaksa
Slow Query Log’u Aktif Etmek
MySQL ve MariaDB Konfigürasyon Dosyasını Düzenlemek
En kalıcı yöntem my.cnf veya my.ini dosyasını düzenlemektir. Linux sistemlerde bu dosya genellikle /etc/mysql/my.cnf, /etc/my.cnf veya /etc/mysql/mysql.conf.d/mysqld.cnf yolunda bulunur.
sudo nano /etc/mysql/mysql.conf.d/mysqld.cnf
[mysqld] bölümüne şu satırları ekle:
[mysqld]
slow_query_log = 1
slow_query_log_file = /var/log/mysql/mysql-slow.log
long_query_time = 2
log_queries_not_using_indexes = 1
min_examined_row_limit = 100
log_slow_admin_statements = 1
Bu parametrelerin ne işe yaradığını tek tek açıklayalım:
- slow_query_log: Slow query log özelliğini açar veya kapar.
1aktif,0pasif demektir. - slow_query_log_file: Log dosyasının nereye yazılacağını belirtir. Belirtilmezse datadir içinde hostname-slow.log olarak oluşur.
- long_query_time: Saniye cinsinden eşik değeridir. Bu süreyi aşan sorgular loglanır. Ondalıklı değer de kabul eder, örneğin
0.5yarım saniyeyi aşan sorguları yakalar. - log_queries_not_using_indexes: İndeks kullanmayan sorguları,
long_query_timedeğerinden bağımsız olarak loglar. Performans sorunlarının büyük kısmı buradan kaynaklanır. - min_examined_row_limit: En az bu kadar satır inceleyen sorguları loglar. Çok küçük tablolardaki indekssiz sorgularda gürültüyü azaltmak için kullanılır.
- log_slow_admin_statements:
ALTER TABLE,OPTIMIZE TABLEgibi yönetimsel komutları da loglar.
Konfigürasyonu kaydettikten sonra servisi yeniden başlat:
sudo systemctl restart mysql
# veya MariaDB için
sudo systemctl restart mariadb
Sunucuyu Yeniden Başlatmadan Aktif Etmek
Production ortamında servisi yeniden başlatmak her zaman mümkün olmayabilir. MySQL 5.1 ve sonrası ile MariaDB’de slow query log’u dinamik olarak aktif edebilirsin:
mysql -u root -p -e "
SET GLOBAL slow_query_log = 'ON';
SET GLOBAL slow_query_log_file = '/var/log/mysql/mysql-slow.log';
SET GLOBAL long_query_time = 2;
SET GLOBAL log_queries_not_using_indexes = 1;
"
Bu değişiklikler sunucu yeniden başlatıldığında sıfırlanır. Kalıcı olmasını istiyorsan my.cnf‘e de eklemeyi unutma.
Mevcut ayarları kontrol etmek için:
mysql -u root -p -e "
SHOW VARIABLES LIKE 'slow_query%';
SHOW VARIABLES LIKE 'long_query_time';
SHOW VARIABLES LIKE 'log_queries_not_using_indexes';
"
Log Dosyasının İzinlerini Ayarlamak
Log dosyasını manuel oluşturuyorsan MySQL kullanıcısının yazma iznine sahip olduğundan emin ol:
sudo touch /var/log/mysql/mysql-slow.log
sudo chown mysql:mysql /var/log/mysql/mysql-slow.log
sudo chmod 640 /var/log/mysql/mysql-slow.log
Logrotate ile log dosyasının şişmesini engellemek için /etc/logrotate.d/mysql-slow dosyası oluşturabilirsin:
/var/log/mysql/mysql-slow.log {
daily
rotate 7
compress
delaycompress
missingok
notifempty
create 640 mysql mysql
postrotate
if [ -d /var/run/mysqld ] && [ -r /var/run/mysqld/mysqld.pid ]; then
mysqladmin flush-logs
fi
endscript
}
Slow Query Log Dosyasını Okumak
Log dosyası ham haliyle okunabilir ama büyük sistemlerde binlerce satır olabilir. Önce ham formata bakalım:
sudo tail -f /var/log/mysql/mysql-slow.log
Tipik bir slow query log girişi şöyle görünür:
# Time: 2024-01-15T14:23:45.123456Z
# User@Host: appuser[appuser] @ web01 [192.168.1.50]
# Query_time: 5.234123 Lock_time: 0.000145 Rows_sent: 1 Rows_examined: 2847392
SET timestamp=1705327425;
SELECT * FROM orders o
JOIN customers c ON o.customer_id = c.id
WHERE o.status = 'pending'
AND o.created_at > '2024-01-01'
ORDER BY o.total_amount DESC;
Bu girişi okumak için dikkat edilmesi gereken alanlar:
- Query_time: Sorgunun toplam çalışma süresi (saniye)
- Lock_time: Kilitleme için harcanan süre
- Rows_sent: İstemciye gönderilen satır sayısı
- Rows_examined: Sorgunun incelediği satır sayısı
Yukarıdaki örnekte sorgu 1 satır döndürmek için 2.8 milyon satır incelemiş. Bu açık bir indeks eksikliği işaretidir.
mysqldumpslow ile Log Analizi
MySQL’in kendi aracı olan mysqldumpslow benzer sorguları gruplayarak özetler:
# En yavaş 10 sorguyu göster
sudo mysqldumpslow -s t -t 10 /var/log/mysql/mysql-slow.log
# En çok çalıştırılan yavaş sorguları göster
sudo mysqldumpslow -s c -t 10 /var/log/mysql/mysql-slow.log
# En fazla satır inceleyen sorguları göster
sudo mysqldumpslow -s r -t 10 /var/log/mysql/mysql-slow.log
# Belirli bir veritabanı veya tablo içeren sorguları filtrele
sudo mysqldumpslow -s t -t 10 /var/log/mysql/mysql-slow.log | grep "orders"
-s parametresi sıralama kriterini belirler:
- t: Query_time’a göre sırala (toplam süre)
- at: Ortalama Query_time’a göre sırala
- c: Sorgunun kaç kez çalıştığına göre sırala
- r: İncelenen satır sayısına göre sırala
- l: Lock time’a göre sırala
pt-query-digest ile Gelişmiş Analiz
Percona Toolkit’in pt-query-digest aracı çok daha detaylı analiz sunar. Önce Percona Toolkit’i kur:
# Debian/Ubuntu
wget https://repo.percona.com/apt/percona-release_latest.generic_all.deb
sudo dpkg -i percona-release_latest.generic_all.deb
sudo apt-get update
sudo apt-get install percona-toolkit
# RHEL/CentOS
sudo yum install percona-toolkit
Temel kullanım:
# Tam rapor oluştur
pt-query-digest /var/log/mysql/mysql-slow.log
# Son 1 saatin loglarını analiz et
pt-query-digest --since 3600 /var/log/mysql/mysql-slow.log
# Sadece belirli bir veritabanının sorgularını analiz et
pt-query-digest --filter '$event->{db} && $event->{db} eq "myapp"' /var/log/mysql/mysql-slow.log
# Raporu dosyaya yaz
pt-query-digest /var/log/mysql/mysql-slow.log > /tmp/slow_query_report.txt
pt-query-digest çıktısında her sorgu için şunları göreceksin:
- Kaç kez çalıştırıldığı
- Toplam ve ortalama çalışma süresi
- Minimum ve maksimum süreler
- Yüzde kaçını oluşturduğu
- Örnek sorgu metni
Gerçek Dünya Senaryosu: E-ticaret Sitesi Performans Sorunu
Diyelim ki bir e-ticaret sitesinin veritabanı yöneticisisin ve her gün öğleden sonra yavaşlama şikayeti alıyorsun. Slow query log’u aktif edip birkaç saat bekledikten sonra pt-query-digest ile analiz yapıyorsun. Rapor şöyle bir sorguyu öne çıkardı:
SELECT p.*, c.name as category_name, b.name as brand_name
FROM products p
LEFT JOIN categories c ON p.category_id = c.id
LEFT JOIN brands b ON p.brand_id = b.id
WHERE p.is_active = 1
AND p.price BETWEEN 100 AND 500
AND p.stock > 0
ORDER BY p.created_at DESC
LIMIT 20;
Bu sorgu 3.2 saniye sürüyor ve günde 15.000 kez çalışıyor. EXPLAIN ile bakalım:
mysql -u root -p myapp -e "
EXPLAIN SELECT p.*, c.name as category_name, b.name as brand_name
FROM products p
LEFT JOIN categories c ON p.category_id = c.id
LEFT JOIN brands b ON p.brand_id = b.id
WHERE p.is_active = 1
AND p.price BETWEEN 100 AND 500
AND p.stock > 0
ORDER BY p.created_at DESC
LIMIT 20;
"
Çıktıda type: ALL görüyorsun, yani tam tablo taraması var. 500.000 ürünlük bir tabloda bu felaket demek. Çözüm şu indeksi eklemek:
ALTER TABLE products
ADD INDEX idx_active_price_stock_created (is_active, price, stock, created_at);
Bu indeks eklendikten sonra sorgu süresi 3.2 saniyeden 0.04 saniyeye düştü. Günlük 15.000 çalışma düşünüldüğünde bu ciddi bir kazanım.
Performance Schema ile Slow Query Log’u Tamamlamak
MySQL 5.6 ve MariaDB 10.0 sonrasında Performance Schema, slow query log’a güzel bir alternatif ve tamamlayıcı sunar. Yeniden başlatma gerektirmez:
mysql -u root -p -e "
-- En yavaş 10 sorguyu getir
SELECT
DIGEST_TEXT,
COUNT_STAR as execution_count,
ROUND(AVG_TIMER_WAIT/1000000000, 2) as avg_seconds,
ROUND(MAX_TIMER_WAIT/1000000000, 2) as max_seconds,
ROUND(SUM_TIMER_WAIT/1000000000, 2) as total_seconds,
SUM_ROWS_EXAMINED as total_rows_examined,
SUM_ROWS_SENT as total_rows_sent
FROM performance_schema.events_statements_summary_by_digest
ORDER BY SUM_TIMER_WAIT DESC
LIMIT 10;
"
Performance Schema ve slow query log’u birlikte kullanmak en iyi yaklaşımdır. Slow query log anlık sorunları yakalarken Performance Schema kümülatif istatistikler sunar.
Eşik Değerini Doğru Ayarlamak
long_query_time değeri kuruma göre farklılık gösterir. Çok yüksek tutarsan gerçek sorunları kaçırırsın, çok düşük tutarsan log dosyası şişer ve analiz zorlaşır.
Genel yaklaşım:
- Başlangıç aşaması: 2 saniye ile başla, sorunlu sorguları tespit et ve optimize et.
- Olgun sistem: 1 saniyeye indir, ince ayar yap.
- Yüksek performans gerektiren sistem: 0.5 veya 0.1 saniyeye indir.
- Geliştirme ortamı: 0 değeri tüm sorguları loglar, geliştirme sırasında faydalı olabilir.
Mevcut sisteminizde ortalama sorgu süresini görmek için:
mysql -u root -p -e "
SELECT
ROUND(AVG(TIMER_WAIT)/1000000000, 4) as avg_query_seconds,
ROUND(MAX(TIMER_WAIT)/1000000000, 4) as max_query_seconds,
COUNT(*) as total_queries
FROM performance_schema.events_statements_history_long;
"
Bu çıktıya bakarak long_query_time değerini gerçekçi biçimde belirleyebilirsin.
Slow Query Log Rotasyonu ve Yönetimi
Aktif bir sistemde slow query log hızla büyüyebilir. Günlük log boyutunu izlemek için:
# Log boyutunu kontrol et
ls -lh /var/log/mysql/mysql-slow.log
# Log içindeki toplam sorgu sayısını say
grep -c "^# Query_time" /var/log/mysql/mysql-slow.log
# Son 100 yavaş sorguyu görüntüle
tail -n 1000 /var/log/mysql/mysql-slow.log | grep -A 5 "Query_time"
Log dosyasını elle döndürmek için:
# Mevcut log dosyasını yedekle
sudo mv /var/log/mysql/mysql-slow.log /var/log/mysql/mysql-slow-$(date +%Y%m%d).log
# MySQL'e yeni log dosyasına geçmesini söyle
sudo mysqladmin flush-logs
# Eski log dosyasını sıkıştır
sudo gzip /var/log/mysql/mysql-slow-$(date +%Y%m%d).log
Slow Query Log’dan Öğrenilen Dersleri Uygulamak
Sorguyu tespit etmek ilk adım, asıl iş optimizasyon kısmındadır. Slow query log’da sık karşılaşılan sorun tipleri ve çözümleri:
Tam tablo taraması (type: ALL): WHERE koşulundaki kolonlara indeks ekle.
Gereksiz yüksek Rows_examined / Rows_sent oranı: Sorgu binlerce satır inceliyor ama az satır döndürüyorsa indeks eksikliği ya da kötü sorgu yapısı var demektir.
Yüksek Lock_time: Tablo kilitleme sorunu var. SHOW PROCESSLIST ve SHOW ENGINE INNODB STATUS komutlarıyla derinlemesine incele.
SELECT * kullanımı: Sadece ihtiyacın olan kolonları seç. Gereksiz veri transferini azaltır, covering index kullanımını mümkün kılar.
Bir sorguyu optimize ettikten sonra slow query log’da takibini yapmak için şu bash betiğini kullanabilirsin:
#!/bin/bash
# slow_query_monitor.sh
# Slow query logundaki son N girişi analiz eder
LOG_FILE="/var/log/mysql/mysql-slow.log"
THRESHOLD=5 # Kaç kez görünürse uyar
echo "=== Slow Query Raporu - $(date) ==="
echo ""
echo "Son 1 saatteki yavaş sorgu sayısı:"
awk '/^# Time:/{time=$3} /^# Query_time:/{print time, $3}' "$LOG_FILE" |
awk -v limit=$(date -d '1 hour ago' +%Y-%m-%dT%H:%M:%S) '$1 >= limit {count++} END {print count " sorgu"}'
echo ""
echo "En çok tekrar eden yavaş sorgular:"
mysqldumpslow -s c -t 5 "$LOG_FILE" 2>/dev/null
Sonuç
Slow query log, veritabanı yönetiminin vazgeçilmez araçlarından biridir. Pasif bir izleme mekanizması gibi görünse de doğru yapılandırıldığında sana saatlerce sürecek sorun giderme süreçlerini dakikalara indirgeyecek veriler sunar.
Önemli noktalara bakalım. long_query_time değerini sisteme uygun şekilde ayarla, çok yüksek bırakma. log_queries_not_using_indexes parametresini mutlaka aktif et çünkü indeks kullanmayan sorgular bazen eşik değerine ulaşmadan da ciddi sorunlar çıkarabilir. mysqldumpslow hızlı bir bakış için yeterlidir ama pt-query-digest çok daha kapsamlı analiz sunar.
Slow query log’u sürekli açık tutmak en iyi pratiktir. Log boyutunu logrotate ile kontrol altında tutarak hem sürekli izleme yapabilir hem de disk dolmasını engelleyebilirsin. Performance Schema ile birlikte kullandığında gerçek anlamda kapsamlı bir veritabanı performans izleme altyapısına sahip olursun.
Son olarak şunu unutma: Yavaş sorguyu bulmak başarının yarısıdır. Diğer yarısı EXPLAIN çıktısını okuyup doğru indeksi eklemek veya sorguyu yeniden yazmaktır. Slow query log bu yolculuğun haritasını çizer, yürümek sana kalır.
