您的位置:首页 > 数据库 > Redis

BitMap算法应用:Redis队列滤重优化

2016-05-01 17:50 603 查看
工作中有用到Redis滤重队列。

原来的方法如下:

方法一

为了保证操作原子性,使用Redis执行Lua脚本。

在脚本中的逻辑是,如果队列不超过某个数值,进行一次lrem操作(队列使用list结构),然后将新元素入列。

优点:

简单,直观。

缺陷:

lrem的时间复杂度为O(N),N为队列中的元素个数;所以,性能一般。

因为防止队列内容过多,防止发生N级别的删除操作,限制了一个滤重的阀值,如果超过这个阀值就不能使用滤重功能。

方法二

为了解决以上痛点,新玩法为:

为了保证操作原子性,使用Redis执行Lua脚本。

同样使用Lua脚本,排重分为两步,使用了Redis自带的二进制数组进行维护是否存在重复的状态:

在入队之前,先从二进制数组中查询下这个key是否存在,即
getbit key offset
。如果存在说明队列中存在一个这个offset的值,就不需要进行入队操作,直接中断执行就好。

在出队的时候,将出队的元素在二进制数组中设置为不存在,即,setbit key offset 0。

优点:

因为是bitmap算法,在查询是否存在执行的offset的时候,时间复杂度是O(1),并且与队列中元素个数无关。

优雅,如果算是优点的话,哈哈。

缺点:

最重要的一点是redis bitmap的offset必须是
int
,比如,long范围的offset是不存在的,这是一个很
重要
的点,一定要注意(都是血泪史)。

因为入队和出队都进行了bitmap的数据维护,所以需要确保在编码的时候一定谨慎,足够健壮。

总结

从上面的分析来看,感觉方法二完胜方法一。其实不尽然,只能说各有不同的场景。

方法一比较通用,不论入队的内容是什么,都可能滤重,方法二依赖与Bitmap算法,意味key只能是数值型的元素。

在实际应用中,以上两种滤重方式一般是可以联合使用的。如果key是数值类型,没有超出int的取值范围,那么就直接使用方法二,如果超出了int的取值范围的数值就使用方法一。

扩展

还有一种滤重的算法叫:布隆过滤器,感兴趣的同学可以了解下:Bloom filter。如果不需要删除,不在乎误判率的话那应该是很合适的一个算法,空间和时间都很高效。

另外如果有人遇到过其他的一些坑或者有更好的建议,欢迎指点。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: