# İşlediğim satırları aynı anda güncellerken chunk yerine chunkById mı kullanmalıyım?

> Satır başına gerçek iş varsa `chunkById()` kullanıp cursor kolonuna dokunmayın; iş yalnız bir kolonu güncellemekse tek bir set-based `UPDATE`'e geçin.

- Soruldu: 2026-08-28
- Yanıtlandı: 2026-09-01
- Soran: Naz
- Etiketler: laravel, eloquent, performans
- Kaynak: https://www.muhammetsafak.com.tr/sor-bakalim/isledigim-satirlari-ayni-anda-guncellerken-chunk-yerine-chunkbyid-mi-kullanmaliyim/
- Dil: tr-TR
- Yazar: Muhammet Şafak

---
**Soru:** Laravel'de büyük bir tabloyu batch bir job ile işliyorum. Kayıtları `where('status', 'pending')` ile filtreleyip `chunk()` ile geziyorum ve her satırda `status` kolonunu `pending`'den `processed`'e güncelliyorum.

Sorun şu: bazı satırlar hiç işlenmeden atlanıyor. Sanırım güncellediğim satırlar filtreden düşünce OFFSET kayıyor ve altındaki kayıtlar es geçiliyor. `chunk()` yerine `chunkById()` mı kullanmalıyım? Bu skip problemini gerçekten çözer mi?


Kısa cevap: Evet, `chunkById()` kullanın. Gördüğünüz atlama, `chunk()`'ın OFFSET tabanlı sayfalamasının bilinen bir tuzağıdır ve sizinki tam ders kitabı örneği. Tek bir koşul var: cursor kolonunu (genelde `id`) asla güncellememeniz.

1. **`chunk()` neden atlıyor.** `chunk()` her sayfa için `LIMIT/OFFSET` ile ayrı bir sorgu atar. İlk 500 satırı işleyip `status`'ü `processed` yapınca bu satırlar `where status = pending` filtresinden düşer; ikinci sayfada `OFFSET 500` artık daralmış bir sonuç kümesine uygulanır ve tam ortadaki bir dilim hiç görülmeden atlanır.
2. **`chunkById()` nasıl çözer.** OFFSET yerine keyset (seek) sayfalama yapar: `WHERE id > ? ORDER BY id LIMIT n`. Bir sonraki sayfa daima son işlenen id'den devam eder; işlenenler zaten cursor'ın gerisinde kaldığı için "kayma" diye bir şey olmaz.
   ```php
   Order::where('status', 'pending')
       ->chunkById(500, function ($orders) {
           foreach ($orders as $order) {
               $order->update(['status' => 'processed']);
           }
       });
   ```
3. **Kritik kural: cursor kolonunu güncellemeyin.** `status`'ü güncellemek sorun değil, çünkü o cursor değil. Ama sıraladığınız kolonu (id) döngü içinde değiştirirseniz seek mantığı bozulur ve yine tutarsızlık yaşarsınız.
4. **Bellek için `lazyById()`.** Aynı seek mantığını kullanır ama closure yerine tek bir `LazyCollection` döner; `foreach` ile akıcı yazmak isterseniz, üstelik büyük tablolarda RAM'i şişirmeden, daha temiz olur.
5. **En hızlısı çoğu zaman iterasyon değildir.** Güncelleme deterministikse (satır başına hesaplama yoksa) satır satır dönmeye hiç gerek yok; tek bir set-based sorgu hepsinden hızlıdır ve atomiktir:
   ```sql
   UPDATE orders SET status = 'processed' WHERE status = 'pending';
   ```
6. **Lock ve deadlock'a dikkat.** Büyük batch update'lerde chunk boyutunu makul tutun (500–1000 iyi bir başlangıç) ve gerekiyorsa her chunk'ı kendi transaction'ında işleyin; tek dev transaction hem lock süresini hem deadlock riskini büyütür.

**Sonuç:** Ben olsam satır başına gerçek bir iş varsa `chunkById()` (ya da akıcılık için `lazyById()`) kullanır ve cursor kolonuna asla dokunmazdım. Ama yaptığınız iş sadece bir kolonu güncellemekse, iterasyonu tamamen bırakıp tek bir set-based `UPDATE`'e geçerdim — hem daha hızlı hem de skip riski sıfır.

## İlgili Yazılar

- [Eloquent'te N+1'i erken yakalamak için Model::preventLazyLoading'i yalnızca lokalde mi yoksa production'da da mı açmalıyım?](https://www.muhammetsafak.com.tr/sor-bakalim/eloquentte-n-1i-erken-yakalamak-icin-model-preventlazyloadingi-yalnizca-lokalde-mi/) — Sor Bakalım
- [Integer cent olarak sakladığım para alanları için custom cast mı yoksa accessor/mutator mı kullanmalıyım?](https://www.muhammetsafak.com.tr/sor-bakalim/integer-cent-olarak-sakladigim-para-alanlari-icin-custom-cast-mi-yoksa/) — Sor Bakalım
- [2-5 GB dosyaları PHP belleğini şişirmeden stream ile nasıl indirtirim?](https://www.muhammetsafak.com.tr/sor-bakalim/buyuk-dosyalari-php-bellegini-sismeden-stream-ile-indirmek/) — Sor Bakalım
