zoukankan      html  css  js  c++  java
  • BitMap算法应用:Redis队列滤重优化

    工作中有用到Redis滤重队列。

    原来的方法如下:

    方法一

    • 为了保证操作原子性,使用Redis执行Lua脚本。
    • 在脚本中的逻辑是,如果队列不超过某个数值,进行一次lrem操作(队列使用list结构),然后将新元素入列。

    优点:
    简单,直观。

    缺陷:

    1. lrem的时间复杂度为O(N),N为队列中的元素个数;所以,性能一般。
    2. 因为防止队列内容过多,防止发生N级别的删除操作,限制了一个滤重的阀值,如果超过这个阀值就不能使用滤重功能。

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

    • 为了保证操作原子性,使用Redis执行Lua脚本。
    • 同样使用Lua脚本,排重分为两步,使用了Redis自带的二进制数组进行维护是否存在重复的状态:
      1. 在入队之前,先从二进制数组中查询下这个key是否存在,即getbit key offset。如果存在说明队列中存在一个这个offset的值,就不需要进行入队操作,直接中断执行就好。
      2. 在出队的时候,将出队的元素在二进制数组中设置为不存在,即,setbit key offset 0。

    优点:

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

    缺点:

    1. 最重要的一点是redis bitmap的offset必须是int,比如,long范围的offset是不存在的,这是一个很重要的点,一定要注意(都是血泪史)。
    2. 因为入队和出队都进行了bitmap的数据维护,所以需要确保在编码的时候一定谨慎,足够健壮。

    总结
    从上面的分析来看,感觉方法二完胜方法一。其实不尽然,只能说各有不同的场景。
    方法一比较通用,不论入队的内容是什么,都可能滤重,方法二依赖与Bitmap算法,意味key只能是数值型的元素。
    在实际应用中,以上两种滤重方式一般是可以联合使用的。如果key是数值类型,没有超出int的取值范围,那么就直接使用方法二,如果超出了int的取值范围的数值就使用方法一。

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


    另外如果有人遇到过其他的一些坑或者有更好的建议,欢迎指点。

  • 相关阅读:
    Java8 Stream Function
    PLINQ (C#/.Net 4.5.1) vs Stream (JDK/Java 8) Performance
    罗素 尊重 《事实》
    小品 《研发的一天》
    Java8 λ表达式 stream group by max then Option then PlainObject
    这人好像一条狗啊。什么是共识?
    TOGAF TheOpenGroup引领开发厂商中立的开放技术标准和认证
    OpenMP vs. MPI
    BPMN2 online draw tools 在线作图工具
    DecisionCamp 2019, Decision Manager, AI, and the Future
  • 原文地址:https://www.cnblogs.com/liushijie/p/5450859.html
Copyright © 2011-2022 走看看