redis中的bitmap实例分析

1、BitMap是什么

使用一个位来表示元素的值或状态,该元素本身即为key。Bitmap可以极大地节省存储空间,因为我们知道8个bit可以组成一个Byte。2^32次方40亿数据只需要500M内存,需要内存少了8倍

2、setbit命令介绍 setbit key offset value
#设置bitmapkey为20220328 uid为100的用户已签到1
setbit 20220320 100 1
setbit 20220320 200 1
setbit 20220321 100 1
setbit 20220321 300 1
getbit 20220320 100 #返回1,说明这个用户已签到了
bitcount 20220320 #获取bitmap数量

bitmap的坑

127.0.0.1:6400>
setbit bittest 100 1 #设置不存在的offset返回0
(integer) 0
127.0.0.1:6400>
setbit bittest 100 1 #设置已存在的offset返回1
(integer) 1

使用Redis中的Bitmap实现高效数据操作

setbit maxKey 4000000000 1 #直接弄了你600多M内存

/**
* 布隆过滤器bloom Filter
* 1.百万分之一的概率哈希冲突,所以有存在的不一定存在,但是不存在的百分百不存在
* 2.不能删除,删除的时候不能简单的直接置为0,可能会影响其他元素的判断,其实问题不大一般生产数据也不会删除的,都是软删除
* 3.新增数据时候写入bloom Filter
* 4.2^32次方40亿数据内存占用才600M,超级省内存,查找速度非常快,160M内存可以在千万级数据做到1%的误判
* 5.bitmap根据offset去申请内存的,所以要省内存的情况要限制offset值
*/
public function bloomAction(){
$t1 = time();

for($i=0;
$i<
99;
$i++){
$bl = new BloomFilter();

//$str = "
1https://arnaud.le-blanc.net/php-rdkafka-doc/phpdoc/book.rdkafka.html?id="
.time();

$str = "
https://dasda.le-blanc.net/php-rdkafka-doc/phpdoc/book.rdkafka.html?id="
.mt_rand(1,99999999);

p($str);

$res1 = $bl->
JSHash($str);
//两次哈希3s,md5哈希重复的概率是百万分之一
p($res1);

}
//p($res);

$t2 = time();

echo $t2-$t1;

}
/**
* 布隆过滤器初始化 bloom Filter 执行 php index.php "
index/demo/loadDb2bloom"

*/
public function isExistBloomAction(){
$redis = redisCursor();

$email = input("
email"
,"
"
,"
trim"
);

$tel = input("
tel"
,"
"
);

$result = false;

$msg = "
"
;

if(filter_var($email,FILTER_VALIDATE_EMAIL)){
$key1 = "
bloom_user_email"
;

$offset = BloomFilter::JSHash($email);

$result = $redis->
getbit($key1,$offset);

$msg = $email;

}elseif($tel){
$key2 = "
bloom_user_telephone"
;

$offset = BloomFilter::JSHash($tel);

$result = $redis->
getbit($key2,$offset);

$msg = $tel;

}
$result?apiSuccess($msg."
,已存在"
):apiError($msg."
,不存在"
);

}
/**
* 布隆过滤器初始化 bloom Filter 执行 php index.php "
index/demo/loadDb2bloom"

*/
public function loadDb2bloomAction(){
$time1 = time();

$redis = redisCursor();

$key1 = "
bloom_user_email"
;

$key2 = "
bloom_user_telephone"
;

//setbit() offset 必须是数字,value必须是1或0
//$redis->
setbit($key,30,1);

$table = "
user"
;

$pkid = "
id"
;

$field1 = "
email"
;

$field2 = "
telephone"
;

$maxid = Db::name($table)->
max($pkid);

$size = 5000;

$page = ceil($maxid/$size);

for($i=0;
$i<
$page;
$i++){
$start = $i*$size;

$where = "
$pkid between "
.$start."
and "
.($start+$size);

$res = Db::name($table)->
where($where)->
field("
$field1,$field2"
)->
select();

if($res){//同步到bitmap
foreach($res as $k=>
$v){
//布隆过滤器 1.存在的不一定存在, 2.不存在的100%不存在(原因,哈希冲突可能用100W分之一的可能重复)
//所以注册的时候判断不存在的,百分百可以注册,存在的可以查询一下数据库是否真的不存在
$value1 = BloomFilter::JSHash($v["
$field1"
]);

$value2 = BloomFilter::JSHash($v["
$field2"
]);

$redis->
setbit($key1,$value1,1);
//email去重
$redis->
setbit($key2,$value2,1);
//mobile去重
}
}
$time2 = time();

echo $where."
消耗时间 "
.($time2-$time1).PHP_EOL;

}
$time3 = time();

echo "
总消耗时间 "
.($time3-$time1).PHP_EOL;

} <
?php
class BloomFilter
{
/**
* 下面的哈希函数随便用一个都行,都是把字符串转换成数字
*/
/**
* hash方法类
* 由Justin Sobel编写的按位散列函数
* update:Denny
* 返回之前做了内存限制在160M,超过10亿的哈希后的数值,把它限制在10亿内,此时1000W的数据可做到1%误判,内存不差这600多M的话就别限制了
* 因为redis的bitmap申请内存是看offset申请内存的,setbit mykey 400000000 1,这样直接申请了600M内存
*/
public static function JSHash($string, $limitMemory=true,$len = null)
{
$hash = 1315423911;

$len || $len = strlen($string);

for($i = 0;
$i <
$len;
$i++)
{
$hash ^= (($hash <
<
5) + ord($string[$i]) + ($hash >
>
2));

}
$hashNum = ($hash % 0xFFFFFFFF) &
0xFFFFFFFF;

//为了节省内存,超过10亿就对半拆,10亿,这时候大约是130M内存占用,千万级数据可以做到1%误判率,内存足够可以不用判断,直接生成就行了
//如果数据过4000W的话不用限制了,因为生成的数据最大也是2^32次方40多亿,此时内存占用大概在600M封顶了
if($limitMemory){
if($hashNum>
4000000000){
$hashNum = intval($hashNum/5);

}elseif($hashNum>
3000000000){
$hashNum = intval($hashNum/4);

}elseif($hashNum>
2000000000){
$hashNum = intval($hashNum/3);

}
}
return $hashNum;

}
}

