Autor del tema
#0
Bir web uygulamasının kalbi genellikle veritabanıdır, değil mi? PHP ile geliştirme yaparken MySQL'in gücünden faydalanıyoruz, ancak zamanla, özellikle trafik arttıkça veya veri hacmi büyüdükçe, performans sorunları kaçınılmaz hale gelebiliyor. "Sayfalar yavaş açılıyor," "sorgular çok uzun sürüyor," gibi şikayetler hepimizin başına gelmiştir. Peki, bu darboğazları nasıl aşarız, uygulamalarımızı nasıl daha hızlı ve ölçeklenebilir hale getiririz? Gelin, bu konuyu derinlemesine inceleyelim.
En temelden başlayalım: İndeksleme. Bir veritabanı tablosundaki indeksler, adeta bir kitabın içindekiler veya dizin sayfası gibidir. Ne kadar büyük olursa olsun, aradığınız bilgiye çok daha hızlı ulaşmanızı sağlar. Düşünsenize, 1 milyon satırlık bir tabloda belirli bir kullanıcı ID'sini arıyorsunuz... İndeks yoksa, MySQL tüm satırları tek tek taramak zorunda kalır, ki bu da tam bir felaket. Oysa doğru bir indeksle, saniyeler içinde sonuca ulaşırsınız. Peki, nerede kullanmalıyız bu indeksleri? Genellikle, , ve cümlelerinde sıkça kullandığımız sütunlarda indeks oluşturmak akıllıca bir hareket. Örneğin, bir tablosunda sütununda sıkça arama yapıyorsanız veya sütununa göre sıralama yapıyorsanız, bu sütunlara indeks eklemek uygulamanızın nefes almasını sağlar. Tabii her sütuna indeks eklemek de çözüm değil, çünkü indeksler de yer kaplar ve , , işlemlerini yavaşlatır. Bu yüzden altın kural: İhtiyaç duyulan yerlere, doğru indeksleri atmak.
Şu komutunu kullanmayı alışkanlık haline getirin. Bir sorgunun nasıl çalıştığını, hangi indeksleri kullandığını veya kullanmadığını, kaç satır taradığını size açıkça gösterir. Bu, sorgu optimizasyonunun olmazsa olmazıdır.
Gelelim Sorgu Optimizasyonuna. Bu konuda yapılabilecek o kadar çok şey var ki...
Veritabanı Yapısı ve Normalizasyon da kritik bir konu.
Şimdi PHP tarafına, yani Uygulama Seviyesi Optimizasyonlarına geçelim. MySQL ne kadar hızlı olursa olsun, PHP kodunuz kötü yazılmışsa yine de yavaşlık yaşarsınız.
Caching konusu, performans optimizasyonunun en güçlü silahlarından biri.
Sunucu Seviyesi Optimizasyonları da göz ardı edilmemeli.
Son olarak, İzleme ve Analiz olmadan performans optimizasyonu körlemesine ilerlemek gibidir.
Performans optimizasyonu tek seferlik bir iş değildir, sürekli bir süreçtir. Uygulamanız büyüdükçe, kullanıcı sayısı arttıkça veya yeni özellikler eklendikçe, yeni darboğazlar ortaya çıkacaktır. Bu yüzden düzenli olarak izlemek, test etmek ve optimize etmek gerekiyor.
Peki, sizin bu konudaki tecrübeleriniz neler? Hangi optimizasyon teknikleri sizin için en çok işe yaradı? Ya da karşılaştığınız en ilginç performans sorunu neydi ve nasıl çözdünüz? Deneyimlerinizi ve önerilerinizi merak ediyorum...
En temelden başlayalım: İndeksleme. Bir veritabanı tablosundaki indeksler, adeta bir kitabın içindekiler veya dizin sayfası gibidir. Ne kadar büyük olursa olsun, aradığınız bilgiye çok daha hızlı ulaşmanızı sağlar. Düşünsenize, 1 milyon satırlık bir tabloda belirli bir kullanıcı ID'sini arıyorsunuz... İndeks yoksa, MySQL tüm satırları tek tek taramak zorunda kalır, ki bu da tam bir felaket. Oysa doğru bir indeksle, saniyeler içinde sonuca ulaşırsınız. Peki, nerede kullanmalıyız bu indeksleri? Genellikle
CODE
1WHERECODE
1JOINCODE
1ORDER BYCODE
1GROUP BYCODE
1usersCODE
1emailCODE
1created_atCODE
1INSERTCODE
1UPDATECODE
1DELETECODE
123
CREATE INDEX idx_users_email ON users (email);
CREATE INDEX idx_products_category_price ON products (category_id, price);
Şu
CODE
1EXPLAINGelelim Sorgu Optimizasyonuna. Bu konuda yapılabilecek o kadar çok şey var ki...
- kullanmaktan kaçının: Gerçekten ihtiyacınız olan sütunları belirtin. Neden mi? ÇünküCODE
1
SELECT [i]kullanmak, istemediğiniz verileri de veritabanından çekmek demek. Bu hem ağ trafiğini artırır hem de MySQL'in daha fazla veri okumasına neden olur. Diyelim ki sadece kullanıcının adını ve soyadını istiyorsunuz, o zamanCODE1
[/i]yazın. Basit ama etkili.CODE1
SELECT first_name, last_name FROM users
- vs. Alt Sorgular: GenellikleCODE
1
JOINişlemleri alt sorgulara göre daha performanslıdır, çünkü MySQLCODE1
JOINiçin daha optimize edilmiş algoritmalar kullanır. Ancak her zaman değil. BazenCODE1
JOINile kontrol etmek, büyük tablolarlaCODE1
EXISTSyapmaktan daha iyi olabilir. Duruma göre değerlendirmek lazım.CODE1
JOIN
- cümlelerindeCODE
1
WHEREyerineCODE1
ORveyaCODE1
UNIONkullanmak: Özellikle karmaşıkCODE1
INkoşulları, indeks kullanımını engelleyebilir.CODE1
ORveyaCODE1
UNION ALLile sorguyu parçalamak, MySQL'in indeksleri daha verimli kullanmasını sağlayabilir.CODE1
IN
- veCODE
1
LIMITile sayfalama: Büyük veri setlerinde sayfalama yaparkenCODE1
OFFSETdeğeri çok büyüdüğünde performans düşer. Neden mi? Çünkü MySQL,CODE1
OFFSETkadar satırı atlamak için yine de okumak zorunda kalır. Bu durumda, son görülen ID'yi kullanarak bir sonraki sayfayı getirmek (CODE1
OFFSET) çok daha verimli bir yaklaşımdır.CODE1
WHERE id > last_seen_id LIMIT N
- Fonksiyonları cümlelerinde kullanmaktan kaçının:CODE
1
WHEREgibi bir ifade,CODE1
WHERE DATE(created_at) = '2023-01-01'sütunundaki indeksi kullanamaz. Çünkü MySQL her satır içinCODE1
created_atfonksiyonunu çalıştırmak zorunda kalır. Bunun yerine,CODE1
DATE()gibi bir aralık belirtmek, indeksi aktif hale getirir.CODE1
WHERE created_at >= '2023-01-01 00:00:00' AND created_at < '2023-01-02 00:00:00'
Veritabanı Yapısı ve Normalizasyon da kritik bir konu.
- Doğru veri tiplerini seçin: Bir sütun için yeterliykenCODE
1
INTkullanmak,CODE1
BIGINTyerineCODE1
VARCHAR(255)kullanmak gereksiz yere yer kaplar ve performansı olumsuz etkiler. Mümkün olan en küçük ve en uygun veri tipini kullanın.CODE1
TEXT
- Normalizasyon seviyesi: Veritabanı tasarımında normalizasyon, veri tekrarını azaltmak ve tutarlılığı artırmak için önemlidir. Ancak bazen, özellikle yoğun okuma yapılan sistemlerde, denormalizasyon yapmak performans artışı sağlayabilir. Örneğin, bir kullanıcının toplam sipariş sayısını her seferinde hesaplamak yerine, bu bilgiyi tablosunda bir sütunda tutmak ve güncel tutmak, okuma performansını artırabilir. Tabii bu, yazma işlemlerini biraz daha karmaşık hale getirir. Bir denge bulmak şart.CODE
1
users
- vs. Lookup Tabloları: Sabit ve az sayıda seçenek içeren alanlar içinCODE
1
ENUMkullanmak yer kazandırabilir. Ancak bu seçenekler zamanla değişebilecekse veya daha fazla detay içerecekse, ayrı bir lookup tablosu kullanmak daha esnektir ve veritabanı tutarlılığı açısından daha iyidir.CODE1
ENUM
Şimdi PHP tarafına, yani Uygulama Seviyesi Optimizasyonlarına geçelim. MySQL ne kadar hızlı olursa olsun, PHP kodunuz kötü yazılmışsa yine de yavaşlık yaşarsınız.
- Veritabanı Bağlantıları: Her istekte yeni bir veritabanı bağlantısı açıp kapatmak bir maliyettir. Persistent bağlantılar () bu maliyeti azaltabilir, ancak dikkatli kullanılmalı. Yanlış kullanıldığında kaynak sızıntılarına veya beklenmedik durumlara yol açabilirler. Genellikle, modern framework'ler ve container'lar bu durumu daha iyi yönetir.CODE
1
PDO::ATTR_PERSISTENT => true
- Veri Çekme ve İşleme: Büyük sonuç kümeleriyle çalışırken tüm veriyi tek seferde belleğe yüklemek yerine, satır satır işlemek (metotlarını kullanırken dikkatli olmak) bellek kullanımını azaltır. Özellikle milyonlarca satırı işleyen batch script'lerde bu hayati önem taşır.CODE
1
fetch
- N+1 Sorgu Problemi: ORM kullanıyorsanız, bu sorunla karşılaşma olasılığınız yüksek. Bir ana sorguyla N adet kayıt çekip, sonra her kayıt için ayrı ayrı detaylarını çekmek N+1 sorgu problemidir. Örneğin, 100 kullanıcı çekip, her kullanıcının en son siparişini ayrı bir sorguyla çekmek 101 sorgu demektir. veya eager loading tekniklerini kullanarak bu 101 sorguyu tek bir sorguya indirmek mümkündür.CODE
1
JOIN
Caching konusu, performans optimizasyonunun en güçlü silahlarından biri.
- OPcode Önbellekleme (OPcache): PHP kodunuz her çalıştığında derlenir. OPcache, derlenmiş kodu bellekte tutarak bu derleme adımını atlar. Bu, saf PHP performansı için olmazsa olmazdır. Neredeyse tüm modern PHP kurulumlarında varsayılan olarak gelir ve düzgün yapılandırıldığından emin olunması gerekir.
- Uygulama Seviyesi Önbellekleme (Redis, Memcached): Sıkça okunan ama nadiren değişen verileri (kullanıcı profilleri, ayarlar, ürün listeleri vb.) veritabanından çekmek yerine Redis veya Memcached gibi hızlı anahtar-değer depolarında önbelleğe almak, veritabanı üzerindeki yükü inanılmaz derecede azaltır. Bu, uygulamanızın ölçeklenebilirliğini doğrudan etkiler.
- Veritabanı Önbelleği (MySQL Query Cache - Eskidi!): MySQL'in eski sürümlerinde bir sorgu önbelleği vardı. Ancak bu önbellek, bir tabloda küçücük bir değişiklik olduğunda bile tüm önbelleği geçersiz kıldığı için çoğu zaman performansı artırmaktan çok düşürüyordu. Bu yüzden MySQL 5.7.20'den itibaren kullanımdan kaldırıldı ve MySQL 8.0'dan itibaren tamamen kaldırıldı. Artık bu işi uygulama seviyesinde Redis/Memcached ile veya daha düşük seviyede InnoDB Buffer Pool ile yapıyoruz.
Sunucu Seviyesi Optimizasyonları da göz ardı edilmemeli.
- MySQL Yapılandırması ():CODE
1
my.cnf
- : InnoDB tablolarının ve indekslerinin bellekte tutulduğu alandır. Genellikle sunucudaki RAM'in %50-%80'i kadar ayarlanması önerilir. Bu, disk I/O'yu büyük ölçüde azaltır.CODE
1
innodb_buffer_pool_size
- : Aynı anda kaç bağlantıya izin verileceğini belirler. Uygulamanızın ihtiyaçlarına göre ayarlanmalı. Çok düşük olması bağlantı reddine, çok yüksek olması kaynak tüketimine yol açabilir.CODE
1
max_connections
- : Belirli bir süreden daha uzun süren sorguları loglamanızı sağlar. Performans darboğazlarını tespit etmek için paha biçilmez bir araçtır. Mutlaka aktif edin ve düzenli olarak kontrol edin.CODE
1
slow_query_log
- PHP Yapılandırması ():CODE
1
php.ini
- : Bir PHP script'inin kullanabileceği maksimum bellek miktarını belirler. Büyük veri setleriyle çalışırken veya karmaşık işlemler yaparken bu limiti artırmak gerekebilir.CODE
1
memory_limit
- : Bir script'in çalışmasına izin verilen maksimum süredir. Uzun süren işlemler için artırılması gerekebilir, ancak sonsuz döngüleri veya hatalı kodları maskelememesi için dikkatli olunmalı.CODE
1
max_execution_time
- Donanım ve Altyapı:
- SSD kullanımı: Veritabanı sunucularında SSD diskler, geleneksel HDD'lere göre kat kat daha hızlı I/O performansı sunar. Bu, özellikle disk tabanlı işlemleri yoğun olan veritabanları için devasa bir fark yaratır.
- Yeterli RAM: MySQL'in verileri ve indeksleri bellekte tutabilmesi için yeterli RAM'e sahip olmak, disk okuma ihtiyacını azaltarak performansı artırır.
Son olarak, İzleme ve Analiz olmadan performans optimizasyonu körlemesine ilerlemek gibidir.
- komutu: Tekrar ediyorum, her sorgunuzu analiz etmek için kullanın.CODE
1
EXPLAIN
- Slow Query Log: Periyodik olarak kontrol ederek optimize edilmesi gereken sorguları tespit edin.
- Percona Toolkit: Özellikle gibi araçları kullanarak slow query loglarını daha anlamlı raporlara dönüştürebilirsiniz.CODE
1
pt-query-digest
- APM (Application Performance Monitoring) Araçları: New Relic, Datadog, Blackfire gibi araçlar, uygulamanızın ve veritabanınızın performansını gerçek zamanlı olarak izlemenizi, darboğazları kolayca tespit etmenizi sağlar.
- Xdebug: Geliştirme ortamında PHP kodunuzun performansını profilleyerek, hangi satırların veya fonksiyonların daha fazla zaman aldığını görebilirsiniz.
Performans optimizasyonu tek seferlik bir iş değildir, sürekli bir süreçtir. Uygulamanız büyüdükçe, kullanıcı sayısı arttıkça veya yeni özellikler eklendikçe, yeni darboğazlar ortaya çıkacaktır. Bu yüzden düzenli olarak izlemek, test etmek ve optimize etmek gerekiyor.
Peki, sizin bu konudaki tecrübeleriniz neler? Hangi optimizasyon teknikleri sizin için en çok işe yaradı? Ya da karşılaştığınız en ilginç performans sorunu neydi ve nasıl çözdünüz? Deneyimlerinizi ve önerilerinizi merak ediyorum...