Ana içeriğe atla

Veri Bilimi Okulu

Bloom Filter Nedir? Arrow’un Yeni Çözümü: Bloom Filter Folding
Bloom Filter Nedir? Arrow’un Yeni Çözümü: Bloom Filter Folding
bloom_filter_folding_kapak_960x640

Bir bloom filter’ı ne kadar büyük yapacağınıza karar vermek için kaç farklı değer geleceğini bilmeniz gerekir. Ama yazmaya başlarken bunu bilmezsiniz. Arrow’un yeni çözümü şu: önce bol bol yer ayır, sonra katla.

Başlığa bakıp “tamam da bloom filter neydi?” diyorsanız, iki cümlede halledelim: bloom filter, tek bir soruya cevap veren minik bir veri yapısıdır — “bu değer bu kümenin içinde var mı?” Verdiği cevap ise ya “kesinlikle yok”tur ya da “muhtemelen var”. Arada üçüncü bir seçenek yoktur[1][2].

Bunu bir depo düşünerek canlandırın. 500 kapalı koli var, birinde aradığınız parça. Her kolinin kapağında minik bir etiket olsa ve bu etiket “bu kolide o parça kesinlikle yok” diyebilse, o koliyi hiç açmadan geçersiniz. Etiket “olabilir” diyorsa açar bakarsınız — bazen boşuna açmış olursunuz, ama asla dolu bir koliyi yanlışlıkla atlamazsınız. Bloom filter tam olarak bu etiket: küçücük, hızlı, bazen fazladan iş çıkarır ama sizi hiçbir zaman yanlış cevaba götürmez.

Peki bu neden bir veri mühendisini ilgilendirsin? Çünkü o koliler Parquet dosyalarınızın içindeki row group‘lar (satır grupları) ve o etiketleri okumak, terabaytlarca veriyi hiç açmadan geçmek anlamına geliyor. Klasik bir “samanlıkta iğne” sorgusuna bakalım:

SELECT *
FROM orders                    -- 2 TB, S3'te Parquet
WHERE customer_id = 'a3f9-...' -- milyarlarca satırda birkaç kayıt

Bu sorgunun hızını belirleyen şey hesaplama değil, ne kadar veriyi hiç okumadan atlayabildiğiniz. Parquet’in bunun için birkaç aracı var: partition pruning (bölüm budama), row group (satır grubu) seviyesindeki min/max istatistikleri, page index. Ama customer_id gibi yüksek kardinaliteli (high-cardinality) bir kolonda min/max istatistiği çuvallar: her row group’un min’i kabaca “aaa…”, max’ı kabaca “zzz…” çıkar; aradığınız değer hepsinin aralığına düşer ve hiçbir şey atlanamaz[25].

İşte bloom filter tam bu boşluğu doldurmak için Parquet’e girdi[2]. Ve Nisan 2026’da Pydantic ekibinin Apache Arrow’a katkı olarak gönderdiği bloom filter folding, bu yapının yıllardır süren en can sıkıcı ayar derdini ortadan kaldırdı[1]. Arrow C++ tarafında da 25.0.0 sürümüyle varsayılan hale geldi[3][6].

Bu yazıda önce bloom filter’ın içini açacağız: bitler ve hash’ler nasıl işliyor, Parquet hangi varyantı seçmiş ve boyutlandırma ikilemini göreceğiz; sonra folding’in nasıl çalıştığını ve neden matematiksel olarak güvenli olduğunu adım adım açacağız. Finalde de asıl merak ettiğimiz soruya geleceğiz: peki Spark ile yazıp okuyanlar için bu ne anlama geliyor? (Spoiler: hikâye orada biraz farklı işliyor ve Spark’ın iki ayrı bloom filter’ı var; ikisini karıştırmak çok kolay.)

Terminoloji notu: Yazı boyunca row group (satır grubu), bloom filter, fpp (yanlış pozitif olasılığı), NDV (farklı değer sayısı) gibi terimleri İngilizce bırakıyorum; sahada herkes böyle konuşuyor. Folding için Türkçe “katlama” da kullanacağım, çünkü kelimenin kendisi zaten yapılan işi anlatıyor.

