Buyukweb
MySQL Performans Optimizasyonu: Index, Sorgu ve Cache Rehberi

MySQL Performans Optimizasyonu: Index, Sorgu ve Cache Rehberi

MySQL yavaşlığının gerçek nedenini bulmak için slow query log, EXPLAIN analizi ve index stratejisi. InnoDB buffer pool, my.cnf ayarları ve Redis cache entegrasyonu — Buyukweb cPanel ve VDS perspektifinden adım adım rehber.

Büyükweb Editör EkibiHosting, Sunucu ve Sistem Yönetimi Editörü12 dakika okuma

MySQL Performans Optimizasyonu: Index, Sorgu ve Cache — Adım Adım Rehber

Sayfanız birkaç saniyede açılıyor ama MySQL 100ms'de cevap vermesi gerekirken 3 saniyede mi veriyor? Sunucu CPU'su çıldırmış, web panelindeki "veritabanı yükü" çubuğu kırmızıya mı kaçtı? Büyük ihtimalle sorun sunucu donanımında değil — kötü yazılmış bir sorgu ya da eksik bir index yüzünden her istekte on binlerce satır taranıyor. Bu rehberde Buyukweb hosting altyapısı perspektifinden yavaş MySQL sorgusunu nasıl tespit eder, nasıl düzeltisiniz, adım adım anlatıyoruz.

Buyukweb perspektifi: cPanel paylaşımlı hosting paketlerimizde veritabanı motoru olarak MariaDB 10.6 LTS standart kuruludur. Slow query logları cPanel > phpMyAdmin > Status sekmesinden takip edilebilir; ama doğrudan MySQL komut satırı paylaşımlı hosting'de kısıtlıdır. VDS paketlerinde (₺250/ay'dan) root yetkisi tam verilir: mysqltuner.pl çalıştırabilir, my.cnf düzenleyebilir, performance_schema sorgulayabilirsiniz. VDS unmanaged çalışır — veritabanı tuning müşteri sorumluluğundadır.

Darboğazı Bulmadan Ayar Yapmayın

İnternette "MySQL hızlandırma" başlıklı pek çok içerik doğrudan innodb_buffer_pool_size ayarıyla başlar. Oysa o ayarı yapmadan önce neyin yavaş olduğunu bilmek gerekir. Çünkü sorun buffer pool olmayabilir; tek bir tabloda düzgün index olmadığı için tüm sunucu dizlerinin üstüne çökmüş olabilir.

Tanı sırası şu şekilde olmalı:

  1. Slow query log ile yavaş sorguları bul
  2. EXPLAIN ile o sorguların tarama şeklini gör
  3. Eksik index ekle — genellikle bu tek adım %80 çözümü getirir
  4. Gerekiyorsa InnoDB buffer pool ve diğer my.cnf parametrelerini ayarla
  5. Cache katmanı (Redis/Memcached) ihtiyacı varsa son aşamada ekle

Adım 1: Slow Query Log — Kim Suçlu?

VDS'de Aktif Etme

-- Canlı olarak aktif et (restart gerekmez)
SET GLOBAL slow_query_log = 'ON';
SET GLOBAL long_query_time = 1.0;   -- 1 saniyeden uzun sorguları yakala
SET GLOBAL slow_query_log_file = '/var/log/mysql/slow.log';
SET GLOBAL log_queries_not_using_indexes = 'ON';

my.cnf'de kalıcı hale getirme:

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

Buyukweb VDS notu: AlmaLinux 9 / Ubuntu 22.04 / Debian 12 paketlerimizde MariaDB 10.11 veya MySQL 8.0 kurulabilir. my.cnf yolu değişebilir: /etc/my.cnf, /etc/mysql/mariadb.conf.d/50-server.cnf veya /etc/mysql/mysql.conf.d/mysqld.cnf. mysql --help | grep my.cnf ile doğru yolu bulabilirsiniz.

Log Dosyasını Analiz Etme

# En uzun süren 10 sorgu
mysqldumpslow -s t -t 10 /var/log/mysql/slow.log

# En çok tekrarlanan 10 sorgu
mysqldumpslow -s c -t 10 /var/log/mysql/slow.log

# Daha kapsamlı analiz: pt-query-digest (percona-toolkit)
pt-query-digest /var/log/mysql/slow.log | head -100

Çıktıda Query_time: 4.3 ve Rows_examined: 450000 gibi satırlar görüyorsanız — bu sorgu her çalıştığında 450 bin satıra dokunuyor. Tek bir index ile bu sayı birkaç yüze düşebilir.

