Drupal 11'de Cache Optimizasyonu: Redis ve BigPipe Birlikte Kullanımı

Redis ve BigPipe Birlikte Kullanımı
MetrikÖnceSonra
İlk bayt süresi (TTFB)~1.8s~180ms
Sayfa yükleme (giriş yapmış)~16s~3.2s
Cache hit oranı~%40~%94

1. Neden Redis ve BigPipe birlikte?

Drupal'ın varsayılan cache sistemi disk tabanlı çalışır. Yüksek trafikli sitelerde bu yaklaşım ölçeklenmez: her istek için dosya kilitleme, seri/deserializasyon yükü ve yavaş I/O darboğaz yaratır. Redis, bu sorunu bellek üzerinde çalışarak çözer; ancak Redis tek başına yetmez.

BigPipe ise tamamen farklı bir problemi çözer: giriş yapmış kullanıcılar için sayfa render süresini. Kimlik doğrulama, sepet, bildirim gibi kişiselleştirilmiş bloklar sayfayı "cache'lenemez" hale getirir. BigPipe bu blokları placeholder ile erteler; statik içeriği anında gönderir, kişisel içeriği paralel akışla doldurur.

Temel ilke: Redis önbelleği hızlandırır; BigPipe kişiselleştirilmiş içeriği cache sınırlamalarının dışına taşır. İkisi farklı katmanlarda çalışır ve birbirini tamamlar — çakışmaz.

Redis'in katkısı: Cache okuma/yazma işlemlerini bellekte yapar. Disk I/O ortadan kalkar. Cache tag invalidation mikrosaniyeler içinde gerçekleşir.

BigPipe'ın katkısı: Sayfa render'ını bölümlere ayırır. Cache'lenemez parçalar asenkron akışla gelir, kullanıcı boş sayfa görmez.

2. Kurulum

Redis modülü ve bağlantı kurulumu

Drupal 11 ile Redis entegrasyonu için drupal/redis modülü ve PHP Redis extension'ı gerekir. PhpRedis extension'ı Predis PHP kütüphanesine göre yaklaşık 3-4× daha hızlıdır; üretim ortamında her zaman PhpRedis tercih edilmeli.

# Composer ile Redis modülünü yükle
composer require drupal/redis

# PhpRedis extension (Debian/Ubuntu)
apt install php8.3-redis

# Modülü aktifleştir (BigPipe zaten core'da)
drush en redis bigpipe -y
drush cr

settings.php yapılandırması

Redis bağlantısı ve chainedfast backend'i settings.php içinde tanımlanır. chainedfast, statik PHP önbelleğini Redis ile birleştirir; aynı istek içinde tekrar erişilen veriler Redis'e bile gitmez.

// Redis bağlantı bilgileri
$settings['redis.connection']['interface'] = 'PhpRedis';
$settings['redis.connection']['host']      = '127.0.0.1';
$settings['redis.connection']['port']      = 6379;
$settings['redis.connection']['password']  = 'gizli_sifre';
$settings['redis.connection']['database']  = 0;

// chainedfast: statik PHP cache + Redis
$settings['cache']['default'] = 'cache.backend.chainedfast';

// Render cache Redis'te, bootstrap hızlı PHP'de
$settings['cache']['bins']['render']    = 'cache.backend.redis';
$settings['cache']['bins']['bootstrap'] = 'cache.backend.chainedfast';
$settings['cache']['bins']['config']    = 'cache.backend.chainedfast';
$settings['cache']['bins']['discovery'] = 'cache.backend.chainedfast';

// Prefix: birden fazla Drupal sitesi aynı Redis'i kullanıyorsa
$settings['cache_prefix'] = 'novebo_';
Dikkat: cache.backend.chainedfast backend'i PHP'nin statik önbelleğini kullandığından, CLI (drush) isteklerinde bu önbellek çalışmaz. drush cr sonrası web isteği atarak cache'in doğru dolduğunu kontrol edin.

3. BigPipe nasıl çalışır?

