Veritabanı Yönetimi

    Slow Query Log Yapılandırma ve Analizi

    MySQL yavaş sorgu günlüğünü doğru eşikle açıp gerçek yük üreten sorguları bulma yöntemi.

    11 dk okuma Güncellendi: 25 Ağustos 2026

    "Site bazen çok yavaşlıyor" cümlesi, sistem yöneticisinin en sevmediği cümledir; çünkü ne zaman, hangi sayfada ve neden olduğunu söylemez. Veritabanı katmanında bu belirsizliği ortadan kaldıran tek araç yavaş sorgu günlüğüdür. Slow query log yapılandırma işini doğru yaptığında, "bazen yavaşlıyor" ifadesi yerini "şu sorgu günde 84 bin kez çalışıyor ve toplam sürenin yüzde 40'ını tek başına yiyor" gibi ölçülebilir bir cümleye bırakır.

    Bu rehberde yavaş sorgu günlüğünün tam olarak neyi kaydettiğini, kalıcı ve anlık olarak nasıl açılacağını, eşik değerini neye göre seçmen gerektiğini ve üretilen kaydın nasıl okunacağını anlatacağım. Ardından üç farklı analiz yöntemini karşılaştıracağız: dağıtımla gelen mysqldumpslow, çok daha güçlü olan pt-query-digest ve günlüğe hiç dokunmadan çalışan sys şeması. Son bölümlerde bulunan sorguyu EXPLAIN ile nasıl düzelteceğine ve günlük dosyasının diski doldurmasını nasıl engelleyeceğine değineceğim.

    Slow Query Log Tam Olarak Neyi Kaydeder#

    Yavaş sorgu günlüğü, çalışma süresi belirlediğin eşiği aşan her sorguyu tüm ayrıntısıyla bir dosyaya (ya da tabloya) yazar. Kaydedilen bilgi yalnızca sorgu metni değildir; sorgunun ne kadar sürdüğü, kilit beklemede ne kadar geçirdiği, kaç satır incelediği ve kaç satır döndürdüğü de yazılır. Bu son iki sayı, çoğu zaman sürenin kendisinden daha öğreticidir.

    Önemli bir ayrım var: günlük, sorgunun çalışma süresini ölçer, ağ gecikmesini veya uygulamanın sonucu işleme süresini değil. Yani günlükte hiçbir şey yokken sayfa hâlâ yavaşsa sorun veritabanında olmayabilir. Bir diğer ayrım da şu: bir sorgu eşiği aşmadığı hâlde saniyede yüzlerce kez çalışıyorsa günlüğe düşmez ama toplam yükün büyük bölümünü üretebilir. Bu yüzden eşiği makul bir seviyeye çekmek, "sadece felaket sorguları" yerine "yükü üretenleri" görmenin yoludur.

    Kaydedilen alanların anlamı şöyledir:

    AlanAnlamıNeye işaret eder
    Query_timeToplam çalışma süresiSorgunun maliyeti
    Lock_timeKilit bekleme süresiKilitlenme sorunu
    Rows_sentDöndürülen satır sayısıSonuç kümesi büyüklüğü
    Rows_examinedİncelenen satır sayısıİndeks yokluğu

    Rows_examined değeri Rows_sent değerinden kat kat büyükse, MySQL istediğin satırı bulmak için tabloyu taramak zorunda kalmıştır; bu neredeyse her zaman eksik indeks anlamına gelir. Bir sorguda 3 satır dönerken 1,2 milyon satır incelenmişse, indeks eklemek o sorguyu binlerce kat hızlandırabilir.

    Yapılandırma: Kalıcı ve Anlık Ayarlar#

    Günlüğü kalıcı olarak açmak için yapılandırma dosyasına birkaç satır eklersin. Bu ayarlar sunucu yeniden başladığında da geçerli kalır.

    # /etc/mysql/mysql.conf.d/mysqld.cnf
    [mysqld]
    slow_query_log      = 1
    slow_query_log_file = /var/log/mysql/mysql-slow.log
    long_query_time     = 1
    log_output          = FILE
    
    # İndeks kullanmayan sorguları da yakala (dikkatli kullan)
    log_queries_not_using_indexes = 1
    log_throttle_queries_not_using_indexes = 60
    
    # Çok az satır inceleyen sorguları görmezden gel
    min_examined_row_limit = 100
    
    # ALTER, OPTIMIZE gibi yönetim komutlarını da kaydet
    log_slow_admin_statements = 1
    

    Dosyanın var olduğundan ve MySQL'in yazabildiğinden emin ol; bu adım atlandığında günlük sessizce boş kalır:

    sudo mkdir -p /var/log/mysql
    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
    sudo systemctl restart mysql
    

    Sunucuyu yeniden başlatamayacağın durumlarda ayarların çoğu çalışma anında da değiştirilebilir. Bu, canlı bir sorunu incelemek için ideal yöntemdir:

    SET GLOBAL slow_query_log = 'ON';
    SET GLOBAL long_query_time = 0.5;
    SET GLOBAL log_queries_not_using_indexes = 'ON';
    
    -- Kontrol
    SHOW VARIABLES LIKE 'slow_query%';
    SHOW VARIABLES LIKE 'long_query_time';
    

    Dikkat edilecek nokta: long_query_time oturum bazlı bir değişkendir de. SET GLOBAL yaptığında zaten açık olan bağlantılar eski değeri kullanmaya devam eder; yeni bağlantılar yeni değeri alır. Bu yüzden değişikliğin etkisini görmek için biraz beklemen ya da bağlantı havuzunu yenilemen gerekebilir.

    log_output = TABLE seçeneği günlüğü mysql.slow_log tablosuna yazar ve SQL ile sorgulamayı mümkün kılar; ancak yazma maliyeti daha yüksektir ve pt-query-digest gibi araçlar dosya bekler. Üretimde FILE tercih edilmelidir.

    Eşik Değerini Doğru Seçmek#

    long_query_time varsayılan olarak 10 saniyedir ve bu değer pratikte işe yaramaz — 10 saniye süren bir sorgun varsa zaten farkındasındır. Asıl bilgi, 0,5 ile 2 saniye arasındaki sorgularda saklıdır.

    Yaklaşımım şu şekilde: önce eşiği görece yüksek tut, en kötüleri temizle, sonra kademeli olarak düşür. Bu, günlüğün bir anda okunamaz hâle gelmesini önler.

    Aşamalong_query_timeAmaç
    İlk kurulum2En kötü sorguları bul, günlük küçük kalsın
    Temizlik sonrası1Yaygın yavaşlıkları yakala
    İnce ayar0.3 – 0.5Toplam yükü üretenleri gör
    Kısa süreli teşhis0Her sorguyu kaydet, en fazla birkaç dakika

    long_query_time = 0 ayarı tüm sorguları günlüğe yazar. Bu, bir sorunu canlı yakalamak için çok güçlü bir yöntemdir ama disk çok hızlı dolar; yoğun bir sunucuda dakikada yüzlerce megabayt üretebilir. Yalnızca kısa süreli teşhis için, yanı başında durarak kullan ve mutlaka geri kapat.

    log_queries_not_using_indexes ayarı da benzer bir tuzak barındırır. İndeks kullanmayan her sorguyu kaydeder; küçük referans tablolarına yapılan yüzlerce hızlı sorgu da buna dahildir ve günlüğü gereksiz yere şişirir. min_examined_row_limit ile birlikte kullanmak bunu dengeler: yalnızca en az belirtilen sayıda satır inceleyen sorgular kaydedilir. Ayrıca log_throttle_queries_not_using_indexes değeri dakikada kaç böyle sorgunun kaydedileceğini sınırlar.

    Replikada da yavaş sorgu takibi yapmak istersen ilgili değişkeni ayrıca açman gerekir; replikasyon iş parçacığının uyguladığı sorgular varsayılan olarak günlüğe yazılmaz. Replika gecikmesinin kaynağını ararken bu ayar oldukça işe yarar.

    Günlük Kaydının Anatomisi#

    Günlüğü ilk açtığında karşına şuna benzer bloklar çıkar. Her blok tek bir sorguyu ve onun ölçümlerini içerir:

    # Time: 2026-08-25T11:42:07.418293Z
    # User@Host: uygulama[uygulama] @  [185.12.34.60]  Id: 184922
    # Query_time: 3.847215  Lock_time: 0.000118  Rows_sent: 12  Rows_examined: 1284507
    SET timestamp=1787654527;
    SELECT s.id, s.tutar, m.ad
    FROM siparis s JOIN musteri m ON m.id = s.musteri_id
    WHERE s.durum = 'bekliyor' AND s.olusturma > '2026-08-01'
    ORDER BY s.olusturma DESC LIMIT 12;
    

    Bu blok tek başına bir teşhis içeriyor. Rows_examined 1,2 milyon, Rows_sent ise 12. Yani MySQL 12 satır döndürmek için bir milyondan fazla satır okumuş. Lock_time neredeyse sıfır olduğu için sorun kilitlenme değil; sorun, durum ve olusturma sütunlarında uygun bir indeksin bulunmaması. Bu tür bir orantısızlık gördüğünde çözüm neredeyse her zaman indekstir.

    Farklı bir örnek düşünelim: Query_time 4 saniye ama Lock_time 3,9 saniye olsun. Bu durumda sorgunun kendisi hızlıdır, sorun başka bir işlemin tuttuğu kilidi beklemektir. Bu tamamen farklı bir sorundur ve çözümü indeks değil, uzun süren işlemleri kısaltmaktır; MySQL tablo kilitlenme sorunları yazısı bu senaryoyu ayrıntısıyla ele alıyor.

    Günlüğe hızlıca göz atmak için basit kabuk komutları yeterlidir:

    # Kaç yavaş sorgu kaydedilmiş
    grep -c "Query_time" /var/log/mysql/mysql-slow.log
    
    # En uzun süren 5 kaydı bul
    grep "Query_time" /var/log/mysql/mysql-slow.log | sort -t: -k2 -rn | head -5
    
    # Canlı izle
    sudo tail -f /var/log/mysql/mysql-slow.log
    

    Analiz: mysqldumpslow, pt-query-digest ve sys Şeması#

    Ham günlüğü gözle okumak birkaç yüz satıra kadar mümkündür; ötesinde bir araç gerekir. Üç seçeneğin var.

    mysqldumpslow MySQL ile birlikte gelir, ek kurulum istemez ve benzer sorguları gruplayıp sıralar. Hızlı bir bakış için yeterlidir:

    # Toplam süreye göre ilk 10 sorgu
    mysqldumpslow -s t -t 10 /var/log/mysql/mysql-slow.log
    
    # Çağrı sayısına göre
    mysqldumpslow -s c -t 10 /var/log/mysql/mysql-slow.log
    

    pt-query-digest ise gerçek analiz aracıdır. Sorguları normalleştirir, her grubun toplam süredeki payını yüzde olarak verir ve süre dağılımını gösterir:

    pt-query-digest /var/log/mysql/mysql-slow.log --limit=10
    

    Çıktının profil bölümü, hangi sorguya öncelik vereceğini doğrudan söyler:

    # Profile
    # Rank Query ID           Response time    Calls  R/Call  Item
    # ==== ================== ================ ====== ======= ==============
    #    1 0x7C2A9E1F4B8D3A05  1284.5521 47.2%   3641  0.3527  SELECT siparis musteri
    #    2 0xA13B5C7D9E2F4610   612.3390 22.5%  84120  0.0073  SELECT urun
    #    3 0x4E8F1D2C6B3A5079   231.1177  8.5%     19 12.1641  SELECT rapor
    

    Buradaki üçüncü satır önemli bir dersi barındırır: çağrı başına 12 saniye süren o sorgu en yavaşıdır, ama günde 19 kez çalıştığı için toplam etkisi ikinci sıradakinin yarısı kadardır. İkinci sıradaki sorgu ise 7 milisaniyede bitiyor ama 84 bin çağrıyla toplamın beşte birini üretiyor. Optimizasyona toplam süreye göre başla. Bu aracın kurulumunu ve diğer yeteneklerini Percona Toolkit ile veritabanı bakımı yazısında bulabilirsin.

    Üçüncü yöntem ise günlüğe hiç dokunmaz: performance_schema verilerini okunabilir hâle getiren sys şeması. Günlük açık olmasa bile çalışır ve sunucu açıldığından beri biriken istatistikleri gösterir:

    SELECT query, exec_count, total_latency, avg_latency, rows_examined_avg, rows_sent_avg
    FROM sys.statement_analysis
    ORDER BY total_latency DESC
    LIMIT 10;
    

    Bu yöntemin avantajı anlıktır ve disk yazmaz; dezavantajı ise sorgu metinlerinin kısaltılmış olması ve sunucu yeniden başladığında sıfırlanmasıdır. İkisini birlikte kullanmak en iyisidir: hızlı bakış için sys, derin analiz için günlük ve pt-query-digest. Yavaş sorgu bulmanın diğer yöntemlerini MySQL yavaş sorgu bulma yazısında topladım.

    Bulunan Sorguyu Düzeltmek#

    Suçluyu bulduktan sonra iş asıl şimdi başlıyor. İlk adım daima EXPLAIN ile sorgunun nasıl yürütüldüğünü görmektir:

    EXPLAIN SELECT s.id, s.tutar, m.ad
    FROM siparis s JOIN musteri m ON m.id = s.musteri_id
    WHERE s.durum = 'bekliyor' AND s.olusturma > '2026-08-01'
    ORDER BY s.olusturma DESC LIMIT 12;
    

    Çıktıda type sütunu ALL ise tam tablo taraması yapılıyor demektir; key sütunu NULL ise hiçbir indeks kullanılmıyordur. rows sütunu ise MySQL'in kaç satır inceleyeceğini tahmin ettiğini gösterir. Bu üç sinyal birlikte göründüğünde çözüm bellidir:

    -- Filtre ve sıralama sütunlarını kapsayan bileşik indeks
    CREATE INDEX idx_siparis_durum_tarih ON siparis (durum, olusturma);
    
    -- Sonucu doğrula
    EXPLAIN SELECT s.id FROM siparis s
    WHERE s.durum = 'bekliyor' AND s.olusturma > '2026-08-01'
    ORDER BY s.olusturma DESC LIMIT 12;
    

    Bileşik indekste sütun sırası önemlidir: eşitlik karşılaştırması yapılan sütun (durum) önce, aralık karşılaştırması yapılan sütun (olusturma) sonra gelir. Ters sırada tanımlarsan indeks aralık sorgusundan sonra durur ve sıralamada kullanılamaz. MySQL 8 kullanıyorsan EXPLAIN ANALYZE ile tahmin yerine gerçek ölçümleri de görebilirsin:

    EXPLAIN ANALYZE SELECT s.id FROM siparis s WHERE s.durum = 'bekliyor' LIMIT 12;
    

    İndeks eklemenin bedeli olduğunu da unutma: her indeks yazma işlemlerini biraz yavaşlatır ve disk kullanır. Kullanılmayan indeksleri düzenli olarak temizlemek, eklemek kadar önemlidir. Genel performans ayarları için MySQL ve MariaDB performans optimizasyonu yazısındaki tampon havuzu önerileri iyi bir tamamlayıcıdır.

    Günlük Dosyasını Yönetmek ve Sık Yapılan Hatalar#

    Birinci hata, günlüğü açıp unutmaktır. Düşük bir eşikle açılan günlük hızla büyür ve diski doldurur; disk dolduğunda MySQL yazma işlemlerini reddeder, yani performans aracın bir kesinti sebebine dönüşür. Çözüm, günlüğü döndürmektir:

    # /etc/logrotate.d/mysql-slow
    /var/log/mysql/mysql-slow.log {
        daily
        rotate 7
        missingok
        compress
        notifempty
        create 640 mysql mysql
        postrotate
            /usr/bin/mysqladmin flush-logs
        endscript
    }
    

    postrotate bölümündeki flush-logs komutu kritiktir; onsuz MySQL eski dosya tanımlayıcısına yazmaya devam eder ve döndürülmüş dosya büyümeye devam ederken yenisi boş kalır.

    İkinci hata, long_query_time = 0 ayarını açıp geri kapatmayı unutmaktır. Birkaç dakikalık teşhis için harikadır, saatlerce açık kalırsa hem disk hem de performans maliyeti üretir. Açarken hemen bir hatırlatıcı kur.

    Üçüncü hata, en yavaş sorguya odaklanıp toplam yükü göz ardı etmektir. Günde birkaç kez çalışan 12 saniyelik bir rapor sorgusu can sıkıcıdır ama sistemi yavaşlatan genellikle o değildir. pt-query-digest çıktısındaki yüzde sütununu ölçüt al.

    Dördüncü hata, günlük dosyasının izinlerini yanlış vermektir. MySQL dosyaya yazamıyorsa hiçbir uyarı görmezsin, günlük sadece boş kalır. Açtıktan sonra birkaç dakika bekleyip dosyanın büyüdüğünü doğrula:

    ls -lh /var/log/mysql/mysql-slow.log
    sudo -u mysql test -w /var/log/mysql/mysql-slow.log && echo "yazilabilir"
    

    Beşinci hata, günlüğü hassas veri içermiyormuş gibi ele almaktır. Sorgu metinleri parametre değerlerini de içerir; bu, müşteri e-postaları, telefon numaraları ve bazen kimlik bilgileri anlamına gelir. Dosyanın izinlerini 640 tut, yedeklerken şifrele ve gereksiz eski kopyaları saklama. Aynı hassasiyet, pt-query-digest raporlarını bir yere gönderirken de geçerlidir.

    Sıkça Sorulan Sorular#

    long_query_time değerini kaç yapmalıyım#

    Varsayılan 10 saniye pratikte işe yaramaz, çünkü o kadar süren sorguları zaten fark edersin. İlk kurulumda 2 saniye ile başlayıp en kötü sorguları temizlemeni, sonra kademeli olarak 1 ve 0,5 saniyeye indirmeni öneririm. Değer ondalıklı olabilir, yani 0.3 gibi bir eşik geçerlidir. Kısa süreli teşhis için 0 yapıp tüm sorguları kaydedebilirsin ama bunu birkaç dakikadan uzun tutma.

    Slow query log performansı etkiler mi#

    Makul bir eşikle çalışırken etkisi ihmal edilebilir; yalnızca eşiği aşan sorgular için tek bir satır yazılır. Etki, eşiği çok düşürdüğünde veya log_queries_not_using_indexes ayarını sınırsız açtığında ortaya çıkar; o zaman her sorgu için disk yazması yapılır ve yoğun sistemlerde bu hissedilir. log_output = TABLE seçeneği de dosyaya yazmaya kıyasla daha maliyetlidir, üretimde FILE tercih edilmelidir.

    Sunucuyu yeniden başlatmadan slow query log açabilir miyim#

    Evet. SET GLOBAL slow_query_log = 'ON'; ve SET GLOBAL long_query_time = 1; komutlarıyla ayarlar anında devreye girer. Ancak long_query_time oturum bazlı da bir değişken olduğu için, zaten açık olan bağlantılar eski değeri kullanmaya devam eder; etkiyi görmek için yeni bağlantıların açılmasını beklemen gerekebilir. Bu değişiklikler kalıcı değildir, sunucu yeniden başlayınca yapılandırma dosyasındaki değerlere döner.

    Günlükte hiç kayıt yoksa ne anlama gelir#

    Üç ihtimal var. Birincisi gerçekten eşiği aşan sorgu olmamasıdır; eşiği düşürüp tekrar bak. İkincisi günlüğün aslında açılmamış olmasıdır; SHOW VARIABLES LIKE 'slow_query_log'; çıktısını kontrol et. Üçüncüsü ve en sinsi olanı, MySQL'in dosyaya yazma izni olmamasıdır; dosyanın sahibinin mysql kullanıcısı olduğunu ve dizinin yazılabilir olduğunu doğrula. Bu üçüncü durumda hiçbir hata mesajı görmezsin.

    mysqldumpslow mu pt-query-digest mi kullanmalıyım#

    mysqldumpslow MySQL ile birlikte geldiği için ek kurulum gerektirmez ve hızlı bir bakış için yeterlidir. pt-query-digest ise sorguları çok daha iyi normalleştirir, her sorgunun toplam yükteki yüzdesini verir ve süre dağılımını gösterir; hangi sorguya öncelik vereceğine karar vermek için gerçekten faydalı olan budur. Sunucuya Percona Toolkit kurabiliyorsan pt-query-digest tercih edilmelidir.

    Yavaş sorguyu bulduktan sonra ne yapmalıyım#

    İlk adım EXPLAIN ile sorgunun yürütme planına bakmaktır. type sütunu ALL ve key sütunu boş ise tam tablo taraması yapılıyordur ve çözüm genellikle uygun bir indeks eklemektir. Rows_examined değerinin Rows_sent değerine oranı yüksekse bu tanı doğrulanır. Bileşik indeks oluştururken eşitlik karşılaştırması yapılan sütunu önce, aralık karşılaştırması yapılan sütunu sonra yazmayı unutma; sıra yanlışsa indeks beklediğin faydayı vermez.

    Kapanış#

    Yavaş sorgu günlüğü, veritabanı performansında tahmin yürütmeyi ölçmeyle değiştiren en ucuz araçtır. Aklında kalması gereken dört alışkanlık şu: eşiği varsayılan 10 saniyede bırakma, kademeli olarak 1 saniyenin altına indir; analiz yaparken en yavaş sorguya değil toplam süre payı en yüksek sorguya odaklan; Rows_examined ile Rows_sent oranını indeks eksikliğinin ilk göstergesi olarak oku; ve günlüğü logrotate ile döndürmeden açık bırakma. Bir de günlük dosyasının müşteri verisi içerdiğini unutma, izinlerini ona göre ayarla.

    Bu ayarları yapabilmek için MySQL yapılandırma dosyasına erişimin olması gerekir; VDS ve bulut sunucu paketlerimiz tam root erişimiyle bunu mümkün kılar. Sunucunun genel kaynak durumunu ölçmek için bant genişliği hesaplayıcı gibi araçlarımıza bakabilir, performans analizini ve indeks çalışmalarını uzman gözüyle yürütmek istersen sunucu yönetimi hizmetimizden yararlanabilirsin.

    MySQLPerformansLog

    Uygulamaya geçmeye hazır mısınız?

    NVMe SSD, ücretsiz SSL ve %99.9 uptime garantisiyle Clou.TR hosting ve sunucu çözümleriyle projenizi hayata geçirin.