cPanel'de phpMyAdmin ile Slow Query Kontrolü

VDS olmadan, paylaşımlı hosting müşterileri için:

cPanel > Veritabanları > phpMyAdmin > [veritabanı] > Durum (Status) sekmesi
→ "Queries: slow queries" satırına bakın

Ayrıca phpMyAdmin > SQL sekmesinde SHOW GLOBAL STATUS LIKE 'Slow%'; komutuyla anlık sayıyı görebilirsiniz.

Adım 2: EXPLAIN ile Sorgu Plan Analizi

Slow query log'da yakaladığınız sorguya bakın — önüne EXPLAIN koyun, çalıştırın:

EXPLAIN SELECT * FROM wp_posts
  WHERE post_type = 'product'
    AND post_status = 'publish'
  ORDER BY post_date DESC
  LIMIT 20;
EXPLAIN Kolonu Dikkat Edin
type: ALL Tam tablo taraması — tüm satırlara dokunuyor, çok kötü
type: index Index sırasıyla tam tarama — yine yavaş
type: range Index aralığı — kabul edilebilir
type: ref Index eşleşme — iyi
type: eq_ref / const Tek satır eşleşme — en iyi
key: NULL Hiç index kullanılmamış
rows: 500000 Tahmin edilen taranacak satır sayısı — yüksekse sorun var
Extra: Using filesort Sıralama için geçici disk/bellek kullanımı — index ile giderilebilir
Extra: Using temporary Geçici tablo oluşturuluyor — ciddi kaynak tüketimi

MySQL 8.0 ve MariaDB 10.9+ için daha ayrıntılı:

EXPLAIN ANALYZE SELECT ...;  -- MySQL 8.0 / MariaDB 10.9+
EXPLAIN FORMAT=JSON SELECT ...;  -- JSON çıktı

EXPLAIN ANALYZE hem planı hem gerçek çalışma süresini verir; plan ile gerçek arasındaki fark büyükse istatistikler eski demektir — ANALYZE TABLE tablo_adi; çalıştırın.

Adım 3: Index Stratejisi — Sorunun %80'ini Çözen Adım

Hangi Sütunlara Index?

  • WHERE koşullarında sık kullanılan sütunlar
  • JOIN bağlantısında kullanılan foreign key sütunlar
  • ORDER BY ve GROUP BY sütunları
  • Yüksek kardinaliteli sütunlar (çok sayıda benzersiz değer: email, ID, tarih)
  • Düşük kardinaliteli sütunlar (evet/hayır, durum: 3-4 değer) — genellikle tek başına index değerini taşımaz, bileşik index içinde işe yarar

Temel Index Örnekleri

-- Tek sütun
ALTER TABLE wp_posts ADD INDEX idx_post_type (post_type);

-- Bileşik (composite) — sıra önemli: önce eşitlik, sonra aralık/sıralama
ALTER TABLE wp_posts ADD INDEX idx_type_status_date
  (post_type, post_status, post_date);

-- Benzersiz zorunluluğu olan sütun
ALTER TABLE kullanicilar ADD UNIQUE INDEX idx_email (email);

-- Covering index: sorgu tüm veriyi index'ten alır, tabloya dokunmaz
ALTER TABLE siparis_detay ADD INDEX idx_cover
  (siparis_id, urun_id, miktar, fiyat);

Covering index ne zaman kullanılır? EXPLAIN çıktısında Extra: Using index görüyorsanız — MySQL tüm veriyi doğrudan index'ten alıyor, tabloya hiç gitmiyor. Bu en verimli yol.

Index Analizi — Kullanılmayanları Temizle

Index de maliyetsiz değil: yazma operasyonlarında (INSERT, UPDATE, DELETE) her index güncellenir. Gereksiz index yazma yavaşlığı yaratır.

-- Mevcut indexleri gör
SHOW INDEX FROM wp_posts;

-- Kullanılmayan indexleri bul (MySQL 8.0 sys şeması)
SELECT object_schema, object_name, index_name
FROM sys.schema_unused_indexes
WHERE object_schema NOT IN ('mysql', 'performance_schema', 'sys');

-- Index istatistiklerini güncelle
ANALYZE TABLE wp_posts;

-- Gereksiz index sil
DROP INDEX idx_eski ON wp_posts;

WooCommerce ve WordPress'te Sık Karşılaşılan Index Eksikleri

-- wp_postmeta üzerinde meta_key + meta_value arama için
ALTER TABLE wp_postmeta ADD INDEX idx_meta_key_value
  (meta_key, meta_value(20));