1. Peki Bu Etiket İçeride Nasıl Çalışıyor?

Girişteki “kesinlikle yok / muhtemelen var” asimetrisi işin bütün sırrı, o yüzden bir daha altını çizelim: yanlış negatif (false negative) yoktur. Filtre “yok” diyorsa değer gerçekten yoktur ve o row group’u hiç açmadan atlayabilirsiniz. Yanlış pozitif (false positive) olabilir — filtre, olmayan bir değer için ara sıra “muhtemelen var” der — ama bunun bedeli yalnızca boşa giden bir okumadır, asla yanlış sonuç değil[1][2].

Bu garantinin nereden geldiğini görmek için mekaniğe inelim, çünkü yazının geri kalanı — özellikle folding kısmı — tamamen bu mekaniğin üstüne kurulu. Klasik bir bloom filter, bir bit dizisi ve birkaç hash fonksiyonundan ibarettir.

Parquet’te her row group’un her kolonu için ayrı bir bloom filter tutulur[2]. WHERE customer_id = 'a3f9' sorgusunda motor, kolon verisine dokunmadan önce her row group’un filtresine sorar; “kesinlikle yok” diyen her row group tamamen atlanır: sıkıştırma açma yok, I/O yok, hiçbir şey yok. Yüksek kardinaliteli kolonlarda bu, verinin %99’unu atlamak anlamına gelebiliyor[1]. Teorik bir iddia da değil bu: DuckDB ekibi point lookup’larda 50 kata varan hızlanma ölçtü[11], InfluxData 30 kata varan kazanımlar raporladı[12].

2. Parquet’in Tercihi: Split Block Bloom Filter

Ders kitabındaki bloom filter’ın modern donanımda bir sorunu var: k tane biti bit dizisinin her yerine saçılmış durumda, yani tek bir sorgu k farklı rastgele bellek adresine dokunuyor. Her biri potansiyel bir cache miss. Milyonlarca değer kontrol ediyorsanız bu, bin bıçak darbesiyle ölmek demek.

Parquet bu yüzden Split Block Bloom Filter (SBBF) denen, önbellek dostu bir varyant kullanıyor[2][7]. Fikir şu: bir değerin bitlerini filtrenin geneline saçmak yerine, hepsini tek bir block‘un içine hapset. Her block 256 bit — tam iki cache line boyu ve SIMD komutlarına birebir uygun. Bir değer tek bir block’a dokunur, yani bir sorgu esasen tek bir bellek erişimidir.

Yerleştirme şöyle işliyor. Filtre z tane block’tan oluşur; değerin 64-bit hash’i (xxHash[10]) ikiye bölünür:

  • Üst 32 bit hangi block’a gidileceğini seçer. Pahalı bir modulo yerine spec bir çarp-kaydır (multiply-shift) numarası kullanır: i = ((h >> 32) * z) >> 32. Bu, hash’i [0, z) aralığına düzgün dağıtır[2][8] — ve birazdan göreceğiz ki folding’i mümkün kılan tam olarak bu formül.
  • Alt 32 bit block’un içinde hangi 8 bitin set edileceğine karar verir. Her block, 32 bitlik sekiz word’den oluşur; sekiz tek sayılık “salt” sabiti kullanılarak her word’de tam olarak bir bit set edilir[2].
Şekil 2 — SBBF’de hash ikiye bölünür: üst yarı block’u seçer, alt yarı block içindeki 8 biti belirler. Bir değer = 8 bit = 1 block; bu yüzden lookup tek bellek erişimine iner [2][7][8].

3. Boyutlandırma İkilemi: Yazmadan Önce Bilemediğiniz Sayı

# Yazarken karar vermeniz gereken şey:
# "Bu row group'ta kaç farklı değer olacak?"  → NDV
# Cevabı ancak yazma bitince öğrenirsiniz.