Geleneksel Drupal render akışında, sayfadaki en yavaş "cache'lenemez" blok tüm sayfanın TTFB'sini belirler. BigPipe bu engeli ortadan kaldırır: sunucu önce cacheable içeriği gönderir, tarayıcı bunu render ederken kişiselleştirilmiş bloklar için placeholder'lar doldurulur.

  1. İstek gelir
  2. Statik HTML anında gönderilir
  3. Placeholder'lar akışla doldurulur
  4. Sayfa tamam, bağlantı kapanır

Modül aktivasyonu yeterli mi?

BigPipe modülünü etkinleştirmek genellikle yeterlidir; ancak maksimum fayda için blokların render cache ve lazy_builder ayarları doğru kurulmalıdır. Cache'lenemez blokların #lazy_builder kullandığından emin olun.

// Yanlış: tüm sayfa cache'lenemez hale gelir
function my_block_build() {
  return [
    '#markup' => t('Merhaba @user', ['@user' => \Drupal::currentUser()->getDisplayName()]),
    '#cache'  => ['max-age' => 0],
  ];
}

// Doğru: BigPipe placeholder olarak render edilir
function my_block_build() {
  return [
    '#lazy_builder'       => ['my_module.lazy:buildUserGreeting', []],
    '#create_placeholder' => TRUE,
  ];
}
BigPipe olmadanBigPipe ile
Yavaş blokTüm sayfayı bloklarYalnızca kendini etkiler
TTFBEn yavaş bileşen kadarCacheable render süresi
Kullanıcı deneyimiBoş beyaz ekranSayfa kademeli dolar
Giriş yapmış kullanıcıCache yokKişisel içerik paralel gelir

4. Redis sunucu optimizasyonu

Redis performansı sadece PHP tarafındaki yapılandırmayla sınırlı değil; redis.conf ayarları ve bellek politikası da kritik rol oynar. Drupal cache için allkeys-lru politikası idealdir: bellek dolduğunda en az kullanılan anahtarlar otomatik silinir.

# /etc/redis/redis.conf

# Maksimum bellek (sunucu RAM'ine göre ayarla)
maxmemory 512mb
maxmemory-policy allkeys-lru

# Kalıcılık gerekmez, performans için kapat
save ""
appendonly no

# TCP bağlantı kuyruğu (yüksek eşzamanlılık için)
tcp-backlog 511
tcp-keepalive 300

# Lazy freeing: büyük anahtarları arka planda sil
lazyfree-lazy-eviction yes
lazyfree-lazy-expire yes
lazyfree-lazy-server-del yes

# Unix socket (aynı sunucuda Nginx+PHP varsa daha hızlı)
unixsocket /var/run/redis/redis.sock
unixsocketperm 770

Unix socket ile bağlantı

PHP ve Redis aynı sunucudaysa TCP yerine Unix socket kullanmak küçük ama ölçülebilir bir iyileşme sağlar. Gecikmeler mikrosaniye seviyesine iner.

// settings.php — Unix socket bağlantısı
$settings['redis.connection']['interface'] = 'PhpRedis';
$settings['redis.connection']['host']      = '/var/run/redis/redis.sock';
$settings['redis.connection']['port']      = 0; // socket için 0
Hetzner VPS notu: CX22/CX32'de Redis için 256–512 MB bellek yeterlidir. Orta ölçekli sitelerde toplam cache boyutu genellikle 50–150 MB arasında kalır. redis-cli info memory ile gerçek kullanımı kontrol edin.

5. Akıllı cache invalidation

Redis hızı sağlar; ancak cache'in ne zaman geçersiz kılınacağını belirleyen mekanizma cache tag'larıdır. Drupal 11'de cache tag invalidation varsayılan olarak veritabanı üzerinden çalışır. Redis modülü bunu Redis'e taşır ve tag invalidation dramatik biçimde hızlanır.

// node:42 güncellenince bu blok otomatik geçersiz olur
function mymodule_build_node_block($nid) {
  $node = Node::load($nid);

  return [
    '#theme'  => 'my_node_card',
    '#node'   => $node,
    '#cache'  => [
      'keys'     => ['my_node_card', $nid],
      'tags'     => $node->getCacheTags(), // ['node:42']
      'contexts' => ['languages'],
      'max-age'  => Cache::PERMANENT,
    ],
  ];
}