-- wp_posts'ta post_type + post_status + post_date birlikte kullanılıyor
ALTER TABLE wp_posts ADD INDEX idx_type_status_date
  (post_type, post_status, post_date);

-- wp_options'ta autoload sorgulama
ALTER TABLE wp_options ADD INDEX idx_autoload (autoload);

Adım 4: InnoDB Buffer Pool ve my.cnf Ayarları

Buffer pool, InnoDB'nin veri ve index sayfalarını RAM'de tuttuğu havuzdur. Diskten okumak yerine RAM'den okumak 100-1000 kat daha hızlıdır. Hit ratio (oran) %99+ olmalı; düşükse buffer pool yetersizdir.

-- Anlık buffer pool hit ratio
SELECT
  ROUND((1 - (
    (SELECT VARIABLE_VALUE FROM performance_schema.global_status
     WHERE VARIABLE_NAME = 'Innodb_buffer_pool_reads') /
    (SELECT VARIABLE_VALUE FROM performance_schema.global_status
     WHERE VARIABLE_NAME = 'Innodb_buffer_pool_read_requests')
  )) * 100, 2) AS hit_ratio_pct;
-- Hedef: 99.0 ve üzeri

VDS'de Önerilen my.cnf Bloğu

[mysqld]
# RAM'in %50-70'i — 8 GB RAM'li VDS için
innodb_buffer_pool_size         = 5G
innodb_buffer_pool_instances    = 4    # Her instance 1.25 GB

# Redo log (MariaDB 10.5+ / MySQL 8.0+ değerleri)
innodb_log_file_size            = 512M
innodb_flush_log_at_trx_commit  = 2    # 0: en hızlı / 1: ACID / 2: denge

# I/O — NVMe SSD için yüksek tut
innodb_io_capacity              = 2000
innodb_io_capacity_max          = 4000
innodb_flush_method             = O_DIRECT

# Bağlantı ve geçici tablo
max_connections                 = 300
thread_cache_size               = 64
tmp_table_size                  = 256M
max_heap_table_size             = 256M

# Eski query cache — MySQL 8.0 ve MariaDB 10.3+ artık deprecated/removed
# Açık bırakmayın: contention yaratır
query_cache_type                = 0
query_cache_size                = 0

Önemli: innodb_flush_log_at_trx_commit = 1 tam ACID garantisi sağlar ama her COMMITte diske yazar. E-ticaret veya finansal uygulama için 1 kullanın. Blog/içerik sitesi için 2 makul denge. 0 en hızlı ama sunucu çökmesinde son saniyedeki işlemler kaybolabilir.

mysqltuner.pl — Hazır Analiz Aracı (VDS)

# İndirip çalıştır
wget https://raw.githubusercontent.com/major/MySQLTuner-perl/master/mysqltuner.pl
perl mysqltuner.pl --user root --pass SIFRE

mysqltuner.pl çalışan sunucuya bakarak innodb_buffer_pool_size, max_connections, query_cache gibi parametreler için somut öneriler verir. Sunucu en az 24 saat çalışmış olmalı ki anlamlı istatistik toplansın.

Adım 5: performance_schema ile Derinlemesine Tanı

performance_schema MySQL 5.6 / MariaDB 10.0'dan beri varsayılan gelen hafıza içi tanı sistemidir. Overhead çok düşük (%1-3 CPU).

-- En yavaş sorgu özetleri (MySQL 8.0 ve MariaDB 10.5+)
SELECT
  DIGEST_TEXT,
  COUNT_STAR                       AS calisma_sayisi,
  ROUND(AVG_TIMER_WAIT / 1e9, 2)  AS ort_ms,
  ROUND(SUM_TIMER_WAIT / 1e9, 2)  AS toplam_ms
FROM performance_schema.events_statements_summary_by_digest
ORDER BY SUM_TIMER_WAIT DESC
LIMIT 10;

-- Tablo I/O — hangi tablo en çok kaynak tüketiyor?
SELECT object_schema, object_name,
  ROUND(SUM_TIMER_WAIT / 1e12, 2) AS wait_saniye
FROM performance_schema.table_io_waits_summary_by_table
WHERE object_schema NOT IN ('mysql', 'performance_schema', 'sys')
ORDER BY SUM_TIMER_WAIT DESC LIMIT 10;

Adım 6: Sorgu Yazım Hataları — Gözden Kaçan 5 Tuzak

Index doğru olsa da sorgu yanlış yazılırsa index kullanılmaz:

