海量數據解決方案Bitmap

版權聲明:本文爲博主原創文章,遵循 CC 4.0 BY-SA 版權協議,轉載請附上原文出處連接和本聲明。
本文連接:gudepeng.github.io/note/2019/1…java

一.Bitmap簡介

1:Bitmap算法又名位圖算法,其原理是,使用下標代替數值或特定的意義,使用這個位爲0或者1表明特性是否存在。
2:Bitmap算法具備效率高,節省空間的特色,適用於對大量數據進行去重,查詢等,由於bit在計算機內只佔一個bit,而int類型佔用32個bit,因此空間節省了32倍。
git

二.使用場景

假若有一批人羣信息,要給這批人羣打上標籤,例如你要給某些人打上會員的標籤,而且要打上30天內購買過商品的人。你可能會在每一個人身上打上標籤,這樣你在獲取一個會員集合的時候須要遍歷每個人判斷他是否包含會員標籤,這樣的計算量太大。
你能夠換一種思路,咱們有10我的,這樣咱們就會定義一個10位的數組,咱們能夠把人羣按照自增類型設定每一個人的id(從0開始),而後每一位對應一我的,用0,1去判斷這我的是否有這個標籤。
會員標籤 |0|1|1|1|0|0|0|1|0|0|
這樣咱們就能夠很是直觀的查詢到,咱們id爲1,2,3,7的人事會員。接下來咱們繼續打一個30天內購買過商品的人
30天內購買過商品的人 |1|1|0|0|0|0|0|0|0|0|
能夠直觀的看褚30天內購買過商品的人id爲0,1。那麼咱們想要找出30天內購買過商品的會員,咱們只須要作與運算(&)。
會員標籤 |0|1|1|1|0|0|0|1|0|0|
30天內購買過商品的人 |1|1|0|0|0|0|0|0|0|0|
30天內購買過商品的會員 |0|1|0|0|0|0|0|0|0|0|
那麼咱們想要找出30天內購買過商品或者是會員,咱們只須要作或運算(|)。
會員標籤 |0|1|1|1|0|0|0|1|0|0|
30天內購買過商品的人 |1|1|0|0|0|0|0|0|0|0|
30天內購買過商品的會員 |1|1|1|1|0|0|0|1|0|0|
那麼你必定會說,我建立一個map就能夠搞定這個,爲何要使用bitmap呢。
由於在計算機中, 1個int佔4字節即32位,而使用Bitmap的話,只須要1/32的內存。
你可能會說,那麼我有一個10萬的人羣,那麼我有多少個標籤就須要開闢多少個10萬位的bit,可是假如我只有一我的符合這個標籤就會浪費不少資源,接下來我會在具體的使用方法中告訴你們這個問題是怎麼解決的。
github

三.具體實現方法

在這裏咱們主要講解兩種主要的使用方法,第一個是EWAHCompressedBitmap(谷歌對Bitmap的實現),第二個是RoaringBitmap(這個也是大多數主流應用所使用的,例如Spark,Hive,Kylin等)。算法

1.EWAHCompressedBitmap

EWAH是把Bitmap存褚在一個long數組中,每一個元素能夠看做爲一個64位的二進制數,在EWAH中也叫作word,EWAH初始化是4個word,當全部word都被佔用後,就會進行擴容。當添加的數據跨度很大的時候,EWAH會建立一個RLW(Running Length word),RLW被分爲兩部分,低32位標識當前word跨越了多少個空word,高32位標識當前RLW後面有多少個連續的word。這樣就解決了,跨度很大而開闢大量空間的問題。
數組

2.RoaringBitmap

Roaring Bitmap是將32位的整數分割成2的16次方個整數的數據塊,來共享相同的16個最高有效位。使用專門的容器來保存它們的16個最低有效位。當一個數據塊整數不超過4096個時,使用一個16位整數的有序數組(在java中使用short類型數組)。當超過4096個整數時,咱們使用2^16位的位圖(在java中使用long類型數組)。所以咱們有兩種類型的容器,對於稀疏數據塊的數組容器(ArrayContainer)和對於密集數據塊的位圖容器(BitmapContainer)。閾值4096保證容器的級別,每一個整數使用不超過16比特。使用位圖容器時,使用2^16來表示超過4096(=2^12)個整數,少於16比特/整數(2^16 / 2^12 = 2^4 = 16,若是值都充滿long數組,最理想狀況下1比特/整數)。使用數組容器時使用精確的16比特/整數。
爲何選擇4096這個閾值呢?由於小於4096時,位圖容器可能大於16比特/整數,大於4096時,數組容器會超過2^16(2^12 * 16 = 2^16),佔用空間顯然超過2^16這個低16位表示的數的容量。一句話,整數基數較小時,使用數組更省空間,基數較大時,使用bitmap更省空間。
這些容器保存在一個共享16個最高有效位的動態數組中:它們做爲一級索引。使用數組保證高16位有序。咱們認爲一級索引通常很小。當n=1 000 000時,它至多包含16個實體。所以它應該保存在CPU緩存中。容器自己不該該使用超過8KB。
下面是一篇論性能對比論文:
db.ucsd.edu/wp-content/…緩存

四.RoaringBitmap使用方式

1.maven引入

<dependencies>
    <dependency>
        <groupId>org.roaringbitmap</groupId>
        <artifactId>RoaringBitmap</artifactId>
        <version>0.8.12</version>
    </dependency>
</dependencies>複製代碼

2.方法使用

public static void main(String[] args) {
    RoaringBitmap rb = RoaringBitmap.bitmapOf(1,2,3,4,7,33,55);
    //select 返回第幾位的值
    System.out.println(rb.select(1));
    //rank 返回小於等於參數的值得個數
    System.out.println(rb.rank(55));
    //contains 是否包含參數
    System.out.println(rb.contains(56));
    //contains 是否包含參數
    System.out.println(rb.contains(5L,56L));
    //add 添加從左閉到右開區間內的值
    rb.add(10L,15L);
    System.out.println(rb);

    RoaringBitmap rb1 = RoaringBitmap.bitmapOf(2,3,4,44);
    System.out.println(rb1);
    //取兩個bitmap的並集
    RoaringBitmap rb1or2=RoaringBitmap.or(rb,rb1);
    System.out.println(rb1or2);
    //取兩個bitmap的交集
    RoaringBitmap rb1and2=RoaringBitmap.and(rb,rb1);
    System.out.println(rb1and2);
    rb.and(rb1);
    System.out.println(rb);
    //獲取第一位
    System.out.println(rb.first());
}複製代碼
相關文章
相關標籤/搜索