PERFORMANS ANALİZİ Optimizasyon Planı 6 Ocak 2026

Dinamik Sistem Performans Analizi

JSON Field, Cache, Index - Sistemi Yavaşlatan Her Şeyi Analiz Ediyoruz!

📝 Basit Anlatım (Herkes İçin)

Neden Yavaşlayabilir?

1. Her Sayfa Yüklemesinde Kural Kontrolü

Kullanıcı anasayfayı açtığında sistem şunu yapıyor:

  • • 100 koleksiyonu veritabanından çek
  • • Her birinin JSON kurallarını kontrol et (saat doğru mu? gün uyuyor mu? sektör eşleşiyor mu?)
  • • İçindeki tüm playlist/album/radio'ları çek
  • • Her sayfa yüklemesinde bu işlem tekrarlanıyor!
Benzetme: Her müşteri geldiğinde tüm menüyü sıfırdan yazmak gibi. Hazır menüyü göstermek çok daha hızlı!
2. JSON Field Sorguları Yavaş

JSON içinde arama yapmak normal sütunda aramaya göre daha yavaş.

  • • Normal: WHERE sector_id = 1 → Çok hızlı (index var)
  • • JSON: WHERE JSON_CONTAINS(display_rules, '1', '$.sectors') → Yavaş (index yok)
Benzetme: Kitaptaki içindekiler bölümünde aramak (index) vs. tüm sayfaları tek tek okumak (JSON arama).
3. N+1 Query Problemi

Her koleksiyon için ayrı ayrı veritabanına gidip içeriği çekmek.

  • • 1 query: Koleksiyonları çek (100 adet)
  • • +100 query: Her koleksiyonun item'larını ayrı ayrı çek
  • Toplam: 101 query!
Benzetme: Market alışverişinde her ürün için ayrı kuyruğa girmek yerine tek seferde tümünü almak.

Nasıl Hızlandırırız?

🗂️
Cache (Önbellek)

Hazır sonuçları sakla. Her seferinde hesaplama! 60 saniye cache = 60 kat daha hızlı!

📇
Index (Dizin)

Veritabanına "kitap içindekiler" ekle. Aramayı 100 kat hızlandır!

Eager Loading

Tüm ilişkili verileri tek seferde çek. 101 query → 2 query!

🔧 Potansiyel Performans Darboğazları

1

JSON Field Query Performansı

❌ Yavaş Query:
SELECT * FROM muzibu_content_collections
WHERE JSON_CONTAINS(display_rules, '1', '$.sectors')
  AND JSON_EXTRACT(display_rules, '$.time_range.start') <= '09:00'
  AND JSON_EXTRACT(display_rules, '$.time_range.end') >= '09:00'
  AND is_active = 1;

// Problem: JSON fonksiyonları index kullanamaz!
// 100 satır için ~50-100ms
Etki:
  • • Full table scan (tüm satırlar okunur)
  • • Her satır için JSON parse
  • • 100 koleksiyon = 50-100ms
  • • 1000 koleksiyon = 500-1000ms 😱
Çözüm:
  • • Virtual columns (JSON → normal column)
  • • Composite index
  • • Query result cache
  • • Application layer cache
2

N+1 Query Problem

❌ Kötü Kod (101 Query):
// 1 query: Koleksiyonları çek
$collections = ContentCollection::where('is_active', true)->get();

foreach ($collections as $collection) {
    // +100 query: Her koleksiyon için ayrı query!
    $items = $collection->items()->get();
}

// Toplam: 1 + 100 = 101 query!
// Her query ~5ms = 505ms 😱
✅ İyi Kod (2 Query):
// Eager loading ile 2 query!
$collections = ContentCollection::with([
    'items.playlist',
    'items.album',
    'items.radio'
])
->where('is_active', true)
->get();

// Query 1: Collections çek
// Query 2: Tüm items + ilişkili data (tek seferde!)
// Toplam: 2 query = 10ms ⚡
Performans Kazancı:
101 query (505ms) → 2 query (10ms) = 50 kat hızlanma!
3

Runtime Rule Evaluation Overhead

Her sayfa yüklemesinde tüm koleksiyonların kuralları PHP'de kontrol ediliyor:

foreach ($collections as $collection) {
    // Saat kontrolü
    if (!$this->checkTimeRange($collection->display_rules)) continue;

    // Gün kontrolü
    if (!$this->checkDays($collection->display_rules)) continue;

    // Sektör kontrolü
    if (!$this->checkSectors($collection->display_rules, $currentSector)) continue;

    // Kullanıcı koşulları
    if (!$this->checkVisibility($collection->visibility_conditions, $user)) continue;
}

// 100 koleksiyon × 4 kontrol × 5ms = 2000ms (2 saniye!) 😱
Worst Case:
  • • 100 koleksiyon kontrol edildi
  • • Hiçbiri koşulları tutmadı
  • • 2 saniye boşa harcandı
  • • Kullanıcı boş sayfa gördü
Çözüm:
  • • Cache: 60 saniyelik önbellek
  • • Pre-compute: Background job ile önceden hesapla
  • • Lazy loading: Sayfa scroll'da yükle
4

Polymorphic Relationship Query Cost

// collection_items tablosu
item_type: 'playlist', item_id: 15
item_type: 'album', item_id: 8
item_type: 'radio', item_id: 3

// Laravel şunu yapar:
SELECT * FROM muzibu_playlists WHERE id IN (15, 42, 88, ...);
SELECT * FROM muzibu_albums WHERE id IN (8, 25, 67, ...);
SELECT * FROM muzibu_radios WHERE id IN (3, 19, 44, ...);

// 3 ayrı tablo = 3 ayrı query + JOIN maliyeti
Optimizasyon:
  • • Morphable modellere index ekle (id, created_at, is_active)
  • • Eager loading kullan (with('items.playlist'))
  • • Tüm item'ları cache'le (60 saniye)

⚡ Optimizasyon Stratejileri (50-100x Hızlanma!)

1. Multi-Layer Cache (En Önemli!)

Layer 1: Query Result Cache
Database query sonuçlarını önbelleğe al
Cache::remember(
  'collections.home.sector_1',
  60, // 60 saniye
  fn() => Collection::with('items')
    ->where('is_active', true)
    ->get()
);

// İlk yükleme: 100ms
// Cache hit: 2ms ⚡
Layer 2: View Fragment Cache
Render edilmiş HTML'i önbelleğe al
@cache('home.collections', 60)
  @foreach($collections as $col)
    <div>{{ $col->name }}</div>
  @endforeach
@endcache

// İlk render: 50ms
// Cache hit: 1ms ⚡
Layer 3: Full Page Cache
Tüm sayfayı önbelleğe al (guest user)
// ResponseCache package
Route::get('/', HomeController)
  ->middleware('cache.response:60');

// İlk yükleme: 200ms
// Cache hit: 5ms ⚡
// 40x hızlanma!

Cache Hit Rate Hedefi: %95+

2ms
Cache Hit
100ms
Cache Miss
50x
Hızlanma!

2. Smart Database Indexing

⭐ Virtual Columns (JSON → Normal Column)
// Migration: Virtual columns ekle
Schema::table('muzibu_content_collections', function($table) {
    // JSON'dan virtual column oluştur
    $table->time('display_time_start')
        ->virtualAs("JSON_UNQUOTE(JSON_EXTRACT(display_rules, '$.time_range.start'))");

    $table->time('display_time_end')
        ->virtualAs("JSON_UNQUOTE(JSON_EXTRACT(display_rules, '$.time_range.end'))");

    $table->json('display_sectors_cached')
        ->virtualAs("JSON_EXTRACT(display_rules, '$.sectors')");

    // Virtual column'a index ekle! ⚡
    $table->index(['is_active', 'display_time_start', 'display_time_end']);
});
❌ Önce (Index Yok):
Full table scan: 100ms
✅ Sonra (Index Var):
Index scan: 2ms ⚡ (50x hızlı!)
📇 Composite Index (Çoklu Sütun)
// Sık kullanılan kombinasyonları index'le
$table->index(['is_active', 'priority', 'created_at'], 'collections_active_priority_idx');
$table->index(['type', 'is_active'], 'collections_type_active_idx');

