1.Bitmaps
Bitmaps在Redis本质上是字符串,但它可以对字符串的位进行操作。我们可以把Bitmaps看出成以
位为单位的数组,为了区别字符串的操作,Redis给Bitmaps提供了一套单独的命令对其进行操
作。
1.1 常用命令
1.1.1 设置值
setbit key offset value setbit
命令接收两个参数, 第一个参数表示你要操作的是第几个bit位,第二个参数表示你要将这个位设
为何值,可选值只有0,1两个。 如果所操作的bit位超过了当前字串的长度,reids会自动增大字串长
度。
1.1.2 获取值
getbit key offset getbit
只是返回特定bit位的值。如果试图获取的bit位在当前字串长度范围外,该命令返回0。
1.1.3 获取Bitmaps指定范围值为1的个数
bitcount key [start] [end]
如果start和end顺序有错误结果则不对
1.2 Bitmaps间运算
BITOP AND destkey key [key ...]
对一个或多个 key 求逻辑并,并将结果保存到 destkey 。
BITOP OR destkey key [key ...]
对一个或多个 key 求逻辑或,并将结果保存到 destkey 。
BITOP XOR destkey key [key ...]
对一个或多个 key 求逻辑异或,并将结果保存到 destkey 。
BITOP NOT destkey key
对给定 key 求逻辑非,并将结果保存到 destkey 。
除了 NOT 操作之外,其他操作都可以接受一个或多个 key 作为输入。
1.3 Bitmaps的优势
假设要对一个1亿日活的网站用户id进行记录,从下图我们可知,Bitmaps的优势在于在存储海量数
字的情况下,它可以以极小的容量进行存储,节省大量的内存空间。
数据类型 | 每个用户id占用空间 | 需要存储用户量 | 1天占用空间 | 1个月占用空间 | 1年占用空间 |
set | 64位 | 10000w | 800MB | 24G | 288G |
Bitmaps | 1位 | 10000w | 12.5MB | 375.5MB | 4.5G |
2.布隆过滤器
布隆过滤器( Bloom Filter) 是在 1970 年由布隆( Burton Howard Bloom) 提出,是一种紧凑型
的、比较巧妙的概率型数据结构,它由一个很长的二进制向量( 位向量)和一系列随机均匀分
布的散列( 哈希)函数组成 此种方式不仅可以提升查询效率,也可以节省大量的内存空间。
当一个元素被加入元素集合时,通过 k 个散列函数将这个元素映射成一个位数组中的 k 个点(
特征) ,把它们置为 1,检索时这些点如果是 1,则被检元素可能存在,如果这些点当中有任
何一个 0,则被检元素一定不在。
准备阶段
1.创建向量位图,并设置其值为0
2.准备hash函数数组
3.准备已有元素集合 构建阶段
4.每个元素循环执行hash函数,并且把结果写入向量位图上
查找阶段
5.接收判断元素循环执行hash函数,获得对应值
确认阶段
6.判断接收元素所算出的结果,如果对应位图全为1,那么这个元素有可能存在于集合中,如果结果存在1个或以上为0,那么这个元素比不存在于数组中
3.基于Redis Bitmaps实现的简单布隆过滤器
1.通过http://en.wikipedia.org/wiki/Bloom_filter#Probability_of_false_positives阅读我们可以进行布隆过滤器的使用,我们需要获取下面的参数:
k是函数个数
m是位图位数
n是插入元素个数
ε是误报率
其中,元素个数和误报率将由使用者提供,通过下面的代码可知
private RedisBloomFilter(int forecastNumber,float bearError,RedisTemplate redisTemplate)
{
this.redisTemplate=redisTemplate;
this.forecastNumber=forecastNumber;
this.bearError=bearError;
this.bitslength = (long) (-forecastNumber * Math.log(bearError) / (Math.log(2) * Math.log(2)));
this.hashFunctionsNum=(int) Math.round((double) this.bitslength / this.forecastNumber * Math.log(2));
this.setScript = new DefaultRedisScript<>(setString, Long.class);
this.getScript = new DefaultRedisScript<>(getString, Long.class);
}
由 方程式我们可计算出位图位数,转换成代码
(long) (-forecastNumber * Math.log(bearError) / (Math.log(2) * Math.log(2)))
又由方程式我们计算出函数个数,转换成代码
Math.max(1, (int) Math.round((double) this.bitslength / this.forecastNumber * Math.log(2)))
2.在实际应用中Hash算法并不是无限的,因此由Adam Kirsch与Michael Mitzenmacher做出了研究,并证明了其效果:使用两个Hash函数的计算依然能保证较低的假阳性概率(两个不同的数据得到相同的结果)。
由原来的
g0(x) = hash1(object) 第0个hash函数求出的hash值
g1(x) = hash2(object) 第1个hash函数求出的hash值
g2(x) = hash3(object) 第2个hash函数求出的hash值
gk(x) = hashn(object) 第k-1个hash函数求出的hash值
转变为
//计算哈希结果
h1=hash1(object) h2=hash2(object)
g0(x) = h1
g1(x) = h1+h2+1
g2(x) = h1+2*h2+4
gk-1(x) = h1+(k-1)*h2+(k-1)^2
与guava不同我在这里还原了论文中的方程式,我使用了zero-allocation-hashing,他提供了多种高
效的哈希算法,这里我选用了guava使用的murmur_3实现和google自家的cityhash实现,由于
cityhash只能对string进行hash,因此该功能暂时只限制做string的处理。
Long hash1=LongHashFunction.murmur_3().hashChars(str);
Long hash2=LongHashFunction.city_1_1().hashChars(str);
for(int i=0;i<hashFunctionsNum;i++)
{
long bloomresult=hash1+i*hash2+i*i;
//确保结果在申请的位置中
bloomresultlist[i]=(bloomresult & Long.MAX_VALUE) % bitslength;
}
3.数据获取部分利用redis lua在执行存储操作的原子性,编写lua对bitmap进行操作
//setbit lua 语句
private final String setString="local actorlist = ARGV "+
"for i,v in pairs(actorlist) do" +
" redis.call('setbit',KEYS[1],tonumber(v,10),1) "+
"end";
//getbit lua 语句
private final String getString="local actorlist = ARGV "+
"for i,v in pairs(actorlist) do" +
" if(redis.call('getbit',KEYS[1],tonumber(v,10))==0) " +
" then" +
" return 0" +
" end "+
"end"+
" return 1";
private Boolean setbits(String[] bloomresultlist,String redisKey)
{
redisTemplate.execute(setScript, new StringRedisSerializer(), new StringRedisSerializer(), Arrays.asList(redisKey),bloomresultlist);
return true;
}
private Boolean getbits(String[] bloomresultlist,String redisKey)
{
if((Long)redisTemplate.execute(getScript, new StringRedisSerializer(), new StringRedisSerializer(), Arrays.asList(redisKey), bloomresultlist)!=1)
return false;
else
return true;
}
参考:
http://redisdoc.com/bitmap/bitop.html
http://en.wikipedia.org/wiki/Bloom_filter#Probability_of_false_positives
书籍:
论文:
Less Hashing, Same Performance: Building a Better Bloom Filter
github:https://github.com/tale2009/Blog-Demonstration/tree/main/RedisBloomFilter