Gerçek Zamanlı Analitik Sıkışması
Smart Tech Devs’deki B2B SaaS platformlarında, yönetici paneli en kritik sayfadır. Müşteriler, sisteme giriş yaptıklarında hemen Aylık Tekrarlayan Gelirlerini (MRR), toplam aktif kullanıcıları ve kayıp oranlarını görmek isterler. Standart bir geliştirici refleksi, users, subscriptions ve invoices tablolarını birleştirerek karmaşık Eloquent toplama sorguları yazmaktır.
Veritabanınızda 10,000 satır olduğunda bu sorgunun süresi 50 milisaniyedir. Ancak veritabanınızda 5 milyon satır olduğunda bu sorgu 6 saniye sürer. Eğer 100 yönetici sabah 9:00’da panellerine girerse, PostgreSQL veritabanınız 100 eşzamanlı 6 saniyelik toplama sorgusu çalıştırmaya çalışır. CPU %100’e çıkar, bağlantı havuzları tükenir ve platform çökme noktasına gelir. Ağır analizleri anlık olarak hesaplayamazsınız. Bunları Materialized Views kullanarak önceden hesaplamanız gerekir.
Çözüm: PostgreSQL Materialized Views
Standart bir SQL View, yalnızca kaydedilmiş bir sorgudur; her çağrıldığında ağır hesaplamaları çalıştırır. Ancak Materialized View, ağır hesaplamaları bir kere çalıştırır ve sonucu fiziksel, sorgulanabilir bir tablo olarak diskinize kaydeder.
Yönetici panelini açtığında, 5 milyon satırı taramıyor. Küçük, önceden hesaplanmış 10 satırlık bir materialized view tablosunu sorguluyor. Yanıt süresi 6 saniyeden 2 milisaniyeye düşer.
Adım 1: Migration Tasarımının Yapılması
Laravel, Materialized Views için yerel şeması oluşturuculara sahip olmadığından, karmaşık toplama mantığını tanımlamak için migration dosyalarımızda ham SQL kullanmamız gerekiyor.
use Illuminate\Database\Migrations\Migration;
use Illuminate\Support\Facades\DB;
class CreateMonthlyRevenueMaterializedView extends Migration
{
public function up(): void
{
// 1. Materialized View oluştur
DB::statement('
CREATE MATERIALIZED VIEW monthly_tenant_revenue AS
SELECT
tenant_id,
DATE_TRUNC(\'month\', created_at) AS billing_month,
COUNT(id) as total_invoices,
SUM(amount) as total_revenue
FROM invoices
WHERE status = \'paid\'
GROUP BY tenant_id, DATE_TRUNC(\'month\', created_at)
');
// 2. Daha sonra EŞZAMANLI güncellemeleri sağlamak için benzersiz bir indeks ekle
DB::statement('
CREATE UNIQUE INDEX monthly_tenant_revenue_unique_idx
ON monthly_tenant_revenue (tenant_id, billing_month);
');
}
public function down(): void
{
DB::statement('DROP MATERIALIZED VIEW IF EXISTS monthly_tenant_revenue;');
}
}
Adım 2: Verilerin Asenkron Olarak Yenilenmesi
Veri fiziksel olarak kaydedildiği için, yeni faturalar ödendikçe bayatlayacaktır. Bunu yenilememiz gerekiyor. Kullanıcı bir butona tıkladığında (bu onların isteğini engeller) yenilemek yerine, her saat başı arka planda bunu yenilemek için bir Laravel Job’u kuruyoruz.
namespace App\Jobs;
use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Foundation\Bus\Dispatchable;
use Illuminate\Support\Facades\DB;
class RefreshRevenueAnalytics implements ShouldQueue
{
use Dispatchable, Queueable;
public function handle(): void
{
// EŞZAMANLI anahtarı mutlak bir sihir.
// PostgreSQL'in, materialized view'ü arka planda güncellemesine izin verir
// TABLOYU kilitlemeden. Kullanıcılar, yeni veriler üretilirken eski verilere erişebilir!
DB::statement('REFRESH MATERIALIZED VIEW CONCURRENTLY monthly_tenant_revenue;');
}
}
Mühendislik ROI’si
Ağır analizleri Materialized Views’a kaydırarak, okuma performansınızı veri hacminizden tamamen bağımsız hale getirirsiniz. Panelleriniz, ana tablolarınızda milyonlarca satır bulunsa bile anında yüklenir. Öngörülemeyen, CPU yoğun dashboard yüklerini düz, O(1) yıldırım hızlı sorgulara dönüştürerek premium bir yönetici kullanıcı deneyimi garanti edersiniz.
Kaynak: Orijinal Makale


