Veritabanı Yönetimi

    my.cnf Optimizasyon Rehberi

    my.cnf içindeki kritik parametreleri sunucu boyutuna göre ayarlamanın pratik rehberi.

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

    MySQL kurduğunda gelen varsayılan yapılandırma, on beş yıl önceki küçük sunucular düşünülerek hazırlanmış bir uzlaşmadır. Bugünkü bir VDS'te bu ayarlarla çalışmak, sekiz çekirdekli bir makineyi tek çekirdek gibi kullanmak demektir. my.cnf optimizasyonu tam da bu boşluğu kapatır: doğru ayarlanmış on beş parametre, çoğu zaman donanım yükseltmesinden daha fazla kazanç sağlar.

    Bu rehberde my.cnf dosyasının nerede durduğunu, hangi bölümlerden oluştuğunu, gerçekten fark yaratan parametreleri neyin belirlediğini ve sunucu boyutuna göre kullanabileceğin hazır başlangıç yapılandırmalarını anlatacağım. Ayrıca internette dolaşan ama modern sürümlerde ya kaldırılmış ya da zararlı olan ayarları da işaretleyeceğim; çünkü kopyala-yapıştır bir my.cnf, ayarsız bir my.cnf'ten daha tehlikeli olabiliyor.

    Dosya Nerede, Hangi Sırayla Okunur#

    MySQL ve MariaDB yapılandırmayı tek bir dosyadan değil, bir dosya zincirinden okur ve sonra okunan kazanır. Dağıtıma göre yol değişir:

    Dağıtım / kurulumAna dosyaEk dizin
    Debian / Ubuntu/etc/mysql/my.cnf/etc/mysql/conf.d/, /etc/mysql/mysql.conf.d/
    RHEL / AlmaLinux / Rocky/etc/my.cnf/etc/my.cnf.d/
    cPanel / WHM sunucuları/etc/my.cnf
    MariaDB (çoğu dağıtım)/etc/my.cnf/etc/my.cnf.d/

    Hangi dosyaların okunduğunu tahmin etme, doğrudan sor:

    # Okunacak dosyaların sırası ve varsayılan değerler
    mysqld --verbose --help | grep -A 1 "Default options"
    
    # Çalışan sunucunun gerçekte kullandığı değer (dosyadakinden farklı olabilir)
    mysql -e "SELECT @@innodb_buffer_pool_size, @@max_connections, @@innodb_log_file_size;"
    

    Doğru pratik, ana dosyayı hiç ellemeyip kendi ayarlarını conf.d altında zz-tuning.cnf gibi alfabetik olarak sonda gelen bir dosyaya yazmaktır. Böylece paket güncellemeleri ana dosyayı değiştirdiğinde ayarların ezilmez ve neyi sen değiştirdin, neyi dağıtım getirdi ayırt edebilirsin. Dosya izinlerinin 644 ve sahibinin root olması gerekir; MySQL, dünya tarafından yazılabilir bir yapılandırma dosyasını güvenlik gerekçesiyle yok sayar.

    Gerçekten Fark Yaratan Bellek Parametreleri#

    Bellek ayarları ikiye ayrılır: global tamponlar (tüm sunucu için bir kez ayrılır) ve oturum tamponları (her bağlantı için ayrı ayrılır). Bu ayrımı kaçırmak, bellek hesabında en sık yapılan hatadır: sort_buffer_size değerini 256 MB yapan biri, 200 bağlantıda teorik olarak 50 GB'lık bir yük yaratmış olur.

    [mysqld]
    # --- GLOBAL: bir kez ayrılır ---
    innodb_buffer_pool_size   = 8G      # tek en önemli ayar
    innodb_log_file_size      = 1G      # buffer pool'un ~1/8'i iyi bir başlangıç
    innodb_log_buffer_size    = 64M     # büyük transaction'lar varsa artır
    tmp_table_size            = 64M     # in-memory geçici tablo tavanı
    max_heap_table_size       = 64M     # yukarıdakiyle AYNI değer olmalı
    
    # --- OTURUM: bağlantı başına ayrılır, dikkatli ol ---
    sort_buffer_size          = 2M
    join_buffer_size          = 2M
    read_buffer_size          = 256K
    read_rnd_buffer_size      = 512K
    

    tmp_table_size ve max_heap_table_size mutlaka aynı değere ayarlanmalıdır; MySQL ikisinin küçüğünü dikkate alır, bu yüzden yalnızca birini artırmak hiçbir işe yaramaz. Bu iki değer, GROUP BY ve DISTINCT sorgularının geçici tablosunun bellekte mi diskte mi oluşacağını belirler. Diske düşme sıklığını ölçebilirsin:

    SHOW GLOBAL STATUS LIKE 'Created_tmp_disk_tables';
    SHOW GLOBAL STATUS LIKE 'Created_tmp_tables';
    -- disk oranı %10'un üzerindeyse tmp_table_size'ı artır veya sorguyu düzelt
    

    Buffer pool ayarı tek başına bir konu olacak kadar kritik; hesaplama yöntemini ve hit oranı ölçümünü InnoDB buffer pool ayarı yazısında ayrıntılı anlattım.

    InnoDB Dayanıklılık ve Yazma Ayarları#

    InnoDB tarafında birkaç parametre doğrudan "ne kadar hızlı yazarım" ile "çökmede ne kadar veri kaybederim" arasındaki dengeyi belirler. En kritiği innodb_flush_log_at_trx_commit:

    DeğerDavranışKullanım
    1Her commit'te günlük diske yazılır ve senkronize edilirVarsayılan; finansal/kritik veri. En güvenli
    2Her commit'te OS önbelleğine yazılır, saniyede bir diskeMySQL çökerse veri korunur, sunucu çökerse ~1 sn kayıp
    0Saniyede bir yazılır ve senkronize edilirEn hızlı, en riskli. Yalnızca yeniden üretilebilir veride

    Karar ölçütün "hız" değil, "bir saniyelik veriyi kaybetmeyi göze alır mıyım" olmalı. Sipariş ve ödeme kaydı tutan bir sistemde 1 dışında bir değer kullanma. Buna karşılık oturum, önbellek ya da analitik verisi tutan bir tabloda 2 belirgin hızlanma sağlar ve makul bir risktir.

    [mysqld]
    innodb_flush_log_at_trx_commit = 1
    innodb_flush_method            = O_DIRECT   # çift önbelleklemeyi önler
    innodb_file_per_table          = ON         # her tabloya ayrı .ibd dosyası
    innodb_io_capacity             = 2000       # SATA SSD için makul
    innodb_io_capacity_max         = 4000       # NVMe'de daha yükseğe çıkabilir
    innodb_read_io_threads         = 8
    innodb_write_io_threads        = 8
    

    innodb_flush_method = O_DIRECT ayarı, sayfaların hem InnoDB havuzunda hem işletim sistemi önbelleğinde iki kez tutulmasını engeller; Linux üzerinde neredeyse her zaman doğru seçimdir. innodb_io_capacity ise InnoDB'nin arka planda saniyede kaç G/Ç işlemi yapabileceğini tahmin etmesini sağlar. Dönen disk çağından kalma varsayılan değer, modern bir NVMe diskte InnoDB'yi gereksiz yere frenler; diskinin gerçek kapasitesini fio ile ölçüp ona göre ayarlaman en doğrusudur.

    innodb_log_file_size değeri de yazma yoğun sistemlerde belirleyicidir. Çok küçükse checkpoint'ler sıklaşır ve yazma dalgalanmaları oluşur. Buffer pool'un sekizde biri kaba bir başlangıçtır; 8 GB havuz için 1 GB günlük dosyası makul olur. MySQL 8.0.30 ve sonrasında bu ayarın yerini innodb_redo_log_capacity alır ve çevrimiçi değiştirilebilir.

    Bağlantı Yönetimi ve Zaman Aşımları#

    max_connections değerini yükseltmek, "Too many connections" hatasına verilen ilk ve genelde yanlış cevaptır. Her bağlantı bellek tüketir ve MySQL binlerce eşzamanlı bağlantıyla verimli çalışmaz; asıl çözüm uygulama tarafında bağlantı havuzu kullanmak ve boşta kalan bağlantıları hızlı kapatmaktır.

    [mysqld]
    max_connections        = 200
    max_connect_errors     = 100000
    thread_cache_size      = 32        # bağlantı başına thread oluşturmayı azaltır
    wait_timeout           = 300       # boşta bekleyen bağlantıyı 5 dk sonra kapat
    interactive_timeout    = 600
    max_allowed_packet     = 64M       # büyük BLOB / import için
    table_open_cache       = 4000
    open_files_limit       = 65535
    

    Gerçek ihtiyacını ölçmeden değer seçme. Sunucunun şimdiye kadarki en yüksek eşzamanlı bağlantı sayısını görmek için:

    SHOW GLOBAL STATUS LIKE 'Max_used_connections';
    SHOW GLOBAL STATUS LIKE 'Threads_connected';
    SHOW GLOBAL STATUS LIKE 'Threads_created';   -- yüksekse thread_cache_size düşük
    

    Max_used_connections değeri max_connections sınırının yüzde 85'ini geçmişse gerçekten sınıra yaklaşıyorsundur. Ama önce şunu kontrol et: bağlantıların çoğu Sleep durumunda mı? Öyleyse sorun uygulamanın bağlantıları kapatmamasıdır, sunucunun kapasitesi değil. Bu hatanın tam teşhisini MySQL Too Many Connections hatası yazısında ele aldım.

    wait_timeout değerini düşürmek, sızdıran uygulamalara karşı etkili bir emniyet supabıdır. Ancak uzun süren rapor sorguları varsa çok kısa bir değer onları da keser; 300 saniye çoğu web uygulaması için dengeli bir başlangıçtır.

    Günlükleme: Yavaş Sorgu ve Hata Günlüğü#

    Ayar yapmadan önce ölçmen gerekir; ölçmenin en ucuz yolu yavaş sorgu günlüğüdür. Üretimde açık bırakmak neredeyse hiçbir maliyeti olmayan bir yatırımdır.

    [mysqld]
    slow_query_log             = 1
    slow_query_log_file        = /var/log/mysql/slow.log
    long_query_time            = 1                 # 1 saniyeden uzun sorgular
    log_queries_not_using_indexes = 0              # gürültü yapar, teşhiste kısa süre aç
    min_examined_row_limit     = 100
    log_error                  = /var/log/mysql/error.log
    log_error_verbosity        = 2
    

    Günlük dosyalarını logrotate yönetimine almayı unutma; aksi hâlde yavaş sorgu günlüğü diski doldurabilir. Aynı risk ikili günlük (binlog) için de geçerlidir ve orada sonuçları daha ağırdır:

    [mysqld]
    log_bin            = /var/log/mysql/mysql-bin
    binlog_expire_logs_seconds = 604800   # 7 gün sonra otomatik temizle
    max_binlog_size    = 256M
    sync_binlog        = 1                # replikasyonda veri kaybını önler
    

    binlog_expire_logs_seconds ayarını atlarsan binlog dosyaları sonsuza kadar birikir ve bir gün disk dolar. Bu, uygulamaların aniden yazma yapamaz hale geldiği klasik bir olaydır; kurtarma yöntemini MySQL binlog diski doldurdu yazısında adım adım anlattım.

    Sunucu Boyutuna Göre Örnek Yapılandırmalar#

    Aşağıdaki üç profil, adanmış bir MySQL sunucusu için makul başlangıç noktalarıdır. Kopyaladıktan sonra mutlaka kendi ölçümlerinle ayarla; hiçbir hazır dosya senin veri kümeni tanımıyor.

    # --- Profil A: 2 GB RAM, küçük site / paylaşımlı ortam ---
    [mysqld]
    innodb_buffer_pool_size        = 512M
    innodb_log_file_size           = 128M
    innodb_flush_log_at_trx_commit = 1
    innodb_flush_method            = O_DIRECT
    max_connections                = 60
    tmp_table_size                 = 32M
    max_heap_table_size            = 32M
    sort_buffer_size               = 1M
    join_buffer_size               = 1M
    thread_cache_size              = 8
    slow_query_log                 = 1
    long_query_time                = 2
    
    # --- Profil B: 8 GB RAM, orta ölçekli e-ticaret ---
    [mysqld]
    innodb_buffer_pool_size        = 5G
    innodb_buffer_pool_instances   = 4
    innodb_log_file_size           = 640M
    innodb_flush_log_at_trx_commit = 1
    innodb_flush_method            = O_DIRECT
    innodb_io_capacity             = 2000
    innodb_io_capacity_max         = 4000
    max_connections                = 200
    thread_cache_size              = 32
    tmp_table_size                 = 64M
    max_heap_table_size            = 64M
    sort_buffer_size               = 2M
    join_buffer_size               = 2M
    wait_timeout                   = 300
    slow_query_log                 = 1
    long_query_time                = 1
    
    # --- Profil C: 32 GB RAM, yoğun OLTP ---
    [mysqld]
    innodb_buffer_pool_size        = 22G
    innodb_buffer_pool_instances   = 8
    innodb_log_file_size           = 2G
    innodb_log_buffer_size         = 128M
    innodb_flush_log_at_trx_commit = 1
    innodb_flush_method            = O_DIRECT
    innodb_io_capacity             = 6000
    innodb_io_capacity_max         = 12000
    innodb_read_io_threads         = 12
    innodb_write_io_threads        = 12
    max_connections                = 500
    thread_cache_size              = 64
    table_open_cache               = 8000
    open_files_limit               = 65535
    tmp_table_size                 = 128M
    max_heap_table_size            = 128M
    slow_query_log                 = 1
    long_query_time                = 0.5
    

    Değişiklikleri uygulamadan önce mutlaka söz dizimini doğrula ve yeniden başlatmayı izle:

    # Söz dizimi ve okunabilirlik kontrolü
    mysqld --validate-config
    
    # Servisi yeniden başlat ve hata günlüğünü izle
    sudo systemctl restart mysql
    sudo tail -f /var/log/mysql/error.log
    

    Kaldırılmış, Zararlı ya da Efsane Ayarlar#

    İnternetteki eski rehberlerin en büyük zararı, artık var olmayan parametreleri önermeleridir. query_cache_size ve query_cache_type MySQL 8.0'da tamamen kaldırılmıştır; yapılandırmada bırakırsan sunucu açılmaz. MariaDB'de hâlâ vardır ama yazma yoğun sistemlerde global bir kilit yarattığı için kapalı tutulması önerilir.

    innodb_thread_concurrency bir dönem popülerdi; modern sürümlerde varsayılan 0 (sınırsız) neredeyse her zaman daha iyidir, elle sınırlamak yalnızca çok özel çekişme senaryolarında anlamlıdır. skip-name-resolve ise gerçekten faydalıdır: DNS çözümlemesini kapatarak bağlantı kurulumunu hızlandırır, ama açtığında yetkileri ana makine adına değil IP'ye göre tanımlamış olman gerekir.

    [mysqld]
    skip_name_resolve = ON      # bağlantı gecikmesini azaltır; yetkileri IP ile tanımla
    

    En büyük tuzak ise otomatik "tuning" betiklerine körü körüne uymaktır. mysqltuner gibi araçlar iyi bir başlangıç noktası verir ama sunucun birkaç saattir çalışıyorsa istatistikleri anlamsızdır ve önerileri yanlış olur. Aracı en az bir haftalık normal trafikten sonra çalıştır, önerileri tek tek değerlendir ve her seferinde tek bir parametre değiştirip etkisini ölç. Aynı anda on ayarı değiştirirsen hangisinin işe yaradığını asla bilemezsin. Ayrıca her değişiklikten önce yapılandırma dosyasının bir kopyasını al; geri dönebilmek, ileri gitmekten daha önemlidir. Veritabanı yedeğini de düzenli almayı ihmal etme; yöntemleri mysqldump ile veritabanı yedekleme yazısında bulabilirsin.

    Sıkça Sorulan Sorular#

    my.cnf dosyasını değiştirdikten sonra MySQL'i yeniden başlatmak şart mı#

    Çoğu parametre için evet, ama hepsi için değil. max_connections, slow_query_log, long_query_time, wait_timeout gibi dinamik değişkenleri SET GLOBAL ile anında değiştirebilirsin. innodb_log_file_size gibi bazı ayarlar ise yeniden başlatma gerektirir. MySQL 8.0.23 ve sonrasında SET PERSIST komutu değişikliği hem anında uygular hem de kalıcı hale getirir, böylece dosyayı elle düzenleme ihtiyacı azalır.

    Bir ayarın gerçekten uygulanıp uygulanmadığını nasıl kontrol ederim#

    Dosyaya yazdığın değere değil, sunucunun bildirdiği değere bak: SHOW VARIABLES LIKE 'innodb_buffer_pool_size'; ya da SELECT @@max_connections; komutları çalışan gerçek değeri verir. Fark varsa muhtemelen ayarı yanlış bölüme yazmışsındır ([mysqld] yerine [client] gibi), sonra okunan bir dosya ezmiştir ya da MySQL değeri yuvarlamıştır. Hata günlüğünde "unknown variable" uyarısı da olabilir.

    mysqltuner önerilerine güvenebilir miyim#

    Kısmen. Aracın çıktısı sunucunun çalışma süresi boyunca biriken durum değişkenlerine dayanır; sunucu yeni başlatıldıysa öneriler anlamsız olur. En az bir haftalık gerçek trafikten sonra çalıştırmalısın. Ayrıca önerilerin bir kısmı genel şablondur ve senin iş yükünü tanımaz; her maddeyi ölçümle doğrulamadan uygulama, özellikle bellek tamponlarını agresif biçimde artıran önerilere temkinli yaklaş.

    query_cache_size ayarını neden bulamıyorum#

    Çünkü MySQL 8.0 ile birlikte sorgu önbelleği tamamen kaldırıldı. Yapılandırma dosyanda bu satır kalırsa MySQL 8 başlatılamaz ve hata günlüğüne bilinmeyen değişken hatası yazar. MariaDB'de özellik hâlâ mevcuttur ama çok çekirdekli ve yazma yoğun sistemlerde tek bir global kilit oluşturduğu için genellikle kapalı bırakılması önerilir; önbellekleme ihtiyacını uygulama katmanında Redis veya Memcached ile karşılamak daha ölçeklenebilir.

    Paylaşımlı hostingte my.cnf ayarlarını değiştirebilir miyim#

    Hayır, paylaşımlı hostingte sunucu genelindeki my.cnf dosyasına erişimin olmaz; ayarlar tüm hesaplar için ortak tutulur. Bu tür ortamlarda yapabileceğin şey uygulama tarafında iyileştirmedir: index eklemek, sorguları düzeltmek, gereksiz eklentileri kaldırmak. Sunucu düzeyinde ayar yapmak istiyorsan root erişimi veren bir VDS ya da bulut sunucuya geçmen gerekir.

    max_connections değerini yükseltmek "Too many connections" hatasını çözer mi#

    Genellikle geçici olarak çözer ama asıl sebebi gizler. Bağlantıların çoğu Sleep durumunda bekliyorsa sorun uygulamanın bağlantıları kapatmamasıdır; sınırı yükseltmek yalnızca hatayı ileri bir tarihe erteler ve bu arada bellek tüketimini artırır. Doğru sıralama şudur: önce SHOW PROCESSLIST ile bağlantıların ne yaptığına bak, uygulama tarafında havuz kullan, wait_timeout değerini makul bir seviyeye çek, sonra gerekiyorsa sınırı yükselt.

    MariaDB ve MySQL için aynı my.cnf kullanılabilir mi#

    Temel parametrelerin büyük kısmı ortaktır, ama ikisi 10.x ve 8.x sürümlerinden sonra belirgin biçimde ayrıştı. MariaDB'de olup MySQL 8'de olmayan (query_cache_type, thread pool ayarları) ya da tersi durumda parametreler var. En güvenli yöntem, ortak temel bir dosya tutup sürüme özel ayarları ayrı bir conf.d dosyasına koymak ve her değişiklikten sonra mysqld --validate-config ile doğrulamaktır.

    Kapanış#

    my.cnf ayarını sağlam bir rutine bağlayabilirsin: ayarlarını ana dosyaya değil conf.d altında kendi dosyana yaz, her seferinde tek parametre değiştirip etkisini ölç, değeri dosyadan değil SHOW VARIABLES çıktısından doğrula ve internetten kopyaladığın hiçbir satırı ne işe yaradığını bilmeden bırakma. Bellek tarafında global ve oturum tamponlarını ayırt etmek, dayanıklılık tarafında ise innodb_flush_log_at_trx_commit kararını iş gereksinimine göre vermek en kritik iki noktadır.

    Bu ayarların tamamı ancak root erişimin varsa anlam taşır. Kendi MySQL yapılandırmanı yönetmek istiyorsan VDS ve sanal sunucu paketlerimiz tam yetki verir; kaynak ihtiyacın dalgalanıyorsa bulut sunucu ile esneyebilirsin. Ayarları, izlemeyi ve yedeklemeyi bizim üstlenmemizi isterseniz sunucu yönetimi ve yedekleme hizmetlerimiz bu işi devralır.

    MySQLYapılandırmaPerformans

    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.