MySQL Too Many Connections Hatası Nasıl Çözülür?
Gece 2’de telefonun çaldığını düşün. Müşteri panikle arıyor: “Site çöktü, hiçbir şey açılmıyor!” Sen de bağlanıp logları açtığında şu satırı görüyorsun:
ERROR 1040 (HY000): Too many connections
MySQL dünyasının en klasik, en sinir bozucu ve aynı zamanda en çok öğretici hatalarından biriyle karşı karşıyasın. Bu yazıda bu hatanın neden çıktığını, nasıl teşhis edileceğini ve kalıcı olarak nasıl çözüleceğini tüm detaylarıyla ele alacağız.
Too Many Connections Hatası Nedir?
MySQL, aynı anda kaç bağlantı kabul edeceğini max_connections adlı bir değişkenle kontrol eder. Bu limite ulaşıldığında yeni gelen bağlantı talepleri reddedilir ve istemci ERROR 1040 hatasını alır.
Burada ince bir nokta var: MySQL, max_connections limitine ek olarak bir adet süper kullanıcı bağlantısı daha rezerve eder. Yani max_connections değerin 150 ise, 151. bağlantı sadece SUPER yetkisine sahip kullanıcılar için açık tutulur. Bu sayede sistem limiti aşmış olsa bile root ile bağlanıp durumu inceleyebilirsin.
Peki bu limit neden aşılır? Birkaç temel senaryo var:
- Bağlantı havuzu yanlış yapılandırılmış: Uygulama her istek için yeni bir bağlantı açıp kapatmak yerine bağlantıları biriktiriyor.
- Yavaş sorgular: Uzun süren sorgular bağlantıyı meşgul tutuyor, yeni istekler de üst üste yığılıyor.
max_connectionsdeğeri çok düşük: Sunucu büyümüş ama MySQL ayarları güncellenmemiş.- Bağlantı sızıntısı: Uygulama bağlantıları düzgün kapatmıyor, onlarca “uyuyan” bağlantı birikmiş.
- DDoS veya bot trafiği: Kısa sürede çok sayıda istek geldi.
Anlık Durum Tespiti
Hata anında ilk yapman gereken şey sisteme bağlanmak ve mevcut durumu anlamak. MySQL’in o bir adet rezerve bağlantısını kullanarak root ile girebilirsin:
mysql -u root -p
Bağlandıktan sonra anlık bağlantı sayısını kontrol et:
SHOW STATUS LIKE 'Threads_connected';
SHOW STATUS LIKE 'Max_used_connections';
SHOW VARIABLES LIKE 'max_connections';
Threads_connected o an aktif bağlantı sayısını, Max_used_connections ise MySQL’in ayağa kalktığından bu yana ulaştığı en yüksek bağlantı sayısını gösterir. Max_used_connections değerin max_connections değerine çok yakınsa, sisteminiz sınırda çalışıyor demektir.
Şu anki tüm bağlantıların durumunu görmek için:
SHOW FULL PROCESSLIST;
Bu komut sana şu bilgileri verir: hangi kullanıcı, hangi host’tan, hangi veritabanına bağlı, ne kadar süredir bekliyor ve ne yapıyor. “Sleep” durumundaki çok sayıda bağlantı görüyorsan, bağlantı sızıntısı veya wait_timeout sorunu var demektir.
Anlık Müdahale: Yangını Söndür
Teşhisi koydun, şimdi acil müdahale zamanı. Önce mevcut bağlantı limitini çalışan MySQL’de geçici olarak artır:
SET GLOBAL max_connections = 300;
Bu değişiklik MySQL’i yeniden başlatmadan anında devreye girer. Ancak dikkat: Bu kalıcı bir çözüm değil, sadece yangını söndürmek için. MySQL yeniden başlatıldığında bu değer sıfırlanır.
Sorunlu bağlantıları öldürmek gerekiyorsa, önce hangi process ID’lerinin sorun çıkardığını tespit et:
SELECT id, user, host, db, command, time, state, info
FROM information_schema.processlist
WHERE command != 'Daemon'
ORDER BY time DESC;
Belirli bir process’i sonlandırmak için:
KILL CONNECTION 1234;
Eğer onlarca uyuyan bağlantıyı tek tek öldürmek zorunda kaldıysan, bunu bash ile otomatize edebilirsin:
mysql -u root -p -e "SELECT CONCAT('KILL ', id, ';') FROM information_schema.processlist WHERE command = 'Sleep' AND time > 300;" | mysql -u root -p
Bu komut 300 saniyeden (5 dakika) uzun süredir uyuyan tüm bağlantıları sonlandırır. Üretim ortamında çalıştırmadan önce ne yapacağını iki kez oku.
Kalıcı Çözüm: MySQL Yapılandırması
Anlık müdahalenin ardından kalıcı çözüme geçelim. MySQL yapılandırma dosyasını açalım:
sudo nano /etc/mysql/mysql.conf.d/mysqld.cnf
# veya CentOS/RHEL için:
sudo nano /etc/my.cnf
Değiştirmen veya eklemen gereken parametreler şunlar:
max_connections: Maksimum eş zamanlı bağlantı sayısı. Kaç bağlantıya ihtiyacın olduğunu hesaplamak için şu formülü kullanabilirsin: (toplam_ram - innodb_buffer_pool_size) / tek_bağlantı_bellek_kullanımı. Tek bir MySQL bağlantısı ortalama 1-2 MB bellek tüketir.
wait_timeout: Uyuyan bir bağlantının kaç saniye sonra otomatik olarak kapatılacağı. Varsayılan değer 28800 saniye yani 8 saattir. Bu çok uzun.
interactive_timeout: Etkileşimli (terminal gibi) bağlantılar için timeout süresi.
connect_timeout: Bağlantı kurulumu için maksimum bekleme süresi.
[mysqld]
max_connections = 300
wait_timeout = 60
interactive_timeout = 60
connect_timeout = 10
Değişiklikleri kalıcı hale getirdikten sonra MySQL’i yeniden başlat:
sudo systemctl restart mysql
# veya
sudo systemctl restart mysqld
Yeniden başlatma sonrası kontrol et:
mysql -u root -p -e "SHOW VARIABLES LIKE 'max_connections'; SHOW VARIABLES LIKE 'wait_timeout';"
Kök Neden Analizi: Gerçek Sorun Nerede?
Limiti artırmak genellikle bir bandaj çözümdür. Asıl soru şu: neden bu kadar çok bağlantıya ihtiyaç duyuyorsunuz?
Yavaş Sorguları Tespit Et
Bağlantılar birikiyorsa çoğunlukla geride yavaş bir sorgu var demektir. Yavaş sorgu logunu aktifleştir:
SET GLOBAL slow_query_log = 'ON';
SET GLOBAL long_query_time = 1;
SET GLOBAL slow_query_log_file = '/var/log/mysql/slow-query.log';
Bir süre çalıştırdıktan sonra bu logu analiz et:
mysqldumpslow -s t -t 10 /var/log/mysql/slow-query.log
Bu komut en çok zaman alan 10 sorguyu listeler. Buradan çıkan sonuçlara göre eksik index’leri eklemeyi veya sorguları optimize etmeyi düşünebilirsin.
Bağlantı Havuzu Kullanımını Kontrol Et
PHP kullanan bir ortamdaysanız (WordPress, Laravel gibi), php-fpm worker sayısı ile MySQL bağlantı sayısı arasında doğrudan bir ilişki vardır. Kaç tane PHP-FPM worker çalıştığını öğren:
ps aux | grep php-fpm | grep -v grep | wc -l
Her PHP-FPM worker potansiyel olarak bir MySQL bağlantısı açabilir. 100 worker varsa MySQL’e teorik olarak 100 bağlantı açılabilir. Bu değeri max_connections ile karşılaştır.
ProxySQL veya MySQL Router Kullan
Çok sayıda uygulama sunucusu varsa ve bağlantı sayısı kontrolden çıkıyorsa, ProxySQL kullanmak ciddi fark yaratır. ProxySQL, uygulamalardan gelen yüzlerce bağlantıyı alıp MySQL’e çok daha az sayıda bağlantı üzerinden iletir. Bağlantı havuzlama işini uygulama katmanından alıp veritabanı önüne koyar.
Basit ProxySQL kurulumu:
# ProxySQL repo ekle
cat > /etc/yum.repos.d/proxysql.repo << EOF
[proxysql_repo]
name=ProxySQL repository
baseurl=https://repo.proxysql.com/ProxySQL/proxysql-2.x/centos/$releasever
gpgcheck=1
gpgkey=https://repo.proxysql.com/ProxySQL/proxysql-2.x/repo_pub_key
EOF
yum install -y proxysql
systemctl start proxysql
Monitoring: Bir Daha Sürpriz Yaşama
Sorun çözüldü, ama nasıl takip edeceksin? İyi bir sysadmin reaktif değil proaktif olur.
Temel İzleme Scripti
Bağlantı sayısını düzenli kontrol eden basit bir bash scripti yaz:
#!/bin/bash
MYSQL_USER="root"
MYSQL_PASS="sifren"
THRESHOLD=80 # Yüzde olarak uyarı eşiği
MAX_CONN=$(mysql -u$MYSQL_USER -p$MYSQL_PASS -se "SHOW VARIABLES LIKE 'max_connections';" | awk '{print $2}')
CURR_CONN=$(mysql -u$MYSQL_USER -p$MYSQL_PASS -se "SHOW STATUS LIKE 'Threads_connected';" | awk '{print $2}')
USAGE_PCT=$(echo "scale=0; $CURR_CONN * 100 / $MAX_CONN" | bc)
echo "$(date): Aktif baglanti: $CURR_CONN / $MAX_CONN (%$USAGE_PCT)"
if [ "$USAGE_PCT" -gt "$THRESHOLD" ]; then
echo "UYARI: Baglanti kullanimi %$THRESHOLD esigini asti!"
# Buraya mail veya Slack bildirimi ekleyebilirsin
# mail -s "MySQL Baglanti Uyarisi" [email protected] <<< "Baglanti kullanimi: %$USAGE_PCT"
fi
Bu scripti crontab’a ekle:
crontab -e
# Her 5 dakikada bir calistir
*/5 * * * * /usr/local/bin/mysql_conn_check.sh >> /var/log/mysql_conn_monitor.log 2>&1
Önemli MySQL Status Değişkenleri
Durum takibinde kullanman gereken değişkenler şunlar:
- Threads_connected: Anlık bağlantı sayısı
- Threads_running: Aktif olarak sorgu çalıştıran thread sayısı
- Max_used_connections: Tarihteki en yüksek eş zamanlı bağlantı sayısı
- Connection_errors_max_connections: Limit aşımı nedeniyle reddedilen bağlantı sayısı
- Aborted_connects: Hatalı tamamlanan bağlantı sayısı
- Aborted_clients: Düzgün kapatılmamış istemci bağlantısı sayısı
Bu değerleri sorgulamak için:
SHOW STATUS LIKE 'Connection_errors%';
SHOW STATUS LIKE 'Aborted%';
SHOW STATUS LIKE 'Threads%';
Aborted_clients sayısı yüksekse, uygulamanın bağlantıları düzgün kapatmadığının işaretidir. Bu genellikle PHP tarafında mysql_close() çağrılmaması veya Python’da context manager kullanılmaması gibi kodlama hatalarından kaynaklanır.
Gerçek Dünya Senaryosu: E-Ticaret Sitesi Çöküşü
Birkaç yıl önce karşılaştığım gerçek bir vakayı paylaşayım. Orta ölçekli bir e-ticaret sitesi, büyük bir indirim kampanyası başladığında tamamen çöktü. Log’lara bakıldığında klasik Too many connections hatasıydı.
Sistemin yapısı şöyleydi: 3 web sunucusu, her birinde 50 PHP-FPM worker, toplam 150 olası bağlantı. MySQL max_connections değeri de tam 150 olarak ayarlanmıştı. Normal günlerde sorun yoktu ama kampanya başladığında 3 sunucunun tüm worker’ları aynı anda MySQL’e bağlanmaya çalıştı.
İlk müdahale olarak max_connections 400’e çıkarıldı. Ama bu sefer farklı bir sorun çıktı: Sunucuda yeterli RAM yoktu ve MySQL bellek yetersizliğinden dolayı yavaşladı.
Kalıcı çözüm için şu adımlar atıldı:
- PHP-FPM üzerinde
pm.max_childrendeğeri her sunucuda 30’a indirildi. - Uygulama seviyesinde persistent connection kaldırılıp bağlantı havuzlama düzgün yapılandırıldı.
wait_timeout28800’den 30’a indirildi, boşta bekleyen bağlantılar hızlıca temizlenmeye başladı.- Öne ProxySQL eklendi, web sunucularından gelen 90 bağlantı MySQL’e 15 bağlantıya düşürüldü.
Sonuç: Bir sonraki kampanyada trafik üç katına çıktı ama MySQL bağlantı sayısı hiçbir zaman 20’yi geçmedi.
MySQL 8.0 ile Gelen Özellikler
MySQL 8.0 kullanıyorsan, yeni gelen connection_control eklentisi ile başarısız bağlantı denemelerini yavaşlatabilirsin. Bu özellikle brute force ve bot trafiğine karşı işe yarar:
INSTALL PLUGIN CONNECTION_CONTROL SONAME 'connection_control.so';
INSTALL PLUGIN CONNECTION_CONTROL_FAILED_LOGIN_ATTEMPTS SONAME 'connection_control.so';
SET GLOBAL connection_control_failed_connections_threshold = 5;
SET GLOBAL connection_control_min_connection_delay = 1000;
Ayrıca MySQL 8.0’da information_schema.innodb_trx tablosu üzerinden uzun süren transaction’ları daha kolay tespit edebilirsin:
SELECT trx_id, trx_started, trx_state, trx_tables_in_use, trx_tables_locked
FROM information_schema.innodb_trx
WHERE trx_started < NOW() - INTERVAL 5 MINUTE;
5 dakikadan uzun süren transaction varsa, bunlar bağlantıları meşgul tutarak yeni bağlantıların birikmesine sebep olabilir.
Max Connections Değerini Doğru Hesaplama
“Kaç bağlantı yeterli?” sorusunun cevabı her ortam için farklıdır. Ama şu hesaplamayı kılavuz olarak kullanabilirsin:
Sunucunun toplam RAM’ini, InnoDB buffer pool büyüklüğünü ve diğer sabit MySQL bellek kullanımını göz önünde bulundurarak şu sorguyu çalıştır:
SELECT
@@innodb_buffer_pool_size / 1024 / 1024 AS buffer_pool_mb,
@@max_connections AS max_conn,
(@@innodb_buffer_pool_size / 1024 / 1024) + (@@max_connections * 2) AS estimated_total_mb;
Bu sorgu tahmini toplam bellek kullanımını MB cinsinden gösterir. Burada her bağlantı için 2 MB hesaplandı; bu ortalama bir değer. Karmaşık sorgular veya büyük sort_buffer_size değerleri bu rakamı artırır.
Genel kural olarak: estimated_total_mb değeri sunucunun fiziksel RAM’inin %75-80’ini geçmemeli. Geçiyorsa ya RAM artırman ya da bağlantı sayısını kısıtlaman gerekiyor.
Sonuç
Too many connections hatası ilk bakışta basit bir limit aşımı gibi görünse de, arkasında birden fazla katmanlı sorun olabilir. Yangını söndürmek için max_connections değerini artırmak genellikle yeterlidir, ama bu sadece bir başlangıç noktasıdır.
Asıl yapman gerekenler sırasıyla şunlar:
- Anlık durumu
SHOW PROCESSLISTile analiz et - Geçici olarak limiti artır, sistemi stabilize et
- Yavaş sorgu logunu aktifleştirip kök nedeni bul
wait_timeoutveinteractive_timeoutdeğerlerini düşür- Uygulama tarafında bağlantı havuzlamasını gözden geçir
- Kapasiteye uygun
max_connectionsdeğerini hesapla ve kalıcı yapılandır - Proaktif monitoring kur, bir daha sürpriz yaşama
Bu hatayı bir kez yaşayan herkes bir şey öğrenir: veritabanı bağlantıları sonsuz bir kaynak değil. Her bağlantı bellek, thread ve dosya tanımlayıcısı tüketir. Bağlantıları verimli kullanmak, sadece MySQL performansını değil tüm sistemin sağlığını doğrudan etkiler.
Gece 2’deki telefon çağrısından sonra sabah 9’da aynı sorunu çözdüğünde yaşadığın rahatlama tarif edilemez. Bir dahaki sefere bu süreci atlamak için şimdi monitoring’i kur.