通过使用布尔类型的位图来存储大数据集合,Redis的Bitmap实现可以提供快速的位操作。这种数据类型不仅提供了基于位的高级操作,而且非常高效。在这篇文章中,我们将介绍如何在Redis中使用Bitmap来实现高效数据操作。
1. 什么是Bitmap?
- 位图是一种可以用位来表示元素状态的数据结构
- 通常用于大型数据集合的高效存储与处理
- Redis的Bitmap可以在数据处理过程中,做到很高的效率
2. 如何在Redis中使用Bitmap
- 首先需要创建一个Bitmap对象,并指定其位数
- 能够对Bitmap的位数进行读、设置、清除三种操作
- 例如:SETBIT command类可以设置或者清空一定位置上的位,BITCOUNT类可以计算当前位图中,被设置为1的位数
3. Redis中Bitmap的常用场景
- 用户在线状态的管理:如果用户在线,则设置位图中对应的位置为1,反之,为0
- 统计:使用BITCOUNT命令计算置位位数,就可以得出统计值了
- Bloom Filter:布隆过滤器通过位图记录元素存在的可能性,经常与Redis数据存储结合使用
4. 基于Bitmap实现的在线状态管理
- 如需跟踪用户的在线状态,则在Redis中生成一个位图,位内容位在线用户
- 当某个用户进入或退出系统时,将其索引对应的位数值设置为1或0
- 可使用SETBIT和GETBIT命令来简化这些操作
5. 如何在Redis中实现Bloom Filters
- Redis专门添加了Bitmap索引功能,可以轻松创建Bloom Filter
- 先使用SETBIT命令在Redis中创建位图,然后使用一个包含散列函数的程序来产生需要设置的多个位的下标
- 出现重复的位设置了多次,正确的未命中率与位图计数器无关
6. Bitmap的优势
- Bitmap仅占用很少的空间,通常是其他数据结构的一小部分
- 一严格映射位到元素,不需要额外的哈希函数
- 极其高效,一般复杂度为log(n)级别
7. 总结
- 在Redis中,Bitmap是一个强大的工具,可以用于在数据集合中执行快速位计算
- BITCOUNT命令是一个很有价值的工具,允许简单的而快速的统计集合位总数。
- 使用Bitmap存储状态信息可以起到很好的实现辅助作用,如在线状态的维护。
- Bitmap还有一种高效的方法就是实现布隆过滤器,可以非常有效的完成去重等任务。