MySQL ve MariaDB’de Bağlantı Havuzu Yönetimi: max_connections Ayarı

Veritabanı sunucunuz yavaşladığında, uygulamalar “Too many connections” hatası fırlatmaya başladığında ya da monitoring ekranlarında bağlantı sayısı kırmızıya döndüğünde, çoğu sysadmin’in ilk tepkisi max_connections değerini artırmak oluyor. Bu doğru bir refleks mi? Kısmen. Ama hikayenin tamamı bu kadar basit değil. Bu yazıda MySQL ve MariaDB’de bağlantı havuzu yönetimini, max_connections parametresinin nasıl doğru ayarlanacağını ve bu işin çevresindeki tüm detayları konuşacağız.

Bağlantı Havuzu Nedir ve Neden Önemlidir?

MySQL/MariaDB’ye her bağlantı kurulduğunda sunucu tarafında bir thread oluşturulur. Bu thread bellek tüketir, CPU zamanı kullanır ve işletim sistemi kaynaklarına el koyar. Küçük ölçekli sistemlerde bu fark edilmez ama yüzlerce, binlerce eş zamanlı bağlantıyla karşılaştığınızda tablo dramatik biçimde değişir.

Bağlantı havuzu (connection pool) kavramı, uygulamanın her işlem için yeni bir bağlantı açıp kapatmak yerine önceden hazırlanmış bir bağlantı kümesini yeniden kullanmasına dayanır. Bu yaklaşım hem bağlantı kurma maliyetini düşürür hem de veritabanı sunucusunun anlamsız thread yönetimi yapmasının önüne geçer.

Havuz yönetimi iki taraftan ele alınmalıdır:

  • Uygulama tarafı: PHP’de PDO, Java’da HikariCP, Python’da SQLAlchemy gibi kütüphanelerin havuz ayarları
  • Sunucu tarafı: MySQL/MariaDB’nin kendi bağlantı limitleri ve thread yönetim mekanizmaları

Bu yazıda ağırlıklı olarak sunucu tarafına odaklanacağız ama uygulama tarafını tamamen görmezden gelmek mümkün değil.

Mevcut Durumu Analiz Etmek

Herhangi bir değişiklik yapmadan önce mevcut bağlantı durumunu anlamak şart. Çoğu sysadmin bu adımı atlıyor ve körü körüne değer artırıyor. Önce şunu çalıştırın:

mysql -u root -p -e "SHOW STATUS LIKE '%connect%';"

Bu komutun çıktısında dikkat etmeniz gereken değerler şunlardır:

  • Connections: Sunucu başladığından beri toplam bağlantı denemesi sayısı
  • Max_used_connections: Şimdiye kadar aynı anda kullanılan maksimum bağlantı sayısı
  • Threads_connected: Şu an aktif bağlantı sayısı
  • Connection_errors_max_connections: Limit aşıldığı için reddedilen bağlantı sayısı
mysql -u root -p -e "SHOW VARIABLES LIKE 'max_connections';"
mysql -u root -p -e "SHOW STATUS LIKE 'Threads_connected';"
mysql -u root -p -e "SHOW STATUS LIKE 'Max_used_connections';"

Altın kural şudur: Eğer Max_used_connections değeri max_connections değerinin yüzde seksenini geçmişse, ya limitinizi artırmanız ya da bağlantı havuzu optimizasyonu yapmanız gerekiyor demektir. Eğer Connection_errors_max_connections değeri sıfırdan büyükse, zaten bağlantı reddediyorsunuz demektir, acil aksiyon gereklidir.

max_connections Değerini Hesaplamak

Burası işin gerçekten kritik kısmı. Pek çok blog yazısı “şu değeri gir, olur” diyor ama aslında bu hesaplama sunucunun fiziksel kapasitesine göre yapılmalıdır.

Temel formül şöyle işliyor:

Her MySQL bağlantısı thread başına bir miktar RAM tüketir. Bu miktar sort_buffer_size, join_buffer_size, read_buffer_size ve benzeri per-thread buffer değerlerinin toplamıyla ilgilidir. Kaba bir hesap için şunu kullanabilirsiniz:

Kullanılabilir RAM = Toplam RAM - OS İçin Ayrılan - InnoDB Buffer Pool
max_connections = Kullanılabilir RAM / Thread başına ortalama bellek

Örnek senaryo: 16 GB RAM’li bir sunucuda InnoDB buffer pool için 8 GB ayırdınız, işletim sistemi için 1 GB bıraktınız. Geriye 7 GB kaldı. Her thread ortalama 8-10 MB tüketiyorsa teorik maksimumunuz 700-875 civarında. Pratik kullanım için bunun yüzde seksenini almak mantıklı: yaklaşık 560-700 bağlantı.

Şu anki per-thread bellek kullanımını görmek için:

mysql -u root -p -e "
SELECT 
  (@@read_buffer_size + @@read_rnd_buffer_size + @@sort_buffer_size + 
   @@join_buffer_size + @@binlog_cache_size + @@thread_stack) / 1024 / 1024 
  AS 'Per Thread MB';
"

max_connections Nasıl Ayarlanır?

Kalıcı değişiklik için /etc/mysql/mysql.conf.d/mysqld.cnf ya da /etc/my.cnf dosyasını düzenlemeniz gerekir. Hangi dosyanın kullanıldığını bulmak için:

mysql --help | grep "Default options" -A 5

ya da:

mysqld --verbose --help 2>/dev/null | grep "Default options" -A 3

Konfigürasyon dosyasını açıp [mysqld] bloğu altına ekleyin:

# /etc/mysql/mysql.conf.d/mysqld.cnf dosyasını düzenleyin
sudo nano /etc/mysql/mysql.conf.d/mysqld.cnf
[mysqld]
max_connections = 500
max_connect_errors = 100
connect_timeout = 10
wait_timeout = 300
interactive_timeout = 300

Servisi yeniden başlatmadan önce yapılandırmayı doğrulayın:

mysqld --validate-config
sudo systemctl restart mysql
# veya MariaDB için:
sudo systemctl restart mariadb

Dikkat: MySQL 5.7 ve MariaDB 10.2 sonrasında bazı değişkenleri canlı olarak da değiştirebilirsiniz. Bu yeniden başlatma gerektirmez:

mysql -u root -p -e "SET GLOBAL max_connections = 500;"

Ancak bu değişiklik sunucu yeniden başladığında sıfırlanır. Kalıcılık için her zaman konfigürasyon dosyasını da güncellemelisiniz.

Thread Cache ile max_connections İlişkisi

Bağlantı yönetiminin sıklıkla gözden kaçan bir parçası thread_cache_size parametresidir. Bir bağlantı kapandığında thread yok edilmek yerine cache’e alınabilir ve bir sonraki bağlantıda yeniden kullanılabilir. Bu, thread oluşturma maliyetini dramatik biçimde düşürür.

mysql -u root -p -e "SHOW STATUS LIKE 'Threads_created';"
mysql -u root -p -e "SHOW STATUS LIKE 'Connections';"

Threads_created / Connections oranı yüksekse thread caching yeterince çalışmıyor demektir. Bu durumda:

[mysqld]
thread_cache_size = 50

MySQL 5.6.8’den itibaren thread_cache_size otomatik ayarlanır ama MariaDB’de manuel ayar hala önemlidir.

Thread pool kullanmak istiyorsanız MariaDB’nin thread pool eklentisi gerçek iş ortamlarında çok daha iyi sonuç verir:

[mysqld]
thread_handling = pool-of-threads
thread_pool_size = 32
thread_pool_max_threads = 1000
thread_pool_idle_timeout = 60

Bu yapılandırmayla MariaDB, binlerce bağlantıyı çok daha az worker thread ile yönetebilir.

Gerçek Dünya Senaryosu: WordPress Sitesinin Çöküşü

Birkaç yıl önce yönettiğim bir e-ticaret altyapısını örnek vereyim. Hafta içi trafikte sorun yoktu ama her Cuma akşamı ve kampanya dönemlerinde site hata veriyordu. Loglar şunu gösteriyordu:

ERROR 1040 (HY000): Too many connections

Sunucu 8 GB RAM’deydi ve max_connections değeri varsayılan 151’di. Uygulama tarafında ise PHP-FPM 200 worker ile çalışıyordu. Her PHP worker bir veritabanı bağlantısı açtığında 200 bağlantıya ihtiyaç duyuyordu. 151 limiti bu durumu karşılamıyordu bile.

Önce monitoring için bir script yazdım:

#!/bin/bash
# mysql_connection_monitor.sh

DB_USER="monitor_user"
DB_PASS="monitor_pass"
LOG_FILE="/var/log/mysql_connections.log"
ALERT_THRESHOLD=80  # max_connections'ın yüzde 80'i

MAX_CONN=$(mysql -u$DB_USER -p$DB_PASS -se "SELECT @@max_connections;")
CURR_CONN=$(mysql -u$DB_USER -p$DB_PASS -se "SELECT COUNT(*) FROM information_schema.processlist;")
USAGE_PCT=$(( (CURR_CONN * 100) / MAX_CONN ))

TIMESTAMP=$(date '+%Y-%m-%d %H:%M:%S')
echo "$TIMESTAMP - Current: $CURR_CONN / Max: $MAX_CONN (%$USAGE_PCT)" >> $LOG_FILE

if [ $USAGE_PCT -ge $ALERT_THRESHOLD ]; then
    echo "$TIMESTAMP - UYARI: Baglanti kullanimi %$USAGE_PCT seviyesinde!" | 
    mail -s "MySQL Baglanti Uyarisi" [email protected]