// collection_items için
$table->index(['collection_id', 'position'], 'items_collection_position_idx');
$table->index(['item_type', 'item_id'], 'items_morphable_idx');
🔍 Full-Text Search (Arama İçin)
// Koleksiyon isimlerinde arama için
DB::statement('ALTER TABLE muzibu_content_collections
    ADD FULLTEXT INDEX collections_search_idx (name)');

3. Query Optimization Patterns

⚡ Eager Loading (Zorunlu!)
❌ Lazy Loading (Yavaş):
$collections = Collection::all();
// 101 query!
✅ Eager Loading (Hızlı):
$collections = Collection::with([
  'items.playlist.sectors',
  'items.album.artists',
  'items.radio'
])->get();
// 4-5 query!
📋 Select Only What You Need
❌ SELECT *:
Collection::all();
// Tüm sütunlar çekiliyor
// JSON parse overhead!
✅ SELECT Specific:
Collection::select([
  'id', 'name', 'slug',
  'icon', 'color', 'type'
])->get();
// Sadece gerekli sütunlar!
🔄 Chunk Processing (Büyük Veri İçin)
// 10000 koleksiyon varsa, 100'er 100'er işle
Collection::with('items')
    ->chunk(100, function($collections) {
        foreach ($collections as $collection) {
            // Process...
        }
    });

// Memory: 10 MB yerine 1 MB ⚡

4. Background Job ile Pre-Computation

💡 Strateji: Ağır işleri arka planda yap, hazır sonuçları göster!
// Scheduled Job: Her 5 dakikada bir çalış
// app/Console/Kernel.php
$schedule->job(new PrecomputeCollectionVisibility)
    ->everyFiveMinutes();

// Job: Tüm koleksiyonları değerlendir, cache'e yaz
class PrecomputeCollectionVisibility
{
    public function handle()
    {
        $now = now();
        $currentHour = $now->format('H:i');
        $currentDay = $now->dayName;

        // Her sayfa için ayrı cache
        foreach (['home', 'sector_show'] as $page) {
            foreach (Sector::all() as $sector) {
                $visibleCollections = Collection::all()
                    ->filter(function($col) use ($currentHour, $currentDay, $sector) {
                        return $this->checkRules($col, $currentHour, $currentDay, $sector);
                    });

                // Cache'e yaz (5 dakika)
                Cache::put(
                    "collections.{$page}.sector_{$sector->id}",
                    $visibleCollections,
                    300 // 5 dakika
                );
            }
        }
    }
}

// Controller'da sadece cache'den oku!
public function home()
{
    $collections = Cache::get('collections.home.sector_' . auth()->user()->sector_id);
    return view('home', compact('collections'));
}

// Sayfa yüklemesi: 0ms (sadece cache read!) ⚡⚡⚡
Performans Kazancı:
Runtime evaluation (200ms) → Pre-computed cache (2ms) = 100 kat hızlanma!

📊 Benchmark Senaryoları

😱

Optimizasyon YOK

Database Query: 150ms
N+1 Queries (101 adet): 505ms
Rule Evaluation (PHP): 200ms
View Rendering: 145ms
TOPLAM: 1000ms
1 saniye = Kullanıcı bekliyor! 😱
🚀

FULL Optimizasyon

Cache Hit (Query Skip): 0ms
Eager Loading (2 query): 10ms
Pre-computed (Background Job): 0ms
View Cache Hit: 5ms
TOPLAM: 15ms
⚡ 66 KAT HIZLANMA! ⚡

Performans Karşılaştırma Grafiği

Optimizasyon YOK 1000ms
100%
Partial Optimizasyon (Cache Only) 100ms
10%
FULL Optimizasyon (Tüm Stratejiler) 15ms
1.5%

🗺️ Uygulama Yol Haritası (Öncelik Sırasıyla)

1

Quick Wins (1-2 Gün)

Hemen uygulanabilir, büyük etki!

50x Hızlanma
  • Eager Loading ekle: Collection::with(['items.playlist', 'items.album', 'items.radio'])
  • Query result cache: Cache::remember('collections', 60, ...)
  • SELECT specific columns: Sadece gerekli alanları çek (JSON'ları atlayarak)
2

Database Optimization (3-4 Gün)

Index'ler ve virtual columns

20x Hızlanma
  • Virtual columns ekle: display_time_start, display_time_end
  • Composite index: (is_active, priority, created_at)
  • Morphable index: collection_items için (item_type, item_id)
3

Advanced Caching (2-3 Gün)

Multi-layer cache yapısı

10x Hızlanma
  • View fragment cache: @cache directive ile blade cache
  • Full page cache: ResponseCache middleware (guest user için)
  • Redis setup: Cache driver olarak Redis kullan
4

Background Processing (3-4 Gün)

Pre-computation ile ultra hız

100x Hızlanma
  • Scheduled job: PrecomputeCollectionVisibility (her 5 dakika)
  • Cache warm-up: Tüm sayfa kombinasyonlarını önceden hesapla
  • Cache invalidation: Collection güncelleme/silme event'lerinde cache temizle
🚀

Tüm Fazlar Tamamlandığında:

1000ms
Önce
15ms
Sonra
66 KAT HIZLANMA! ⚡⚡⚡

📈 Monitoring & Metrics (Takip Etmemiz Gerekenler)

📊 Performans Metrikleri:

  • Page Load Time: Hedef < 200ms
  • Database Query Count: Hedef < 5 query/sayfa
  • Query Time: Hedef < 10ms ortalama
  • Memory Usage: Hedef < 50MB/request

🎯 Cache Metrikleri:

  • Cache Hit Rate: Hedef > 95%
  • Cache Invalidation: İzle ve optimize et
  • Redis Memory: Hedef < 100MB
  • Cache Response Time: Hedef < 2ms
Monitoring Tools:
• Laravel Telescope
• Laravel Debugbar
• New Relic / DataDog
• Redis Monitor