MySQL 'Too many connections' Hatası: Kalıcı Çözüm Rehberi
MySQL 'Too many connections' hatasının kök nedenleri, max_connections ayarı, connection pooling ve uygulama tarafı çözümler — pratik komutlarla.
MySQL veritabanı sunucusu çalışan her web projesinde er ya da geç karşılaşılan en sinir bozucu hatalardan biri ERROR 1040 (HY000): Too many connections mesajıdır. Bu hata göründüğünde site açılmaz, uygulama veritabanına yazamaz ve mysql komutuyla bile sunucuya bağlanılamaz. Bu rehberde hatanın gerçek nedenini nasıl tespit edeceğinizi, geçici müdahaleyle kalıcı çözüm arasındaki farkı ve sunucu kapasitesine göre max_connections değerini nasıl doğru hesaplayacağınızı somut komutlarla öğreneceksiniz.
Hata Tam Olarak Ne Anlama Geliyor?
MySQL, aynı anda kaç istemciden bağlantı kabul edeceğini max_connections adlı bir parametreyle sınırlar. Varsayılan değer MySQL 8.0 ve MariaDB 10.6'da 151'dir. Bu sayıya ulaşıldığında yeni gelen her bağlantı isteği reddedilir ve hatayı tetikler.
Burada kritik bir ayrıntı vardır: MySQL aslında max_connections + 1 bağlantıya izin verir. Fazladan olan bu tek slot SUPER yetkisine sahip kullanıcıya ayrılmıştır. Yani sunucu tıkandığında bile root kullanıcısı bağlanıp müdahale edebilir — şartı, root'un başka bir bağlantıda zaten oturum açmamış olmasıdır. Acil durumda bu davranış hayat kurtarır.
Hatayı tetikleyen üç tipik senaryo vardır:
- Trafik artışı: Eş zamanlı kullanıcı sayısı, mevcut limitin üstüne çıkmıştır.
- Bağlantı sızıntısı (connection leak): Uygulama bağlantıyı açıp kapatmayı unutuyor, kullanılmayan bağlantılar Sleep durumunda birikiyordur.
- Yavaş sorgular: Bir sorgu uzun sürdüğü için bağlantı serbest kalmıyor, yenileri kuyruğa giriyordur.
Çözüme geçmeden önce hangi senaryoda olduğunuzu mutlaka teşhis edin. Aksi halde max_connections değerini sürekli artırırsınız ama hata geri gelir.
Acil Müdahale: Sunucuyu Yeniden Başlatmadan Açmak
Site kapalıyken yapılacak ilk iş mysql istemcisine SUPER yetkili kullanıcıyla bağlanmaktır:
bash mysql -u root -p
Bağlantı kurulduktan sonra mevcut bağlantı tablosunu inceleyin:
sql SHOW PROCESSLIST;
Çıktıda Command sütunu Sleep olan ve Time sütunu yüksek (örneğin 300 saniyenin üstünde) bağlantılar bağlantı sızıntısının kanıtıdır. Bunları toplu olarak sonlandırmak için:
sql SELECT CONCAT('KILL ', id, ';') FROM information_schema.processlist WHERE Command = 'Sleep' AND Time > 300;
Üretilen KILL komutlarını çalıştırdığınızda sunucu hemen rahatlar. Bu kalıcı bir çözüm değildir; sadece site erişimini geri kazandırmak içindir. Asıl iş şimdi başlıyor.
Doğru max_connections Değeri Nasıl Hesaplanır?
İnternette dolaşan "max_connections değerini 500 yap" tavsiyesi tehlikelidir çünkü her bağlantı RAM tüketir. Bağlantı başına ortalama bellek kullanımı 4 MB ile 12 MB arasındadır; iş yüküne göre daha da yükselebilir.
Kullanılabilir RAM'i bağlantıya bölme yaklaşımı için kabaca şu formül işe yarar:
max_connections = (Toplam RAM - InnoDB Buffer Pool - OS payı) / Bağlantı başına RAM
Örnek hesap: 8 GB RAM'li bir VDS sunucusunda 4 GB'ı innodb_buffer_pool_size'a, 1 GB'ı işletim sistemine ayırırsanız MySQL bağlantıları için ~3 GB kalır. Bağlantı başına 8 MB hesabıyla:
3072 MB / 8 MB = 384
Yani teorik tavan 384'tür. Pratikte bunun %70'ini kullanmak güvenlidir, bu da ~270 anlamına gelir.
Aşağıda yaygın sunucu profilleri için referans değerler yer alıyor:
| Toplam RAM | InnoDB Buffer Pool | Önerilen max_connections | Tipik Kullanım |
|---|---|---|---|
| 2 GB | 768 MB | 100 - 150 | Küçük WordPress, blog |
| 4 GB | 2 GB | 200 - 300 | Orta ölçek e-ticaret |
| 8 GB | 4 GB | 300 - 500 | Yoğun trafikli site |
| 16 GB | 10 GB | 500 - 800 | Kurumsal uygulama |
| 32 GB | 22 GB | 800 - 1500 | Çok kiracılı SaaS |
Ayarı kalıcı yapmak için /etc/mysql/my.cnf veya /etc/mysql/mariadb.conf.d/50-server.cnf dosyasındaki [mysqld] bölümüne ekleyin:
ini [mysqld] max_connections = 300 wait_timeout = 120 interactive_timeout = 120
Servisi yeniden başlatın (systemctl restart mysql). Yeniden başlatma yapamıyorsanız çalışan sunucuda anlık değişiklik için:
sql SET GLOBAL max_connections = 300;
Bu komut MySQL servisini yeniden başlatana kadar geçerlidir; konfigürasyon dosyasına da yazmayı unutmayın.
Connection Pooling: Asıl Çözüm Burada
Uygulama her HTTP isteğinde MySQL'e yeni bağlantı açıp kapatıyorsa, hem TCP el sıkışma maliyeti yaşar hem de pik anlarda limiti zorlar. Çözüm bağlantı havuzu (connection pooling)'dur. Havuz, önceden açılmış bağlantıları paylaşımlı olarak kullanır.
İki farklı katmanda havuzlama yapabilirsiniz:
Uygulama Tarafı Havuzlama
Framework seviyesinde havuz konfigürasyonu en kontrollü çözümdür. Örnek değerler:
- Laravel (config/database.php): pool ayarı PHP'de doğal değildir; PHP-FPM süreç sayısı zaten doğal sınırı belirler. PHP-FPM'de pm.max_children değeri, MySQL'e açılabilecek eş zamanlı bağlantı tavanıdır.
- Node.js (mysql2): connectionLimit: 10 ile başlayın; uygulama sunucusu sayısıyla çarpın.
- Django: CONN_MAX_AGE = 60 ile bağlantıyı 60 saniye açık tutun, her istekte yeniden açılmasını engelleyin.
- Spring Boot (HikariCP): maximum-pool-size: 20 varsayılanı çoğu uygulama için yeterlidir.
Proxy Tarafı Havuzlama
Birden fazla uygulama sunucusu varsa ProxySQL veya MaxScale gibi araçlar tek noktadan havuz yönetir. Uygulama, MySQL yerine proxy'ye bağlanır; proxy 1000 uygulama bağlantısını 50 MySQL bağlantısına dönüştürür. Yüksek trafikli yapılarda max_connections değerini 5'e katlamadan aynı kapasiteyi karşılamayı sağlar.
Bağlantı Sızıntısı Tespiti ve Önleme
Sızıntı, hatanın en sinsi nedenidir çünkü trafik normal seviyede bile sunucu zamanla dolar. Tespit için periyodik olarak şu sorguyu çalıştırın:
sql SELECT user, host, db, command, COUNT(*) as connection_count FROM information_schema.processlist GROUP BY user, host, db, command ORDER BY connection_count DESC;
Sleep durumundaki bağlantıların sayısı toplam bağlantının %50'sini geçiyorsa sızıntı vardır. Önlemek için:
- Uygulama kodunda her connect() için try/finally bloğu içinde close() çağrısı yapın.
- ORM kullanıyorsanız transaction'ların kapanışını otomatik yöneten context manager yapısı kullanın (Python'da with, PHP'de try/finally).
- wait_timeout değerini düşürün. Varsayılan 28800 saniyedir (8 saat); bu bir uygulama sunucusu için saçmadır. 120-300 saniye arası sağlıklıdır.
- Veritabanına bağlanan cron job'lar betiğin sonunda mutlaka quit; çağırsın.
İzleme: Hata Tekrar Gelmeden Görmek
Reaktif olmak yerine proaktif olun. Şu üç metriği sürekli izleyin:
sql SHOW STATUS WHERE Variable_name IN ('Threads_connected', 'Max_used_connections', 'Connection_errors_max_connections');
- Threads_connected: Anlık aktif bağlantı sayısı.
- Max_used_connections: Sunucu açıldığından beri ulaşılan tepe değer.
- Connection_errors_max_connections: Limit nedeniyle reddedilen bağlantı sayısı. Bu değer 0'dan büyükse çoktan hata almışsınız demektir.
Production ortamında bu metrikleri Prometheus + Grafana, Zabbix veya Netdata gibi araçlarla 1 dakikalık çözünürlükte toplayın. Max_used_connections değeri max_connections değerinin %80'ine yaklaştığında uyarı kurallarını tetikleyecek alarm tanımlayın.
Ek olarak MySQL'in slow query log özelliğini açın:
ini slow_query_log = 1 slow_query_log_file = /var/log/mysql/slow.log long_query_time = 2
2 saniyenin üstünde süren sorgular log'a düşer. Bağlantıları meşgul tutan ana suçlu genellikle indekssiz SELECT veya JOIN sorgularıdır; EXPLAIN çıktısıyla optimize edilmesi gerekir.
Kontrol Listesi: Sırasıyla Yapılması Gerekenler
Hatayı kalıcı olarak çözmek için bu sırayı takip edin:
- SHOW PROCESSLIST ile mevcut durumu görün; uzun süren Sleep bağlantılarını kapatın.
- SHOW VARIABLES LIKE 'max_connections' ile mevcut limiti öğrenin.
- Sunucu RAM'ine göre yukarıdaki tabloyu kullanarak doğru max_connections değerini hesaplayın.
- wait_timeout değerini 28800'den 120-300 aralığına çekin.
- Uygulama tarafında havuz boyutunu (PHP-FPM workers, HikariCP pool, mysql2 connectionLimit) kontrol edin.
- Connection_errors_max_connections ve Max_used_connections metriklerini izleme sistemine ekleyin.
- Slow query log'u açın ve süresi 2 saniyeyi geçen sorguları indeksleyin.
Bu adımları sırayla uyguladığınızda hatanın geri dönme ihtimali ciddi şekilde azalır. RAM yetersizse max_connections artırmak çare değildir; sunucu kaynaklarını yükseltmeniz veya uygulamanızı birden fazla MySQL replica'sına dağıtmanız gerekir. Karar verirken mevcut sunucunuzun RAM ve CPU kullanım grafiklerini son 30 günlük pencerede inceleyin: pik bellek kullanımı %85'i geçiyorsa sorunun kaynağı kapasitedir, konfigürasyon değil.
Hosting karşılaştırması yapmaya hazır mısın?
100+ firmanın fiyatlarını tek tıkla karşılaştır, en uygun paketi bul.
Hosting Karşılaştır →İlgili Yazılar
WordPress’te Redis/Memcached object cache mantıklı mı?
WordPress’te object cache (Redis/Memcached) ne kazandırır? Uyumsuzluk, ayar hataları ve ne zaman şart olduğu için net kontrol listesi.
Yavaş Database Sorguları Nasıl Bulunur? Net Optimizasyon Rehberi
Yavaş sorguları bulmak için MySQL/PostgreSQL’de doğru log ve metrikleri toplayın, problemli SQL’i tespit edip ölçülebilir şekilde optimize edin.
Snapshot yedekleme gerçek backup yerine geçer mi?
Snapshot (anlık görüntü) hızlı geri dönüş sağlar. Ancak gerçek backup değildir. Doğru strateji, süre/erişim ve test kriterlerini birlikte ele alır.
Paylaşımlı Hosting Yeterli mi? Ne Zaman Değiştirmeli?
Paylaşımlı hosting ne zaman yeterli olur, ne zaman VDS/VPS gerekir? Trafik, kaynak, hız, güvenlik ve maliyet eşiklerini net şekilde öğren.