fi

Monitoring bir hafta boyunca çalıştı ve gerçek pik değeri görüldü. Ardından değerleri güvenli bir şekilde artırdık ve wait_timeout değerini düşürerek boşta kalan bağlantıların daha hızlı temizlenmesini sağladık.

wait_timeout ve interactive_timeout: Sessiz Kaçaklar

Bağlantı sayısı sürekli yüksek kalıyorsa ve makul bir trafik seviyesindeyseniz, sorunu wait_timeout ve interactive_timeout değerlerinde arayın.

wait_timeout: Etkileşimsiz bir bağlantının ne kadar süre sonra kapatılacağı (saniye cinsinden). Varsayılan 28800 saniye yani 8 saat.

interactive_timeout: MySQL istemcisi gibi etkileşimli bağlantılar için aynı süre.

Bir PHP uygulamasında bağlantı havuzu doğru yapılandırılmamışsa, açık kalan bağlantılar 8 saat boyunca bağlantı slotlarını meşgul edebilir. Bunu makul değerlere çekin:

mysql -u root -p -e "
SHOW VARIABLES LIKE 'wait_timeout';
SHOW VARIABLES LIKE 'interactive_timeout';
SELECT 
  user, host, command, time, state 
FROM information_schema.processlist 
WHERE command = 'Sleep' 
ORDER BY time DESC 
LIMIT 20;
"

Uyku modundaki bağlantıları gördüğünüzde wait_timeout değerini düşürün:

[mysqld]
wait_timeout = 300
interactive_timeout = 300

ProxySQL ile Bağlantı Havuzu Katmanı Eklemek

Ciddi bir yük altındaki sistemler için MySQL/MariaDB önüne ProxySQL koymak gerçek anlamda oyun değiştirici olabiliyor. ProxySQL, bağlantı multiplexing yaparak yüzlerce uygulama bağlantısını düzinelerce gerçek veritabanı bağlantısına indirgebilir.

Kurulum Ubuntu/Debian üzerinde:

# ProxySQL repository ekle
wget -O- 'https://repo.proxysql.com/ProxySQL/proxysql-2.x/repo_pub_key' | 
  apt-key add -
echo deb https://repo.proxysql.com/ProxySQL/proxysql-2.x/$(lsb_release -sc)/ ./ | 
  tee /etc/apt/sources.list.d/proxysql.list

apt-get update
apt-get install proxysql

# ProxySQL admin arayüzüne bağlan
mysql -u admin -padmin -h 127.0.0.1 -P 6032

ProxySQL üzerinde bağlantı havuzu ayarları:

# Backend sunucu ekle
INSERT INTO mysql_servers(hostgroup_id, hostname, port, max_connections) 
VALUES (0, '127.0.0.1', 3306, 200);

# Bağlantı havuzu parametreleri
UPDATE global_variables 
SET variable_value = '10000' 
WHERE variable_name = 'mysql-max_connections';

UPDATE global_variables 
SET variable_value = 'transaction' 
WHERE variable_name = 'mysql-connection_pool_algorithm';

LOAD MYSQL SERVERS TO RUNTIME;
SAVE MYSQL SERVERS TO DISK;
LOAD GLOBAL VARIABLES TO RUNTIME;
SAVE GLOBAL VARIABLES TO DISK;

Bu yapılandırmayla 10.000 uygulama bağlantısı ProxySQL üzerinden geçer ama arkada MySQL’e sadece 200 gerçek bağlantı gider. Bu fark, bellek ve CPU tüketiminde muazzam bir iyileşme demektir.

Bağlantı Sorunlarını Teşhis Etmek için Faydalı Sorgular

Canlı sistemde neyin döndüğünü görmek için bu sorguları işinizde kullanın:

mysql -u root -p -e "
-- Kullanıcı başına bağlantı sayısı
SELECT user, COUNT(*) as connections 
FROM information_schema.processlist 
GROUP BY user 
ORDER BY connections DESC;
"

mysql -u root -p -e "
-- Uzun süre çalışan sorgular
SELECT id, user, host, db, command, time, state, LEFT(info, 100) as query
FROM information_schema.processlist
WHERE command != 'Sleep' AND time > 30
ORDER BY time DESC;
"

mysql -u root -p -e "
-- Host başına bağlantı dağılımı
SELECT host, COUNT(*) as conn_count, 
       GROUP_CONCAT(DISTINCT user) as users
FROM information_schema.processlist
GROUP BY host
ORDER BY conn_count DESC
LIMIT 20;
"

Bu sorgular size hem bağlantı kaynağını hem de olası sorunlu noktaları gösterir. Bir IP adresinden gelen aşırı bağlantı sayısı genellikle uygulama tarafında havuz yapılandırma sorununa işaret eder.

