Canlı Bitmap

Bitmap Nedir?
Buradaki bitmap, bir indeks türüdür. Grafik işleme bağlamında geldiyseniz geri dönebilirsiniz.
Temel Kavram
Bitmap, veritabanlarında kullanılan bir indeksleme tekniği türüdür. Belirli bir sütunun değerlerini bit dizisi veya vektörün bir öğesi biçiminde temsil eder ve her bit, ilgili sütunda belirli bir değerin var olup olmadığını gösterir. Dolayısıyla en temel biçimi, her satıra bir bit indeksi atamak ve varlığını 0 ve 1 ile ifade etmektir.
Örneğin, cinsiyet sütunu bulunan bir tablo olduğunu varsayalım. Bu tabloda 5 satır bulunsun ve cinsiyet sütununun değerleri şöyle olsun:
- Erkek
- Kadın
- Erkek
- Kadın
- Diğer
Bu durumda, cinsiyet sütunu için bitmap indeksi şu şekilde gösterilebilir:
- Erkek: 10100
- Kadın: 01010
- Diğer: 00001
Gerçekte Nerede Kullanılır
Elbette bunun yalnızca bu değerleri göstermek için olmadığını herkes düşünecektir. Bitmap indeksini düzgün biçimde kullanmak için aşağıdaki durumu ele alalım.
1CREATE TABLE IF NOT EXISTS Users (
2 id INT PRIMARY KEY,
3 name VARCHAR(100),
4 active BOOLEAN,
5 sex BOOLEAN,
6 age INT
7);
Bu tabloda 1.000.000 satır bulunduğunu varsayalım. Ayrıca aşağıdaki sorguyu çalıştırdığımızı düşünelim.
1SELECT * FROM Users WHERE active = TRUE AND SEX = TRUE;
Bu sorgu, active ve sex sütunlarının her ikisinin de TRUE olduğu satırları bulur. Bu durumda, iki sütunun da TRUE olduğu satırları en hızlı şekilde bulmak için bit AND işlemi kullanılabilir.
- active sütunu için bitmap indeksi önceden
1100110011...(1.000.000 bitlik bir dizi) olarak indekslenmiş olsun, - sex sütunu için bitmap indeksi önceden
1010101010...(1.000.000 bitlik bir dizi) olarak indekslenmiş olsun.
Bu durumda iki bitmap indeksi üzerinde bit AND işlemi gerçekleştirilerek, iki sütunun da TRUE olduğu satırlar hızlı biçimde bulunabilir. Basit bir bit işlemi olduğu için, diğer indeks taramalarına kıyasla sonucu çok daha hızlı elde edilebilir.
Ödünleşimler
Bitmap indeksi çok verimli olabilir, ancak bazı ödünleşimleri vardır. Birçok başka dezavantajı olmakla birlikte şimdilik yalnızca birini ele alacağım.
Bu da bitmap indeksinin düşük kardinalite (yani sütunda olası değerlerin sayısının az olduğu durumlar) için daha uygun olmasıdır. Yüksek kardinaliteli bir sütunda bitmap indeksi kullanılırsa, bitmap çok büyüyebilir.
Yüksek kardinaliteli bir sütunda bitmap indeksi kullanıldığında, her benzersiz değer için ayrı bir bitmap oluşturulmak zorunda olduğundan depolama alanına ihtiyaç duyulabilir. Örneğin, bir sütunda 1.000.000 benzersiz değer varsa, 1.000.000 bitmap oluşturulması gerekir ve bu da oldukça verimsiz olabilir.
Bu Noktada Biz
Yüksek kardinalitede ortaya çıkan bitmap sorununu gidermek için Roaring Bitmap adlı yapı kullanılabilir. Roaring Bitmap'in birçok özelliği vardır, ancak en temel felsefesi ve özelliği veri yoğunluğuna göre en uygun depolama yöntemini dinamik olarak seçerek hem sıkıştırma oranını hem de işlem hızını en üst düzeye çıkarmaktır.
Roaring Bitmap'in Temel Kavramı
İki Aşamalı İndeksleme
Roaring Bitmap, bir tam sayıyı saklamak için önce şu iki aşamayı izler. İki aşamalı indeksleme demesi de olsa, aslında bunu iki boyutlu bir dizi olarak düşünebilirsiniz.
- Üst 16 bitin çıkarılması: Tam sayının üst 16 biti çıkarılır ve indeks olarak kullanılır. Uzunluğu toplam 65536 olan iki boyutlu dizinin dış dizisinin indeksi olur. Bu dizi, aşağıda açıklanacak konteynerin adresini saklar.
- Alt 16 bitin çıkarılması: Tam sayının alt 16 biti çıkarılır ve konteyner içindeki konum olarak kullanılır.
Konteyner
Bu konteyner, az önce benzetmesini yaptığımız iki boyutlu dizinin iç dizisine karşılık gelir. Ancak üst 16 bitin tuttuğu diziden farklı olarak, bu konteynerin iç yapısı içinde bulunan veri miktarına göre değişir. Roaring Bitmap şu 3 konteyner türünü destekler.
- Array Container: Bu konteyner, içinde az veri saklandığında kullanılır. Somut olarak, 4096 veya daha az öğe saklandığında kullanılır. Bu konteyner basitçe sıralanmış bir tam sayı dizisi olarak uygulanmıştır. Öğeyi bulmak için ikili arama yapılabilir.
- Bitmap Container: Bu konteyner, içinde çok veri saklandığında kullanılır. Somut olarak, 4096'dan fazla öğe saklandığında kullanılır. Bu konteyner 65536 bitlik (8192 bayt) bir bitmap olarak uygulanmıştır. Her bit, ilgili konumdaki tam sayının var olup olmadığını gösterir.
- Run Container: Bu konteyner, ardışık tam sayı aralıklarını saklamak için kullanılır. Örneğin 1, 2, 3, 4, 5 gibi ardışık tam sayıları saklarken verimlidir. Bu konteyner (başlangıç değeri, uzunluk) çiftlerinden oluşan bir liste olarak uygulanmıştır. Peki ya hepsi mevcutsa? Bu, baştan sona her şeyin mevcut olduğu anlamına gelir; bu durumda yalnızca (0, 65536) saklanması yeterlidir.
Bu yöntem, Roaring Bitmap'in belleği çok verimli kullanmasını ve farklı veri yoğunlukları için en iyi performansı sunmasını sağlar.
Roaring Bitmap'in İşlevleri
Temel İşlevler
Roaring Bitmap aşağıdaki temel işlevleri sağlar.
- Ekleme: Bitmap'e yeni bir tam sayı ekler. Bu süreçte uygun konteyner seçilebilir veya gerektiğinde konteyner dönüştürülebilir.
- Silme: Bitmap'ten mevcut bir tam sayıyı kaldırır. Bu süreçte de konteynerin durumuna göre uygun işlem yapılır.
- Arama: Belirli bir tam sayının bitmap içinde var olup olmadığını kontrol eder. Bu işlem çok hızlı gerçekleştirilir.
Küme İşlemleri
Roaring Bitmap ayrıca aşağıdaki küme işlemlerini destekler.
- Birleşim (Union): İki bitmap'in tüm öğelerini içeren yeni bir bitmap oluşturur.
- Kesişim (Intersection): Yalnızca iki bitmap'te birlikte bulunan öğeleri içeren yeni bir bitmap oluşturur.
- Simetrik Fark (Symmetric Difference): İki bitmap'ten yalnızca birinde bulunan öğeleri içeren yeni bir bitmap oluşturur.
Bu işlemler, bitmap'in konteyner türüne göre optimize edildiği için çok hızlı gerçekleştirilir.
Go Dilinde
Go dilinde github.com/RoaringBitmap/roaring paketini kullanarak Roaring Bitmap'i kolayca kullanabilirsiniz. Bu paket, Roaring Bitmap'in tüm işlevlerini sağlar ve Go dilinin özelliklerine uygun şekilde optimize edilmiştir.
Kurulum
1go get github.com/RoaringBitmap/roaring/v2
Kullanım Örneği
1package main
2
3import (
4 "log"
5
6 "github.com/RoaringBitmap/roaring/v2"
7)
8
9func main() {
10 b1 := roaring.BitmapOf(1, 2, 3, 4) // 1, 2, 3, 4 ile başlat
11 b2 := roaring.BitmapOf(2, 4, 6, 8, 12) // 2, 4, 6, 8, 12 ile başlat
12
13 b1.Add(5) // 5 ekle
14 b2.Add(10) // 10 ekle
15
16 b2.Remove(12) // 12 kaldır
17
18 and := roaring.FastAnd(b1, b2)
19 or := roaring.FastOr(b1, b2)
20
21 // bitmap işlemleri orijinali değiştirdiği için kopya oluşturup kullan
22 xor := b1.Clone()
23 xor.Xor(b2)
24
25 log.Printf("and result: %v", and.ToArray())
26 log.Printf("or result: %v", or.ToArray())
27 log.Printf("xor result: %v", xor.ToArray())
28}
12025/09/08 22:16:27 and result: [2 4]
22025/09/08 22:16:27 or result: [1 2 3 4 5 6 8 10]
32025/09/08 22:16:27 xor result: [1 3 5 6 8 10]
Yukarıdaki örnek kodda yer almayan başlıca işlevler şunlardır:
Contains(x uint32) bool: Belirli bir değerin bitmap içinde var olup olmadığını kontrol ederGetCardinality() uint64: Bitmap'te bulunan öğe sayısını döndürürRank(x uint32) uint64: Belirli bir değerden küçük veya eşit öğe sayısını döndürürToBytes() ([]byte, error): Bitmap'i bayt dilimine serileştirirFromBytes(data []byte) error: Bitmap'i bayt diliminden geri yüklerClone() *Bitmap: Bitmap'in kopyasını oluştururClear(): Bitmap'i temizlerNextValue(x uint32) int64: x'ten büyük veya eşit bir sonraki öğeyi döndürürPreviousValue(x uint32) int64: x'ten küçük veyaMaximum() uint32: Bitmap'teki en büyük öğeyi döndürürMinimum() uint32: Bitmap'teki en küçük öğeyi döndürür
Bu işlevler de mevcuttur. Ayrıntılı bilgi için resmi dokümantasyona bakabilirsiniz.
Ayrıca roaring paketi, uint32'nin yanı sıra uint64'ü destekleyen bir paket de içermektedir. Hemen hemen aynı arayüzü sunduğu için doğrudan değiştirilerek kullanılabilir.
1package main
2
3import (
4 "log"
5
6 "github.com/RoaringBitmap/roaring/v2/roaring64"
7)
8
9func main() {
10 b1 := roaring64.BitmapOf(1, 2, 3, 4) // 1, 2, 3, 4 ile başlat
11 b2 := roaring64.BitmapOf(2, 4, 6, 8, 12) // 2, 4, 6, 8, 12 ile başlat
12
13 b1.Add(5) // 5 ekle
14 b2.Add(10) // 10 ekle
15
16 b2.Remove(12) // 12 kaldır
17
18 and := roaring64.FastAnd(b1, b2)
19 or := roaring64.FastOr(b1, b2)
20
21 // bitmap işlemleri orijinali değiştirdiği için kopya oluşturup kullan
22 xor := b1.Clone()
23 xor.Xor(b2)
24
25 log.Printf("and result: %v", and.ToArray())
26 log.Printf("or result: %v", or.ToArray())
27 log.Printf("xor result: %v", xor.ToArray())
28}
Sonuç
Roaring Bitmap, yüksek kardinaliteli tam sayı kümelerini verimli biçimde saklamak ve işlemek için güçlü bir araçtır. github.com/RoaringBitmap/roaring paketini Go dilinde kullandığınızda, Roaring Bitmap'in tüm işlevlerinden kolayca yararlanabilirsiniz. Bu paket, çeşitli konteyner türleri ve optimize edilmiş işlemler sayesinde bellek kullanımını ve performansı en üst düzeye çıkarır. Veritabanı indeksleme, günlük analizi, istatistiksel hesaplama gibi birçok alanda Roaring Bitmap'ten yararlanarak verimli veri işleme gerçekleştirebilirsiniz.
Roaring Bitmap özellikle büyük ölçekli veri kümelerinde yüksek performans sergiler ve çeşitli uygulamalarda faydalı şekilde kullanılabilir. Go dilinin sade ve verimli sözdizimiyle birleştiğinde, Roaring Bitmap geliştiricilere güçlü bir araç sunar. Roaring Bitmap'in gelişimiyle birlikte daha fazla işlev ve optimizasyonun ortaya çıkması beklenmektedir.
Bunu neden yaptım derseniz? Zaman serisi veritabanlarında her etiket için indeksleme ve hızlı birleşim işlemleri yapabilmek için.