// Node kaydedilince tüm ilgili cache'leri temizle
function mymodule_node_update(NodeInterface $node) {
  Cache::invalidateTags($node->getCacheTags());
}

Cache context seçimi

Cache context ne kadar geniş tutulursa, aynı içerik için o kadar çok varyant oluşur ve cache verimliliği düşer. user context yerine user.roles tercih edin; bu, her kullanıcı için ayrı cache yerine rol bazlı ortak cache sağlar.

// Kötü: her kullanıcı için ayrı cache — Redis şişer
'contexts' => ['user']

// İyi: rol bazlı — 4-5 varyant yeterli
'contexts' => ['user.roles']

// Çok iyi: sadece anonim/giriş yapmış ayrımı
'contexts' => ['user.roles:authenticated']

// Dil + rol kombinasyonu (çok dilli siteler)
'contexts' => ['languages:language_interface', 'user.roles']

6. Performansı izlemek ve debug etmek

# Genel istatistikler (hit/miss oranı kritik)
redis-cli info stats | grep -E "hits|misses|ops_per_sec"

# Anlık komut akışı (geliştirme ortamında)
redis-cli monitor | grep -v PING

# Anahtar sayısı ve bellek kullanımı
redis-cli info keyspace
redis-cli info memory | grep -E "used_memory_human|maxmemory_human"

# En büyük anahtarlar (bellek sızıntısı tespiti)
redis-cli --bigkeys

# Site prefix'li anahtarları say
redis-cli keys "novebo_*" | wc -l
# Cache bin'lerinin hangi backend'i kullandığını kontrol et
drush php-eval "
  \$bins = ['render','bootstrap','config','discovery','default'];
  foreach (\$bins as \$bin) {
    \$cache = \Drupal::cache(\$bin);
    echo \$bin . ': ' . get_class(\$cache) . PHP_EOL;
  }
"

# BigPipe çalışıyor mu?
drush pm:list --status=enabled | grep bigpipe

# Cache temizle ve ilk yanıt süresini ölç
drush cr && curl -s -o /dev/null -w "%{time_total}" https://novebo.com/
Hedef metrikler: Redis hit oranı %85'in altındaysa cache key tasarımını gözden geçirin. Anonim kullanıcılarda TTFB 200ms altında, giriş yapmış kullanıcılarda 800ms altında olmalıdır (BigPipe placeholder'lar hariç).

7. Canlıya almadan önce kontrol listesi

  • PhpRedis extension yüklü ve etkin: php -m | grep redis
  • Redis şifresi settings.php'de tanımlı; requirepass redis.conf'ta da açık
  • maxmemory-policy allkeys-lru olarak ayarlı (yoksa Redis bellek dolarsa hata verir)
  • BigPipe modülü aktif; page_cache ve dynamic_page_cache modülleri de açık
  • Cache prefix tanımlı; birden fazla site aynı Redis instance'ı paylaşıyorsa zorunlu
  • Render cache context'leri minimumda: user yerine user.roles kullanılıyor
  • Redis kalıcılığı (RDB/AOF) devre dışı; yalnızca cache için kullanılıyorsa disk yazımı gereksiz yük
  • Nginx FastCGI cache ile Redis çakışmıyor: Nginx microcache anonim kullanıcı için Drupal'ı devreye sokmamalı

8. Mimari özet

Anonim kullanıcı akışı: Nginx FastCGI microcache → cache hit ise PHP'ye uğramaz. Miss ise Drupal + Redis render cache → yanıt + Nginx'e yazar.

Giriş yapmış kullanıcı akışı: Nginx microcache bypass → Drupal + Redis (cacheable bloklar) + BigPipe (kişisel bloklar) → kademeli sayfa render.

Cache invalidation: Node/config kaydedilir → Cache tag'lar Redis'te geçersiz olur → Sonraki istek yeni render üretir → Redis'e yazar.

Bellek hiyerarşisi: PHP statik cache (en hızlı, istek ömrü) → Redis chainedfast (hızlı, process ömrü) → MySQL (yavaş, son çare).

İlgili modüller: drupal/redis, bigpipe (core), dynamic_page_cache (core), page_cache (core)