善用Redis BitMap

善用Redis BitMap

legegeCoder 287 2021-10-25

问题

中午生产出现故障,排查下来是调用外部接口超时熔断了。接口主要是传一个设备id判断是否是新用户的。没有做缓存导致高峰期请求超时,从而影响了自己团队的服务。

解决方案

本地布隆过滤器

这是我一开始的解决方案,既然只是一个简单的判断是否是新用户。那么我每次请求过来先判断布隆过滤器里有没有这个设备id。有那么就是老用户,没有那么就请求接口去判断是否是老用户,是老用户那么就放入布隆过滤器中。

public class BloomFilterTest {

     private static BloomFilter<Integer> bloomFilter =  BloomFilter.create(Funnels.integerFunnel(),100,0.01);

    public static void main(String[] args) {

        for (int i = 0; i < 100; i++) {
            if (bloomFilter.mightContain(i)){
                System.out.println(i+"存在.");
            }
	    service.isNew(1L);
	    // 省略
            bloomFilter.put(i);
        }
    }
}

这是一个简单的demo,BloomFilter是guava的一个类。
不过领导说本地缓存设备id太多了是否会导致频繁fullgc。不如还是用Redis吧。

Redis

最简单的就是key里放设备id。value放是否为新设备。不过我个人是不太喜欢这样的。对我个人来说key越简单越好,越少越好,其次不能占用大量的内存。设备id很多,很难想像上千万个设备id的key,只为了存一个true和false,这时候BitMap的好处就来了。具体的介绍可以看这篇文章 Bitmap简介
偏移量 -> 设备id (long类型)
老用户 -> 对应偏移量的值为1。

 // 如果存在就是老用户
        Boolean isOld = stringRedisTemplate.opsForValue().getBit(CommonConstant.OLD_USER_BIT_KEY, cid);
        if (isOld) {
            return false;
        } else {
            boolean isNew = service.isNewDevice(cid);
            if (logger.isDebugEnabled()) {
                logger.debug("checkIsNewDevice cid {} is {}", cid, isNew);
            }
            if (isNew) {
                return true;
            }
            // 不是新用户放入 bitmap
            stringRedisTemplate.opsForValue().setBit(CommonConstant.OLD_USER_BIT_KEY, cid, true);
            return false;
        }


# Redis