CodeWriteson Ben uzak bir veri tabanından veri çekerken kullandım. İki tarih arası verileri çekmeme rağmen yine de çok kayıt olduğu için sorun çıkıyordu. Builder içindeki chunk yöntemi ile bu sorunu aştım. Ha evet, 1 değil 10 sorgu yaptı ama bu durumun çözümü 10 sorgu yaparak halletmekti çünkü veri tabanından parça parça devamlı kayıt almanın tek yolu ya offset/limit kullanmak ya da seek yöntemi geliştirmektir.
Sorgu çok fazla kayıt dönüyorsa ve her bir kayıt bir sürü alan içeriyorsa dönen Collection bellekte fazla yer kaplar, işlenmesi fazla CPU gücü gerektirir. Eğer Collection dizisi içindeki tek bir Collection'da 20 alan varsa ve siz sadece içinden 3 tanesini kullanıyorsanız select, selectRaw gibi yöntemlerle sadece istediğiniz alanları getirerek Collection boyutunu düşürmeniz lazım. Eager loading varsa with('user:id,name')
şeklinde sadece ihtiyacınız olan alanları yüklemeniz lazım.
Elbette asıl sorun Collection'ı nasıl oluşturduğunuz ile başlıyor. Collection tek seferde belleğe yükleniyorsa Collection::chunk() kullanmak kaynak kullanımı açısından size pek bir fayda sağlamaz çünkü komple belleğe alınan Collection üzerinden işlem yapıyorsunuz. Collection'ı lazy şekilde oluşturup işlemeniz lazım. Yani 100 nesneyi tek seferde belleğe alıp 10'arlı gruplar halinde 10 kerede işlemek yerine her seferinde 10 nesneyi belleğe alıp işlemeniz gerekiyor. Laravel bunun için size bazı yöntemler sunuyor:
Bunlardan biri Builder::chunk()
https://laravel.com/docs/8.x/eloquent#chunking-results
Diğeri Builder::cursor()
https://laravel.com/docs/8.x/eloquent#cursors
Builder::chunk() yöntemi offset/limit ile chunk sayısı kadar sorgu yaparak her seferinde chunk içinde belirtilen kaydı Collection'a çevirip işlem yapmanızı sağlar. Builder::cursor() ise sadece tek sorgu yaparak tüm kayıtları belleğe alır ama her bir kaydı Generators sayesinde sadece yinelendiğinde (iterate) modele ya da Collection'a çevirir. Bu yüzden Builder::chunk() az bellek tüketir ama fazla sorgu yapar; Builder::cursor() ise çekilen veri boyutuna göre bellek problemi yaratabilir ama tek sorgu yapar ve daha hızlı çalışır. Builder::chunk() kullanırken, dönen her bir chunk içindeki kayıt sayısı çok fazla ise o da bellek problemi yaratabilir. Burada hangisini kullanacağınızı yapacağınız iş belirleyecek, düşünüp tartacaksınız.
Bu aşamayı da geçtiniz ama mecburen yapılan iş gereği elinizde yine yüklü bir Collection varsa, bazı durumlarda bu Collection yapılan işlem sonucu tekrar tekrar belleğe alınabilir. Bunu engellemek ve sadece ilk belleğe alınan Collection ile çalışmak için Lazy Collection özelliklerinden faydalanabilirsiniz:
https://laravel.com/docs/8.x/collections#lazy-collections
Örneğin:
// Burada elbette sorguya get() yönteminden önce where('status', 2) ekleyerek
// Collection::where() kullanmayabilirsiniz, ben sadece örnek vermeye
// çalışıyorum. O yüzden onu dikkate almayın.
Model::select('id', 'name', 'status')
->get()
->where('status', 2)
->all();
Burada get() yapıldığında dönen Collection belleğe alınıyor fakat sonrasında kullanılan Collection::where() yeni bir Collection oluşturuyor ve onu da belleğe alıyor, mevcut bellekteki kayıtları değiştirmiyor. Burada lazy() yöntemi ile get() ile dönen Collection'ı LazyCollection'a dönüştürmeniz lazım.
Model::select('id', 'name', 'status')
->get()
->lazy()
->where('status', 2)
->all();
Artık kullanılan Collection::where(), ilk belleğe alınan Collection'ı değiştirerek bellek tasarrufu yapmanızı sağladı.
Bu aşamayı da geçtiniz ama hala bellek vs problemler yaşıyorsanız artık yapmanız gereken veri yazma/çekme stratejileri oluşturmak. Cache kullanacaksınız, işleri Job'lara çevireceksiniz, Task oluşturacaksınız, TRIGGER vs kullanıp kayıtları düzenleyeceksiniz, geçici tablolar oluşturacaksınız, bazı verileri ayırıp NoSQL bir yapıda tutacaksınız vs vs...
Bunları da yaptınız ama hala bellek problemine çözüm bulamadınız diyelim. O zaman para bayılmanız lazım. Sanal ya da fiziksel sunucu varsa önce ram, disk ve cpu arttırımı yoksa ya da yetmiyorsa AWS gibi scalable cloud sistemler, 3. parti servisler...
Bu da sorununuza çözüm olmadı ki bu aşamada artık Google ya da Facebook gibi birine cevap yazdığımı varsayıyorum, veri çiftlikleri ya da kendi teknolojinizi üreteceksiniz, onları yaptığı gibi...
Bu da çözüm olmadı diyelim o zaman ömrünüz yeterse dondurulmuş insan projelerini bekleyebilirsiniz. Kendinizi dondurup 100 yıl sonra uyanırsınız ve o zamanın teknolojisi ile hareket edersiniz.
Ve nihai çözüm: Esnaflık. Parası her zaman yazılım kadar iyi değil ama kafa daha rahat. Camekan arabada ya da omzuna aldığın büyük termosta lahmacun satarsın, çocuklar gelir usta benimki çift olsun bol maydanozlu der, akşam eve gelince sattığın lahmacunu düşünmezsin, o da para bu da para 🙂
Dokümanda yazanları tekrar buraya yazmak istemiyorum o yüzden verdiğim linklerdeki tüm örnekleri inceleyin.