Laravel uygulamanız geliştirme ortamında mükemmel çalışıyor. Ancak, birkaç yüz kullanıcı canlıda uygulamaya girdiğinde, sorgu süreleri hızla artar, sunucunuz yorulur ve neyin yanlış gittiğini anlamak için Telescope panosuna bakarken bulursunuz kendinizi. Veritabanı performans problemleri genellikle ilk başta dramatik değildir; sessizce oluşurlar ve ardından hızlıca kötüleşirler.
Bu makale, yüksek trafik alan Laravel uygulamaları için gerçekten etkili olan pratik veritabanı optimizasyon tekniklerini ele alıyor: hemen uygulayabileceğiniz, hemen ölçebileceğiniz ve canlı ortamda güvenle kullanabileceğiniz yöntemler.
1. N+1 Sorgularını Önceden Ortadan Kaldırın
1. N+1 Sorgularını Önceden Ortadan Kaldırın
N+1 problemi, Laravel performansının sessiz katilidir. Bir koleksiyonu yüklediğinizde ve ardından bir döngü içinde her bir öğe için ek bir sorgu tetiklediğinizde meydana gelir.
// ❌ N+1 Problemi — toplamda 1 + N sorgu çalıştırır
$posts = Post::all();
foreach ($posts as $post) {
echo $post->author->name; // her gönderi için ayrı sorgu
}
// ✅ Eager Loading — toplamda 2 sorgu çalıştırır
$posts = Post::with('author')->get();
foreach ($posts as $post) {
echo $post->author->name;
}
İç içe ilişkiler için, bunları bir zincirle kullanın:
$posts = Post::with(['author', 'comments.user', 'tags'])->paginate(20);
Geliştirme ortamında sorgu günlüğünü etkinleştirerek bu sorunları erken tespit edin:
\DB::enableQueryLog();
// ... kodunuzu çalıştırın
dd(\DB::getQueryLog());
Daha iyi bir seçenek olarak, Laravel Telescope veya Debugbar kullanın; her ikisi de yerel ortamınızda N+1 sorunlarını otomatik olarak işaretler.
2. Akıllıca ve Körlemesine İndeksleyin
2. Akıllıca ve Körlemesine İndeksleyin
İndeksler, okuma işlemlerini hızlandırır ancak yazma işlemlerini yavaşlatır. Amacınız, gerçekten filtrelediğiniz ve sıraladığınız sütunları indekslemek — her sütunu değil.
// Bir migration içinde
Schema::table('orders', function (Blueprint $table) {
$table->index('user_id'); // yabancı anahtar sorguları
$table->index('status'); // sık kullanılan WHERE koşulu
$table->index(['user_id', 'created_at']); // kullanıcı geçmişi sorguları için bileşik indeks
});
Bileşik İndeks Sütun Sırası Önemlidir
Bileşik İndeks Sütun Sırası Önemlidir
MySQL, indeksleri soldan sağa kullanır. Eğer sık sık WHERE user_id = ? AND status = ? sorgusu yapıyorsanız, bileşik indekste user_id‘yi ilk sıraya koyun; bu, daha yüksek kardinaliteye sahiptir ve sonuç kümesini daha hızlı daraltır.
İndekslerinizin kullanıldığını doğrulamak için, yavaş bir sorgunun önüne EXPLAIN koyun:
EXPLAIN SELECT * FROM orders WHERE user_id = 42 AND status = 'pending';
Eğer çıktıdaki type: ALL görüyorsanız, MySQL tam bir tablo taraması yapmaktadır — indeksiniz kullanılmıyor demektir.
3. Sorgu Kesitleri ve Tembel Koleksiyonlar Kullanın
3. Sorgu Kesitleri ve Tembel Koleksiyonlar Kullanın
Binden fazla satırı aynı anda işlemek bellek tüketimini aşındırır. Laravel, iki şık çözüm sunar.
// chunk() — kayıtları partiler halinde işler
User::where('subscribed', true)->chunk(500, function ($users) {
foreach ($users as $user) {
// her kullanıcıyı işleyin
}
});
// lazyById() — bellek verimli akış için bir imleç kullanır
User::where(, true)->lazyById()->each(function ($user) {
// tek bir seferde işleyin, düşük bellek tüketimi
});
Aynı zamanda toplu ekleme için, create() çağrılarını döngü içinde kullanmaktan kaçının. Bir denetim ile tek sorgu göndermek için insert() kullanın:
// ❌ 1000 ayrı INSERT sorgusu çalıştırır
foreach ($records as $record) {
Order::create($record);
}
// ✅ Birden fazla satırı içeren tek bir INSERT
Order::insert($records); // $records, dizilerden oluşan bir dizi
4. Doğru Katmanda Agresif Önbellekleme Yapın
4. Doğru Katmanda Agresif Önbellekleme Yapın
Tüm verilerin her istekte veritabanına gitmesi gerekmez. Laravel’in önbellek katmanı burada en iyi dostunuzdur.
// 10 dakika için pahalı bir toplama önbellekleme
$stats = Cache::remember(, 600, function () {
return Order::selectRaw()->whereMonth(, now()->month)->first();
});
Kullanıcı bazlı veri için, önbellek anahtarlarınızı adlandırın:
$key = {$userId}:;
$summary = Cache::remember($key, 300, fn() => $this->buildSummary($userId));
Redis kullanarak üretimde önbellek sürücünüzü ayarlayın — veritabanı veya dosya önbelleğinden çok daha hızlıdır ve atomik işlemler, pub/sub ile TTL’yi yerel olarak destekler.
CACHE_DRIVER=redis
REDIS_HOST=127.0.0.1
REDIS_PORT=6379
5. Sadece İhtiyacınız Olanları Seçin
5. Sadece İhtiyacınız Olanları Seçin
Bu basit bir kural ama çoğu zaman göz ardı ediliyor. SELECT *, büyük metin alanları, JSON blobları ve ihtiyaç duymadığınız zaman damgaları da içeren her sütunu getirir.
// ❌ Her sütunu getirir, bio, avatar_url, ayar JSON vb...
$users = User::all();
// ✅ Görünümün gerçekten kullandığı yalnızca al
$users = User::select(, , )->get();
Bu, bellek kullanımını ve veritabanı ile PHP süreci arasındaki ağ yükünü azaltır – özellikle büyük veri setleri ile belirgin hale gelir.
6. Büyük Tablolar için Sayfalamayı Optimize Edin
6. Büyük Tablolar için Sayfalamayı Optimize Edin
Standart paginate(), arka planda bir COUNT(*) sorgusu kullanır. Milyonlarca satırı olan tablolarda bu maliyetli hale gelir.
Sonuçları “daha fazla yükle” UI’ları için cursorPaginate() kullanın:
// Standart sayfalama — büyük tablolarda pahalı COUNT
$posts = Post::latest()->paginate(20);
// İmleç sayfalama — COUNT yok, son derece verimli
$posts = Post::latest()->cursorPaginate(20);
İmleç sayfalama, OFFSET yerine son kayda kodlanmış bir işaretçi kullanır; bu da performansın derinliğe göre tutarlı kalmasını sağlar.
7. Okuma Ağırlıklı İş Yükleri için Veritabanı Okuma Kopyalarını Kullanın
7. Okuma Ağırlıklı İş Yükleri için Veritabanı Okuma Kopyalarını Kullanın
Uygulamanız okuma ağırlıklıysa (çoğu uygulama öyledir), okuma sorgularını bir kopyaya yönlendirmek ve yazma işlemlerini ana sunucuya göndermek, ana veritabanı sunucunuz üzerindeki yükü dramatik bir şekilde azaltabilir.
Laravel, config/database.php‘de bunu yerel olarak destekler:
'mysql' => [
'read' => [
'host' => [, ], // yedek IP'ler
],
'write' => [
], // ana
],
'sticky' => true, // yazmaların hemen okunması için gereklidir
'driver' => ,
// ... diğer ayarlar
],
sticky = true ile, mevcut istekteki her yazma işlemi ana sunucudan okunur — çoğu zaman oluşabilecek replikasyon gecikmesini önler.
8. Öncelikle Profilleyin, İkinci Olarak Optimize Edin
8. Öncelikle Profilleyin, İkinci Olarak Optimize Edin
Üretimde bir şeyler değiştirmeden önce, ölçüm yapın. Dar boğazlar hakkında içgörü sahibi olmak genellikle yanıltıcıdır.
Kullanılacak yararlı araçlar:
- Laravel Telescope — sorgu süreleri, yavaş sorgular, N+1 tespiti
- Laravel Pulse — uygulamanızın toplu performans verileri
- Clockwork — PHP için tarayıcı geliştirici araçları tarzı profilleme
- MySQL Yavaş Sorgu Günlüğü — ayarlanabilir eşik üzerindeki sorguları yakalar
MySQL’de yavaş sorgu günlüğünü etkinleştirin:
SET GLOBAL slow_query_log = ;
SET GLOBAL long_query_time = 1; // 1 saniyeden fazla süren sorguları kaydet
SET GLOBAL slow_query_log_file = ;
Sonrasında mysqldumpslow veya pt-query-digest (Percona Toolkit’ten) kullanarak çıktıyı analiz edin.
Bu tür bir profil almanın öncelikli yaklaşımı, HanzWeb Ajansı’nda müşteri uygulamalarını denetlerken, en yavaş sorguların genellikle geliştiricilerin tahmin ettiği sorgular olmadığını görmekteyiz.
Sonuç
Sonuç
Veritabanı optimizasyonu tek seferlik bir görev değildir — sürekli bir disiplin gerektirir. En etkili başlangıç noktaları genellikle aynıdır: N+1 sorgularınızı düzeltin, doğru indeksleri ekleyin ve pahalı okuma işlemlerini Redis ile önbelleğe alın. Buralardan sonra, imleç sayfalama, seçimli sütunlar ve okuma kopyaları, trafik arttıkça kullanabileceğiniz diğer yöntemlerdir.
Önce ölçün, sonra optimize edin; Laravel’in yerleşik araçlarını kullanarak gerçek dar boğazları ortaya çıkarın ve dağıtım sürecinizin bir parçası olarak sorgu günlüklerini düzenli olarak inceleme alışkanlığı geliştirin. Veritabanınız size teşekkür edecek — kullanıcılarınız da aynı şekilde.
Kaynak: Orijinal Makale
- 1. N+1 Sorgularını Önceden Ortadan Kaldırın
- 2. Akıllıca ve Körlemesine İndeksleyin
- 3. Sorgu Kesitleri ve Tembel Koleksiyonlar Kullanın
- 4. Doğru Katmanda Agresif Önbellekleme Yapın
- 5. Sadece İhtiyacınız Olanları Seçin
- 6. Büyük Tablolar için Sayfalamayı Optimize Edin
- 7. Okuma Ağırlıklı İş Yükleri için Veritabanı Okuma Kopyalarını Kullanın
- 8. Öncelikle Profilleyin, İkinci Olarak Optimize Edin
- Sonuç