-- 1. Fonksiyon ile wrap edilen sütun → index çalışmaz
-- KÖTÜ:
WHERE YEAR(siparis_tarihi) = 2025
-- İYİ:
WHERE siparis_tarihi >= '2025-01-01' AND siparis_tarihi < '2026-01-01'

-- 2. LIKE sorgusunda baştaki joker → index çalışmaz
-- KÖTÜ:
WHERE urun_adi LIKE '%çanta%'
-- İYİ (sadece önek arama):
WHERE urun_adi LIKE 'çanta%'
-- LIKE '%...%' için MySQL FULLTEXT index veya Elasticsearch kullanın

-- 3. Örtük tip dönüşümü → index atlanır
-- Tablo: user_id INT, sorguda string geliyor
-- KÖTÜ:
WHERE user_id = '42'   -- '42' string, MySQL dönüştürür ama index kaçırabilir
-- İYİ:
WHERE user_id = 42

-- 4. OR ile farklı sütunlar → genellikle tam tablo taraması
-- KÖTÜ:
WHERE kategori_id = 5 OR etiket_id = 12
-- İYİ:
SELECT ... WHERE kategori_id = 5
UNION ALL
SELECT ... WHERE etiket_id = 12

-- 5. SELECT * yerine sadece ihtiyaç duyulan sütunlar
-- KÖTÜ (covering index kullanılamaz):
SELECT * FROM wp_posts WHERE post_type = 'product'
-- İYİ:
SELECT ID, post_title, post_date FROM wp_posts WHERE post_type = 'product'

Cache Katmanı: Ne Zaman Redis, Ne Zaman Veritabanı Yeterli?

MySQL'i ne kadar optimize ederseniz edin, her istek için veritabanına gitmek gereksizdir. Statik ya da seyrek değişen verileri cache'de tutmak hem MySQL yükünü hem de sayfa açılma süresini dramatik biçimde düşürür.

Redis — Session Store ve Uygulama Cache için

# Ubuntu / Debian'da Redis kurulumu (VDS)
apt install redis-server -y
systemctl enable --now redis-server

# /etc/redis/redis.conf
maxmemory 512mb
maxmemory-policy allkeys-lru
// PHP'de Redis ile basit cache
$redis = new Redis();
$redis->connect('127.0.0.1', 6379);

$cacheKey = 'anasayfa_son_urunler';
$ttl = 300; // 5 dakika

$data = $redis->get($cacheKey);
if (!$data) {
    // Veritabanından çek
    $data = json_encode(veritabanindan_cek());
    $redis->setex($cacheKey, $ttl, $data);
}
$sonuclar = json_decode($data, true);

WordPress'te Redis için object cache eklentileri (W3 Total Cache, LiteSpeed Cache Redis entegrasyonu) Buyukweb cPanel'de aktif edilebilir — destek hattından talep edin.

Ne Zaman MySQL Yeterli, Ne Zaman Redis?

