· kaynak dev.to (home feed)
MongoDB 8.3, $map'e arrayIndexAs ekleyerek index çözümünün yükünü yüzde 31'e kadar düşürüyor
MongoDB 8.3, $map'in eleman konumlarını doğrudan arrayIndexAs ile açığa çıkarmasına izin veriyor. dev.to benchmarkları, yerini alan $range/$arrayElemAt çözümünün büyük dizilerde yüzde 15-31 daha maliyetli olduğunu gösteriyor.

8.3 neyi değiştiriyor
MongoDB 8.3, aggregation framework'ünün $map operatörüne isteğe bağlı bir parametre olan arrayIndexAs'ı ekliyor; bu parametre, pipeline diziyi dolaşırken her elemanın konumunu açığa çıkarıyor. Değişikliği benchmarklayan bir dev.to yazısına göre bu, köklü bir boşluğu kapatıyor: $map, $filter ve $reduce hiçbir zaman hangi index'i işlediğinizi söylemedi, bu yüzden bir değeri konumuyla eşleştirmesi gereken pipeline'lar bu eşleştirmeyi kendileri kurmak zorundaydı.
Yerleşik çözüm, dizinin $size'ını kapsayan bir $range üzerinden map yapıyor, sonra $arrayElemAt ile her değeri konumuna göre geri çekiyordu — döngünün zaten sahip olduğu bilgiyi yeniden kurmak için iki adım. arrayIndexAs ile $map diziyi doğrudan alıyor ve geçerli index'i adlandırılmış bir değişkene bağlıyor. dev.to yazarı, herhangi bir zamanlamaya güvenmeden önce her iki biçimin de özdeş çıktı ürettiğini doğrulamış.
Benchmark ne buldu
Testler, Docker içinde MongoDB 8.3.11 üzerinde, 10.000 ile 320.000 float'tan oluşan diziler içeren tek belgelerle çalıştı; süreler explain({executionStats}) üzerinden ölüldü — sunucunun kendi çalışma süresi, ağ veya driver yükü olmadan — ve WiredTiger cache ısınmasını atlamak için her boyut için beş çalıştırmanın en düşüğü kullanıldı.
320.000 elemanda eski desen 134ms sürerken arrayIndexAs ile 102ms sürdü; bu yüzde 31'lik bir avantaj ve manşetteki rakamın kaynağı. Geri kalan sonuçlar: 10.000 elemanda 3ms'ye karşı 2ms, 40.000'de 14ms'ye karşı 12ms, 80.000'de 29ms'ye karşı 24ms ve 160.000'de 58ms'ye karşı 50ms. Yazı, diziler on binlerce elemana ulaştığında istikrarlı bir yüzde 15-30'luk fark bildiriyor ve kabaca 10.000'in altında farkın çalıştırmadan çalıştırmaya oluşan gürültü içinde kaldığı konusunda uyarıyor.
Yazar daha kötüsünü bekliyordu. $arrayElemAt her çağrıda dizinin başından tarama yapsaydı maliyet ikinci dereceden büyürdü — ama MongoDB'nin bellek içi dizi temsili doğrudan konumsal erişimi destekliyor, dolayısıyla ceza, aralığı kurmaktan ve döngünün elinde tuttuğu değeri yeniden getirmekten kaynaklanan sabit bir eleman başına yüküne daha yakın.
Yük altında fark daralıyor
Tek bir sakin bağlantı bir production örneği değildir. 320.000 elemanlı belgeye pymongo üzerinden on aggregation'ı çalıştırıp dört koşu üzerinden ortalamak, eski desenin ardışık çalıştırıldığında 3.137ms sürdüğünü, yeni parametreyle 2.788ms olduğunu gösterdi — yaklaşık yüzde 13'lük fark. On sorgunun tamamı dört çekirdekli bir container'da eşzamanlı çalıştığında bu 1.950ms'ye karşı 1.859ms'ye düştü, kabaca yüzde 5. Sorgular çekirdek için kuyruğa girdiğinde, bir aggregation'ın iç döngüsünü törpülemek daha az kazandırır. Yazara göre uygulanabilir sonuç şu: arrayIndexAs eşzamanlılık altında da yardımcı oluyor, ama CPU doygunluğundaki bir örnekte çekirdek sayısı ve sorgu hacmi daha büyük kaldıraçlar.
Yazı ayrıca işaret etmeye değer bir ölçüm hatası da kaydediyor: On ayrı mongosh süreci başlatıp bunları shell'den zamanlayan erken bir test düzeneği, her diğer koşudan ters sonuçlar üretti, çünkü süreç ve bağlantı başlangıcı sorguların kendisini gölgede bırakıyordu. Tek bir kalıcı Python istemcisi gürültüyü ortadan kaldırdı.
Hata mesajlarında bir yükseltme tuzağı
MongoDB 8.3 ayrıca bir pipeline içinde rastgele bir ObjectId üreten $createObjectId'yi de getiriyor; tam olarak tek bir argüman, boş bir nesne kabul ediyor; mevcut bir değeri dönüştürmek için $toObjectId gerekli. Ayrıca as değişkeni ile arrayIndexAs aynı adı paylaşamaz.
Daha sivri tuzak yükseltmelerle ilgili. Binary'leri 8.3'te olan ama featureCompatibilityVersion'ı hâlâ 8.2 olan bir cluster'da $createObjectId, dokümantasyona işaret eden açık bir uyumluluk hatasıyla başarısız olurken, arrayIndexAs bir yazım hatası gibi okunan, uyumluluktan hiç söz etmeyen genel bir Unrecognized parameter to $map mesajıyla başarısız oluyor. setFeatureCompatibilityVersion'ı 8.3'e çalıştırmak ikisini de anında düzeltiyor — yazara göre boşluk, çarede değil hata mesajında.
Zaten var olan bir index değişkeni
MongoDB dokümantasyonu, arrayIndexAs bildirilmese bile $$IDX'nin $map içinde geçerli index'i tuttuğunu belirtiyor ve yazar bunu doğrudan doğrulamış: yalnızca $$IDX'yi projeksiyonlamak 0'dan n−1'e kadar değerler döndürmüş. Bu, yeni parametreyi büyük ölçüde index'e özel bir ad vermek için bir okunabilirlik özelliği yapıyor, konumsal bilgiye giden tek yol değil.
Neden önemli
Değişikliğin kendisi küçük — tek bir aggregation operatöründe isteğe bağlı bir alan — ama muhtemelen binlerce pipeline'a kopyalanmış bir yapıyı emekliye ayırıyor ve ölçümler bu yapının gerçek bir bedeli olduğunu gösteriyor: önemli olacak kadar büyük dizilerde tutarlı bir yüzde 15-31'lik yük. Sayılara ilişkin çerçeveleme de aynı derecede faydalı. Tek bağlantılı benchmarklar eşzamanlı yük altındaki faydayı abartıyor, beklenen ikinci dereceden maliyet doğrusal çıktı ve yanıltıcı bir hata mesajı — özellik değil — 8.3'e yükselen ekipler için zaman kaybının en olası kaynağı. Bu, sorgu dillerindeki ergonomik kazanımların ölçülmeyi hak ettiğinin ve ölçümün de titizlikle incelenmeyi hak ettiğinin güzel bir hatırlatıcısı.
- #mongodb
- #aggregation
- #databases
- #benchmarks
- #nosql