İşte asıl dert burada. SBBF olsun olmasın, her bloom filter için ne kadar büyük olacağına karar vermeniz gerekir ve doğru boyut, kaç farklı değer tutacağına bağlıdır.

Farklı değer sayısı (NDV, number of distinct values) doluluk oranını (fill rate) belirler: bitlerin ne kadarı 1 olur. Küçük bir filtreye çok değer koyarsanız neredeyse her bit 1 olur; filtre her şeye “muhtemelen var” demeye başlar — doymuştur (saturated) ve tamamen işe yaramazdır. Bir SBBF block’unun yanlış pozitif olasılığı kabaca doluluk oranının 8. kuvvetiyle ölçeklenir (her word için bir çarpan), yani uçurum diktir[1].

Ters yöne gidip filtreyi ihtiyacınızdan çok büyük yaparsanız kusursuz çalışır — ama artık her dosyada, milyarlarca satır boyunca, çoğu boş bir bit dizisinin parasını ödüyorsunuz.

Şekil 3 — Doluluk oranı ile fpp arasındaki ilişki dik bir eğri: değer başına düşen bit sayısı 10’dan 5’e inince yanlış pozitif oranı %1,26’dan %18’e sıçrar [2][1].

Parquet spec’i bu ilişkiyi net bir tabloyla veriyor[2]:

Eklenen değer başına bitYanlış pozitif olasılığı (fpp)
6,0%10
10,5%1
16,9%0,1
26,4%0,01
41%0,001

Sorun şu ki NDV’yi baştan nadiren bilirsiniz. Bir row group’ta 10 farklı event_type olabilir, bir sonrakinde 100.000 farklı trace_id. Writer’ların bu belirsizliğe verdiği cevaplar tarihsel olarak üç tanedir ve hiçbiri bedava değildir[1]:

  • Sabit bir tahmin kullan. arrow-rs’in folding öncesi yaptığı buydu: ortalamada makul, pratikte sık sık yanlış — ya şişkin ya doymuş filtreler.
  • Örnekleme yapıp NDV tahmin et. DuckDB böyle yapıyor; fazladan bir geçiş ve karmaşıklık demek.
  • Birden çok boyut yaz, sonra birini seç. Daha fazla bellek, daha fazla iş. (Aklınızda tutun — Spark bölümünde bu maddeye geri döneceğiz.)

4. Folding: Veriyi Gördükten Sonra Boyutlandır

# arrow-rs / Arrow C++ 25.0.0 sonrası varsayılan davranış
# 1) cömert bir filtre ayır  2) hepsini yaz  3) doluluğu ölç  4) katla

Pydantic ekibinin Arrow’a kattığı optimizasyon işlem sırasını tersine çeviriyor. Boyutu yazmadan önce tahmin etmek yerine, fazlasıyla büyük bir filtreyle başlıyor, bütün değerleri içine yazıyor ve ne kadar dolduğunu tam olarak öğrendikten sonra küçültüyor. Bu küçültme adımının adı folding[1][5].

Folding, komşu block çiftlerini bit düzeyinde OR ile birleştirerek block sayısını yarıya indirir:

block[i] = block[2i] | block[2i + 1]

Her katlama boyutu yarılar; tekrar tekrar katlayabilirsiniz: 8 block → 4 → 2 → 1. OR hiçbir zaman bir biti sıfırlamadığı için, iki kaynak block’un herhangi birinde set olan her bit sonuçta da set kalır[4].

Şekil 4 — Folding, komşu block çiftlerini OR’layarak filtreyi yarılar. Örnekte 4 numaralı block’a düşen değer, iki katlama sonrası 4|5|6|7 block’unda; bitleri hâlâ orada. Katlanmış filtre, baştan o boyutta yazılmış olsaydı elde edilecek filtreyle eşdeğerdir [1][4][5].

Ne kadar katlanacağına nasıl karar veriliyor?

Yazma bittikten sonra implementasyon, block’lar genelindeki gerçek ortalama doluluk oranını ölçüyor ve hedeflenen fpp’nin altında kalarak kaç kez katlayabileceğini hesaplıyor. Her katlama block başına düşen değer sayısını kabaca ikiye katlar, yani fpp her adımda öngörülebilir biçimde tırmanır; algoritma hedefi aşmadan atabildiği kadar adım atar[1][4]. Özet akış şu:

1. Cömert bir filtre ayır (row group'un maksimum satır sayısından türetilir)
2. Bütün değerleri yaz — filtre çoğu zaman kapasitesinin çok altında dolar
3. Bütün block'larda gerçek doluluk oranını ölç
4. fpp ≤ hedef koşulu bozulmadan, katlayabildiğin kadar katla (ikişerli OR)
5. Doğru boyuttaki filtreyi dosyaya serileştir

Pratikte bunun anlamı: düşük kardinaliteli kolonlar dramatik biçimde küçülür (50 farklı değeri olan bir kolona milyonluk filtre gerekmez), yüksek kardinaliteli kolonlar etkili kalır (doyup işe yaramaz hale gelmek yerine hedef fpp’yi koruyan boyuta iner) ve hiçbir ayar gerekmez[1]. Arrow C++ tarafında bu davranış BloomFilterOptions içindeki fold alanıyla geliyor ve varsayılanı true; artık gerçekte olduğundan büyük bir NDV vermeniz disk kullanımını şişirmiyor[6].

Bedeli ne?

Folding bedava değil ama bedavaya yakın. Tek gerçek maliyet yazma anındaki bellek: katlayana kadar cömert filtreyi RAM’de tutmanız gerekiyor — bloom filter başına ~1 MiB mertebesinde[1]. Kolon verisinin ve diğer yazma tamponlarının yanında bu bir yuvarlama hatası.

Hesaplama maliyeti ise doluluğu ölçmek için tek bir popcount taraması, artı logaritmik sayıda OR geçişi. PR’daki ölçümlerde, 1 milyon NDV için kurulmuş ama gerçekte 100 bin değer alan bir filtreyi katlamak, ~190 µs’lik ekleme işine yaklaşık 27 µs eklemiş — o adımda yaklaşık %14’lük bir ek yük[1][5]. Kolonu asıl kodlayıp sıkıştırma maliyetinin yanında görünmez. Karşılığında okuma tarafında sonsuza kadar daha küçük ve daha keskin bir filtre elde ediyorsunuz.

5. Peki Spark? Önce Kötü Haber

df.write \
  .option("parquet.bloom.filter.enabled#customer_id", "true") \
  .option("parquet.bloom.filter.expected.ndv#customer_id", "45000000") \
  .parquet("s3://datalake/orders/")

Şimdi asıl meseleye gelelim, çünkü bu blogun okuyucularının çoğu Parquet’i Rust ya da C++ ile değil, Spark ile yazıyor. Ve burada önemli bir ayrım var:

Spark, Parquet dosyalarını yazarken parquet-java (eski adıyla parquet-mr) kütüphanesini kullanır — Arrow’un Rust ya da C++ implementasyonunu değil. Yani yukarıda anlattığımız folding, bugün itibarıyla Spark’ın df.write.parquet(...) çağrısında devreye girmiyor. Pydantic ekibine göre parquet-java geliştiricileri yaklaşımı taşımaya ilgi gösterdi, ama bu henüz olmuş bir şey değil[1].

Yine de bu, konunun Spark tarafında karşılıksız olduğu anlamına gelmiyor. Aksine: parquet-java’nın aynı probleme kendi cevabı var ve az bilinen bir konfigürasyon olarak yıllardır orada duruyor.

Spark tarafındaki üç ayar seti

Parquet writer’ının bloom filter ayarları Spark’ta .option(...) ile ya da --conf ile veriliyor. Önce varsayılanlar[13][14]:

AyarVarsayılanNe yapar
parquet.bloom.filter.enabledfalseBloom filter yazımını açar. #kolon_adı ekiyle kolon bazında verilebilir.
parquet.bloom.filter.expected.ndvKolondaki beklenen farklı değer sayısı. Optimal boyut bundan hesaplanır.
parquet.bloom.filter.fpp0.01Hedef yanlış pozitif olasılığı. NDV verilmezse dikkate alınmaz.
parquet.bloom.filter.max.bytes1 MBBitset için üst sınır.
parquet.bloom.filter.adaptive.enabledfalseUyarlanabilir mod: boyut, gerçek farklı değer sayısına göre belirlenir.
parquet.bloom.filter.candidates.number5Adaptive modda aynı anda yazılan aday filtre sayısı.

Son iki satır önemli. parquet-java’nın “adaptive bloom filter” özelliği, bölüm 3’te saydığımız üçüncü stratejinin ta kendisi: birden çok aday filtreyi aynı anda doldur, sonunda optimal bit boyutuna sahip olanı seç ve dosyaya onu yaz[13][15]. Arrow’un folding’iyle aynı derdi çözüyor, ama farklı bir takasla: folding tek bir büyük filtre tutup katlarken, adaptive mod 5 ayrı filtreyi paralel besliyor. İkisi de “NDV’yi önceden bilmek zorunda kalmayalım” diyor; birincisi bellekte daha ucuz ve doğrudan varsayılan, ikincisi hâlâ elle açmanız gereken bir bayrak.

Şekil 5 — Aynı derde iki farklı cevap. Arrow tek filtreyi katlayarak küçültür (varsayılan açık); parquet-java aynı anda birkaç aday filtre doldurup en uygununu seçer (parquet.bloom.filter.adaptive.enabled, varsayılan kapalı) [1][6][13][15].

Iceberg tablosu kullanıyorsanız ayarlar tablo property’lerine taşınıyor: write.parquet.bloom-filter-enabled.column.<kolon>, write.parquet.bloom-filter-max-bytes (varsayılan 1 MB) ve write.parquet.bloom-filter-fpp.column.<kolon>[23]. Burada bilinmesi gereken bir incelik var: Iceberg spec’i bir NDV property’si tanımlamadığı için, parquet-java tarafında fpp değeri fiilen yok sayılıyor ve filtre max-bytes kadar sabit boyutta ayrılıyor[24][25]. Yani “fpp’yi 0.001 yaptım, artık daha isabetli” varsayımı Iceberg’de sizi yanıltabilir — gerçek fpp’yi belirleyen şey sabit boyut ve içine giren farklı değer sayısı oluyor. Folding’in çözdüğü problemin ta kendisi, sadece başka bir kılıkta.

6. Okuma Tarafı: Spark Row Group’ları Nasıl Atlıyor?

-- Yazarken filtre üretilmişse, okurken kendiliğinden kullanılır
SELECT * FROM orders WHERE customer_id = 'a3f9-...'
-- Spark UI → SQL sekmesi → "number of row groups read" metriğine bakın

Filtre dosyaya yazıldıysa, okuma tarafında Spark bunu kullanmak için ekstra bir şey yapmanızı istemez. Akış şöyle: filtre predicate’ı Parquet’e itilir (predicate pushdown, spark.sql.parquet.filterPushdown varsayılan açık[16]), okuyucu her row group için sırasıyla partition pruning → min/max istatistikleri → column index → bloom filter zincirini uygular ve elenebilen row group’ları hiç açmaz[22][26]. Bloom filter’ın konumu ve uzunluğu kolon metadata’sında (bloom_filter_offset, bloom_filter_length) durduğu için, filtre tek bir I/O ile çekilebilir[2].

Şekil 6 — Okuma tarafındaki eleme zinciri. Bloom filter en sonda ve en dar kapsamlı halka: sadece eşitlik sorgularında, ama tam da min/max’ın işe yaramadığı yerde devreye giriyor [2][22][25].

7. Karıştırmayın: Spark’ın Diğer Bloom Filter’ı

-- Bu ayarın Parquet dosyalarındaki bloom filter'la HİÇBİR ilgisi yok
SET spark.sql.optimizer.runtime.bloomFilter.enabled = true;

Spark dünyasında “bloom filter” dediğinizde çoğu kişinin aklına gelen şey aslında bambaşka bir özellik: runtime bloom filter join (SPARK-32268, Spark 3.3 ile geldi, Spark 3.4’ten beri varsayılan olarak açık)[17][18][19].

Fikir şu: bir shuffle join’in bir tarafında seçici bir predicate varsa, Spark o taraftaki join key’lerinden çalışma zamanında (runtime) bir bloom filter üretir ve bunu diğer tarafa enjekte eder; böylece shuffle’a girmeden önce eşleşmeyecek satırlar elenir ve ağdan geçen veri azalır[20][21]. Filtre bir BloomFilterAggregate düğümüyle kuruluyor, kontrol BloomFilterMightContain ifadesiyle yapılıyor[20].

İki mekanizma da aynı matematiği kullanıyor ama tamamen farklı şeylerdir:

Parquet bloom filterRuntime bloom filter join
Nerede yaşarParquet dosyasının içinde, kalıcıSorgu boyunca bellekte, geçici
Ne zaman kurulurYazma anında (bir kez)Her sorguda yeniden
Neyi azaltırDiskten/S3’ten okunan veri (row group atlar)Shuffle’dan geçen veri
Nasıl açılırparquet.bloom.filter.enabled#kolon (kapalı)spark.sql.optimizer.runtime.bloomFilter.enabled (3.4+ açık)
Folding etkiler miEvet — ama parquet-java’ya ininceHayır, ilgisiz

Runtime tarafında ayarlanabilecek şeyler de var: expectedNumItems (varsayılan 1.000.000), numBits (8.388.608), maxNumItems (4.000.000), maxNumBits (67.108.864) ve devreye girme eşikleri — filtreyi üreten tarafın creationSideThreshold‘dan (10 MB) küçük, uygulanan tarafın applicationSideScanSizeThreshold‘dan (10 GB) büyük olması gibi[19][20]. Bir join’de bloom filter beklediğiniz halde planda göremiyorsanız, ilk bakılacak yer bu eşikler.

8. Sahada: Ne Zaman Açmalı, Neye Dikkat Etmeli

Bütün bunları toparlayıp pratik bir kontrol listesine indirgeyelim.

Bloom filter’ı şu durumlarda açın: Sorgularınızda eşitlik predicate’ı var (=, IN); kolon yüksek kardinaliteli (UUID, trace_id, user_id, session_id); ve aradığınız değer dosyaların çoğunda yok. Son madde kritik: bloom filter en çok, aranan değeri içermeyen dosyaları elerken kazandırır[25].

Beklentinizi düşürün:

  • Range sorguları, LIKE, IS NULL, != için işe yaramaz. Bloom filter’ın sıralama kavramı yoktur; aralık sorguları min/max ve column index’e kalmıştır[25].
  • Düşük kardinaliteli kolonlarda anlamsız. Zaten dictionary encoding ve min/max işi görüyor; bloom filter sadece dosyayı şişirir.
  • Sadece açmak yetmez, NDV’yi de verin. parquet-java’da NDV verilmezse fpp yok sayılır[24]; filtre ya doyar ya da max.bytes kadar yer kaplar. Tam da folding’in ortadan kaldırdığı zahmet.
  • Dosya boyutu artar. CERN’in testinde aynı DataFrame, bloom filter ile ~10,1 MB, filtresiz ~8,0 MB çıkmış[22]. Küçük veri setlerinde kazanç maliyeti karşılamayabilir.
  • Ölçmeden inanmayın. Küçük tablolarda ve zaten sıralı verilerde bloom filter’lı okuma filtresiz okumadan yavaş bile çıkabilir; row group sayısı ve seçicilik olmadan kazanç yoktur.

Nasıl doğrularsınız? parquet-cli ile dosyaya doğrudan sorabilirsiniz: parquet bloom-filter dosya.parquet -c customer_id -v 'a3f9-...' komutu size “value maybe exists” ya da “NOT exists” der. Rust tarafında da parquet-show-bloom-filter aynı işi görüyor[27]. Filtre gerçekten yazılmış mı sorusunun en hızlı cevabı budur.

Kapanış: Küçük Optimizasyonların Güzelliği

Folding’i sevmemin sebebi yeni bir şey icat etmemesi. Ortada yeni bir veri yapısı, yeni bir format sürümü, yeni bir uyumluluk derdi yok — katlanmış bir SBBF, baştan o boyutta yazılmış bir SBBF’den ayırt edilemez; okuyucu tarafında değişen hiçbir şey yok. Sadece işlem sırası değişti: tahmin etmek yerine ölçmeye geçildi. Kazanç da o kadar: daha küçük dosyalar, aynı fpp, sıfır ayar.

Spark tarafında bugün elimizde bu yok; bunun yerine adaptive.enabled bayrağı ve elle verilen NDV’ler var. Ama parquet-java tarafında ilginin oluştuğu söyleniyor[1] ve bu tür şeyler ekosistemde genelde tek yönlü akar: bir implementasyonda işe yarayan bir numara, birkaç sürüm sonra diğerlerinde varsayılan olur. Arrow’un Rust ve C++ tarafları folding’i varsayılan yaptığına göre, bir sonraki adımın Java writer olması sürpriz olmaz.

O gün gelene kadar yapılacak iş belli: bloom filter yazdığınız kolonlarda NDV’yi elinizle verin, Iceberg kullanıyorsanız max-bytes‘ın fpp’yi ezdiğini bilin ve dosyanızı yazdıktan sonra parquet-cli ile bir kez kontrol edin. Bir sonraki yazıda bu ayarları gerçek bir tabloda deneyip Spark UI’da okunan row group sayısının nasıl değiştiğine bakacağız. O zamana kadar — filtreleriniz küçük, seçicilikleriniz yüksek olsun!


Kaynaklar

  1. Pydantic — Smarter Bloom Filters in Parquet: How Filter Folding Speeds Up Point Lookups in Logfire
  2. Apache Parquet Documentation — Bloom Filter
  3. Apache Arrow — 25.0.0 Release
  4. arrow-rs — parquet/src/bloom_filter/mod.rs
  5. arrow-rs — PR #9628: Bloom filter folding
  6. Apache Arrow — GH-50007: [C++][Parquet] Add bloom filter folding to automatically size SBBF filters
  7. Putze, Sanders, Singler — Cache-, Hash- and Space-Efficient Bloom Filters
  8. Kenneth A. Ross — Efficient Hash Probes on Modern Processors (IBM Research Report)
  9. Broder & Mitzenmacher — Network Applications of Bloom Filters: A Survey
  10. xxHash — Specification v0.1.1
  11. DuckDB — Parquet Bloom Filters in DuckDB
  12. InfluxData — Using Parquet’s Bloom Filters
  13. parquet-java — parquet-hadoop Configuration Reference
  14. parquet-java — ParquetProperties.java (varsayılan değerler)
  15. parquet-java — ParquetOutputFormat.java
  16. Apache Spark Documentation — SQL Performance Tuning
  17. Apache Spark Documentation — SQL Migration Guide
  18. Apache Spark — Release Notes 3.3.0
  19. ASF Jira — SPARK-32268: Bloom Filter Join
  20. Bartosz Konieczny (waitingforcode) — What’s New in Apache Spark 3.3: Joins
  21. The Internals of Spark SQL — Bloom Filter Join
  22. CERN Databases Blog — Enhancing Apache Spark and Parquet Efficiency: Column Indexes and Bloom Filters
  23. Apache Iceberg Documentation — Configuration (Write Properties)
  24. Apache Iceberg dev list — [DISCUSS] Spec Issue on Bloom Filter Properties
  25. Cazpian — Iceberg Bloom Filters with Spark: Configuration, Validation, and Performance
  26. Songkunjump — Parquet Bloom Filter with Spark
  27. arrow-rs — Issue #3023: Support bloom filter reading and writing for parquet
  28. Trino — Issue #21570: Write support for Bloom filters in Iceberg and Delta Lake connectors
  29. Kapak görseli – Photo by Nathan Dumlao on Unsplash

Bir yanıt yazın