Senaryo Tercih
E-ticaret ürün listesi (5 dk değişmiyor) Redis cache
Sipariş durumu (anlık tutarlılık şart) Doğrudan MySQL
Session depolama Redis (MySQL'de lock contention yaratır)
Sayfa sayacı, beğeni sayısı Redis incr() → MySQL'e periyodik yaz
Blog yazısı (saatler sabit) Sayfa cache yeterli (LiteSpeed Cache)
Kullanıcı giriş/yetki kontrolü Redis ile TTL'li cache

Buyukweb perspektifi: VDS paketlerinde Redis'i kendiniz kuruyorsunuz — tam kontrol sizde. cPanel paylaşımlı hosting'de Redis eklentisinin aktif olması gerekir. Sorgulamak için 0850 302 60 70'i arayın. Tercih etmeyin: Session'ı MySQL'de tutmayı — yüksek trafikte lock satırları oluşturur, "veritabanı yanıt vermiyor" hatalarına yol açar.

WordPress ve WooCommerce için Pratik Bakım Sorguları

-- wp_options autoload boyutu
SELECT ROUND(SUM(LENGTH(option_value)) / 1024 / 1024, 2) AS mb
FROM wp_options WHERE autoload = 'yes';

-- Süresi dolmuş transient temizleme
DELETE FROM wp_options
WHERE option_name LIKE '_transient_%'
  AND option_value < UNIX_TIMESTAMP();

-- Eski revisionları sil
DELETE FROM wp_posts WHERE post_type = 'revision';

-- Tablo optimize et (düşük trafikte çalıştırın)
OPTIMIZE TABLE wp_options, wp_posts, wp_postmeta;

Sıkça Sorulan Sorular

Buyukweb cPanel'de slow query log açabilir miyim?

Paylaşımlı hosting'de MySQL/MariaDB global değişkenlerini değiştirme yetkisi yoktur. Ancak phpMyAdmin > Durum (Status) sekmesinden Slow_queries sayacını görebilirsiniz. Hangi sorgunun yavaş olduğunu daraltmak için uygulamanızda sorgu sürelerini loglamanız (Laravel Telescope, WordPress Query Monitor eklentisi) en pratik yol.

Bileşik index'te sütun sırası neden önemli?

MySQL sol-önek kuralına göre çalışır: (a, b, c) index'ini WHERE a=1, WHERE a=1 AND b=2 veya WHERE a=1 AND b=2 AND c=3 sorgularında kullanır. Ama WHERE b=2 sorgusunda bu index kullanılmaz. Sıralamanın en başına kardinalitesi yüksek ve WHERE'de sık geçen sütunu koyun.

InnoDB buffer pool'u ne kadar ayarlamalıyım?

Genel kural: toplam RAM'in %50-70'i. 8 GB RAM'li VDS için 4-5 GB. Ama önce performance_schema veya mysqltuner.pl çıktısına bakın — "InnoDB buffer pool hit ratio" %99'un altındaysa artırın, üstündeyse mevcut değer yeterlidir. Fazla vermek OS ve uygulama için yetersiz bellek bırakır.

MySQL 8.0'daki query cache'i açmalı mıyım?

Hayır. MySQL 8.0'da query cache tamamen kaldırıldı. MariaDB 10.3+ için de query_cache_type = 0 ile kapatın — müdahale gerektiren kilit mekanizması yazma yoğun iş yüklerinde performans düşürür. Uygulama seviyesinde Redis ile cache yapın.

EXPLAIN'de "Using filesort" görüyorum, kötü mü?

Her zaman değil — küçük result set için MySQL bellekte sort yapar, hızlıdır. Sorun EXPLAIN çıktısında rows sayısı yüksekken "Using filesort" varsa: o sıralama için yüz binlerce satır işleniyor demektir. Çözüm: ORDER BY sütunlarını bileşik index'e ekleyin.

pt-query-digest ile mysqldumpslow arasındaki fark ne?

mysqldumpslow MariaDB/MySQL'in içinde gelen temel araç — hızlı ve basit. pt-query-digest ise percona-toolkit'in parçası: query fingerprintleme, zaman dilimlerine göre filtreleme, yüzdelik istatistikler üretir. Büyük ve aktif sistemlerde pt-query-digest çok daha ayrıntılı rapor verir. VDS'de kurulum:

# AlmaLinux / RHEL 9
yum install percona-toolkit -y

# Ubuntu / Debian
apt install percona-toolkit -y

pt-query-digest /var/log/mysql/slow.log --since 3h | head -200

Session'ları Redis'e taşımanın MySQL'e faydası var mı?

Evet — özellikle yüksek eş zamanlı kullanıcı (100+) olan sitelerde büyük fark eder. Her sayfa yüklemede PHP oturumu MySQL'de güncelliyorsa bu UPDATE satırları row-level lock üretir. Redis'e taşındığında MySQL yalnızca gerçek iş mantığı sorgularına odaklanır; ortalama sorgu süresi ve bağlantı sayısı düşer.

Sonuç

MySQL veya MariaDB performans optimizasyonu "en büyük sunucu al, sorun çözülür" mantığıyla olmaz. Çözüm sırası: slow query log → EXPLAIN → index → my.cnf → cache. Adımları atlarsanız yanlış sorunu çözürsünüz. Buyukweb cPanel hosting'de phpMyAdmin ve Query Monitor ile temel tanı yapılabilir; daha ileri seviye tuning için root yetkili VDS gereklidir. Redis cache katmanı ile MySQL'in sırtındaki yükü önemli ölçüde azaltmak mümkün — ama önce hangi sorgular yavaş, onu bulun.

Teknik destek için: 0850 302 60 70.


İlgili Büyükweb Hizmetleri

Veritabanı yoğun uygulamalar için altyapı seçeneklerimiz:

Sorularınız için 0850 302 60 70 numaralı destek hattımıza veya iletişim sayfamıza yazabilirsiniz.

Veritabanı Yönetimi İlgili Hizmetlerimiz

Bu yazıda anlatılan teknik konuyu profesyonel altyapıyla deneyimleyin

Etiketler:

#mysql#performans optimizasyonu#optimizasyon#veritabanı indeks#veritabanı#database

Bu yazıyı paylaş