Kullanıcı Bazlı Bağlantı Limitleri

Tüm sistemi etkileyen global limiti artırmak yerine belirli kullanıcılar veya uygulamalar için özel limitler belirleyebilirsiniz. Bu özellikle multi-tenant yapılarda kritiktir:

mysql -u root -p -e "
-- Kullanıcı başına maksimum eş zamanlı bağlantı sayısı
ALTER USER 'webapp_user'@'%' 
WITH MAX_USER_CONNECTIONS 50;

-- Yeni kullanıcı oluştururken limit belirle
CREATE USER 'reporting_user'@'192.168.1.%' 
IDENTIFIED BY 'sifre' 
WITH MAX_USER_CONNECTIONS 5;

-- Mevcut limitleri kontrol et
SELECT user, host, max_user_connections 
FROM mysql.user 
WHERE max_user_connections > 0;
"

Bu yaklaşımla raporlama araçları ya da analiz scriptleri sistemin tamamını boğmaz, üretim uygulamaları için yeterli bağlantı her zaman mevcut kalır.

Performans İzleme ve Alerting

Sadece değerleri ayarlamak yetmez, bunları sürekli izlemek gerekir. Prometheus ve MySQL Exporter kombinasyonu bu iş için en yaygın tercih:

# MySQL Exporter kurulumu
wget https://github.com/prometheus/mysqld_exporter/releases/download/v0.14.0/mysqld_exporter-0.14.0.linux-amd64.tar.gz
tar xvf mysqld_exporter-0.14.0.linux-amd64.tar.gz
cd mysqld_exporter-0.14.0.linux-amd64

# Monitoring kullanıcısı oluştur
mysql -u root -p -e "
CREATE USER 'exporter'@'localhost' IDENTIFIED BY 'exporter_pass';
GRANT PROCESS, REPLICATION CLIENT, SELECT ON *.* TO 'exporter'@'localhost';
FLUSH PRIVILEGES;
"

# Exporter'ı başlat
echo "[client]
user=exporter
password=exporter_pass" > ~/.my.cnf

./mysqld_exporter --config.my-cnf ~/.my.cnf &

Grafana’da izlemeniz gereken temel metrikler:

  • mysql_global_status_threads_connected: Anlık bağlantı sayısı
  • mysql_global_status_max_used_connections: Tarihsel maksimum
  • mysql_global_variables_max_connections: Limit değeri
  • mysql_global_status_connection_errors_total: Reddedilen bağlantılar
  • mysql_global_status_aborted_connects: Başarısız bağlantı denemeleri

Yaygın Hatalar ve Çözümleri

“max_connections’ı artırdım ama hata devam ediyor”

Bu durumda işletim sistemi limitleri devreye girmiş olabilir. MySQL’in açabileceği dosya sayısını kontrol edin:

# Sistem limitlerini kontrol et
cat /proc/$(pidof mysqld)/limits | grep "open files"

# Servise özel limit ayarla
sudo systemctl edit mysql

Açılan editöre şunu ekleyin:

[Service]
LimitNOFILE = 65535

Ardından:

sudo systemctl daemon-reload
sudo systemctl restart mysql

/etc/security/limits.conf dosyasına da eklemek iyi bir pratik:

mysql soft nofile 65535
mysql hard nofile 65535

“Bağlantı sayısı düşük ama sunucu yine de yavaş”

Bu durumda bağlantı sayısı sorun değil, sorgular sorundur. Slow query log’u aktif edin:

[mysqld]
slow_query_log = 1
slow_query_log_file = /var/log/mysql/slow.log
long_query_time = 2
log_queries_not_using_indexes = 1

Sonuç

max_connections ayarı ilk bakışta basit bir parametre gibi görünse de veritabanı yönetiminin en kritik noktalarından birini oluşturuyor. Doğru yaklaşım sırayla şöyle olmalı: önce mevcut durumu ölç, sonra fiziksel kapasiteye göre hesapla, ardından ilgili diğer parametreleri de birlikte ayarla ve son olarak sürekli izle.

Sadece max_connections değerini artırmak çoğu zaman gerçek sorunu gizler. Aşırı bağlantı sayısının arkasında yanlış yapılandırılmış uygulama havuzları, gereksiz uzun ömürlü bağlantılar ya da optimize edilmemiş sorgular olabilir. ProxySQL gibi ara katmanlar ise özellikle büyük ölçekli sistemlerde bağlantı yönetimini tamamen farklı bir boyuta taşıyor.

Konfigürasyon dosyasına rakam girmek işin en kolay kısmı. Asıl beceri, o rakama nasıl ulaştığınızı ve sistemin bütününde ne anlama geldiğini bilmekte yatıyor.

Yorum yapın