四、Redis新类型之 hyperloglog

一、基础知识

1、常见名词

  • UV:Unique Visitor,独立访客,一般理解为客户端IP。需要去重。
  • PV:Page View,页面浏览量。不用去重。
  • DAU:Daily Active User,日活跃用户量。常用于反映网站、互联网应用或者网络游戏的运营情况。
  • MAU:MonthIy Active User,月活跃用户量。

2、HyperLogLog概念

去重复统计功能的基数估计算法-就是HyperLogLog,它是一种概率算法,结果是有误差的,牺牲准确率来换取空间

HyperLogLog的标准错误为1.04 / sqrt (m),其中“m”是所使用的寄存器数。Redis使用16384个寄存器,因此标准误差为0.81%。

3、HyperLogLog占用大小

在Redis里面,每个HyperLogLog键只需要花费 12KB 内存,就可以计算接近2^64个不同元素的基数。

每个桶取6位,16384*6÷8 = 12kb,每个桶有6位,最大全部都是1,值就是63

4、HyperLogLog原理

只是进行不重复的基数统计,不是集合也不保存数据,只记录数量而不是具体内容。其底层是string类型。

5、什么是基数

是一种数据集,去重复后的真实个数。

基数统计:用于统计一个集合中不重复的元素个数,就是对集合去重复后剩余元素的计算。

6、优缺点

优点

  • 在输入元素的数量或者体积非常非常大时,计算基数所需的空间总是固定的、并且是很小的。

缺点

  • 统计结果不精确,有误差。
  • 因为 HyperLogLog 只会根据输入元素来计算基数,而不会储存输入元素本身,所以 HyperLogLog 不能像集合那样,返回输入的各个元素

7、使用场景

  • 统计某个网站的UV、统计某个文章的UV。
  • 用户搜索网站关键词的数量。
  • 统计用户每天搜索不同词条个数。

 

二、HyPerLogLog如何做的?如何演化出来的?

由去重统计的方式展开

1、Java HashSet

不适合大数据统计,占空间大,hash冲突。

2、Redis set

不适合大数据统计,占用空间。亿级流量下,如果用set存储访问用户的ip,1.5亿访问量*15字节(ipv4地址)=2GB,一个月=60GB。

3、Redis bitmap

不适合大数据统计,样本元素越多内存消耗急剧增大,难以管控+各种慢。

bitmap是通过用位bit数组来表示各元素是否出现,每个元素对应一位,所需的总内存为N个bit。基数计数则将每一个元素对应到bit数组中的其中一位,比如bit数组010010101(按照从零开始下标,有的就是1、4、6、8)。新进入的元素只需要将已经有的bit数组和新加入的元素进行按位或计算就行。这个方式能大大减少内存占用且位操作迅速。

如果一个样本就是1亿个数据的基数位值,大约需要内存100000000/8/1024/1024约等于12M,这样得到统计一个对象样本的基数值需要12M。

但是,如果统计10000个对象样本,就需要117.1875G将近120G,可见使用bitmaps还是不适用大数据量下(亿级)的基数计数场景,但是bitmaps方法是精确计算的。

4、HyperLogLog的诞生

通过牺牲准确率来换取空间,对于不要求绝对准确率的场景下可以使用,因为概率算法不直接存储数据本身,通过一定的概率统计方法预估基数值,同时保证误差在一定范围内,由于又不储存数据故此可以大大节约内存

 

三、为什么Redis集群的最大槽数是16384个?

Redis集群并没有使用一致性hash而是引入了哈希槽的概念。Redis 集群有16384个哈希槽,每个key通过CRC16校验后对16384取模来决定放置哪个槽,集群的每个节点负责一部分hash槽。但为什么哈希槽的数量是16384(2^14)个呢?

CRC16算法产生的hash值有16bit,该算法可以产生2^16=65536个值。换句话说值是分布在0~65535之间。那作者在做mod运算的时候,为什么不mod65536,而选择mod16384

(1)正常的心跳数据包带有节点的完整配置,可以用幂等方式用旧的节点替换旧节点,以便更新旧的配置。这意味着它们包含原始节点的插槽配置,该节点使用2k的空间和16k的插槽,但是会使用8k的空间(使用65k的插槽)。同时,由于其他设计折衷,Redis集群不太可能扩展到1000个以上的主节点。因此16k处于正确的范围内,以确保每个主机具有足够的插槽,最多可容纳1000个矩阵,但数量足够少,可以轻松地将插槽配置作为原始位图传播。请注意,在小型群集中,位图将难以压缩,因为当N较小时,位图将设置的slot / N位占设置位的很大百分比。

(2)如果槽位为65536,发送心跳信息的消息头达8k,发送的心跳包过于庞大。在消息头中最占空间的是myslots[CLUSTER_SLOTS/8]。 当槽位为65536时,这块的大小是: 65536÷8÷1024=8kb 因为每秒钟,redis节点需要发送一定数量的ping消息作为心跳包,如果槽位为65536,这个ping消息的消息头太大了,浪费带宽。16348只有2kb。

(3)redis的集群主节点数量基本不可能超过1000个。集群节点越多,心跳包的消息体内携带的数据越多。如果节点过1000个,也会导致网络拥堵。因此redis作者不建议redis cluster节点数量超过1000个。 那么,对于节点数在1000以内的redis cluster集群,16384个槽位够用了。没有必要拓展到65536个。

(4)槽位越小,节点少的情况下,压缩比高,容易传输Redis主节点的配置信息中它所负责的哈希槽是通过一张bitmap的形式来保存的,在传输过程中会对bitmap进行压缩,但是如果bitmap的填充率slots / N很高的话(N表示节点数),bitmap的压缩率就很低。 如果节点数很少,而哈希槽数量很多的话,bitmap的压缩率就很低。

 

 

四、常见命令 

 五、网页UV统计Demo

Controller

@RestController
@Slf4j
public class HyperLogLogController {
    @Resource
    private RedisTemplate redisTemplate;
    @Resource
    private HyperLogLogService hyperLogLogService;

    public static final String MYKEY = "myhyper";

   // 获取IP去重后的网页访问量
    @RequestMapping(value = "/hyperloglog/uv", method = RequestMethod.GET)
    public long uv() {
        return redisTemplate.opsForHyperLogLog().size(MYKEY);
    }

    // 初始化数据
    @RequestMapping(value = "/hyperloglog/init", method = RequestMethod.POST)
    public void init() {
        hyperLogLogService.init();
    }
}

Service

@Service
@Slf4j
public class HyperLogLogService {
    @Resource
    private RedisTemplate redisTemplate;

    public static final String MYKEY = "myhyper";

    /**
     * 模拟后台有用户点击网页,每个用户来自不同ip地址
     */
    public void init() {
        log.info("------模拟后台有用户点击首页,每个用户来自不同ip地址");
        //  测试模拟,实际不用这种写法
        new Thread(() -> {
            String ip = null;
            for (int i = 1; i <= 200; i++) {
                Random r = new Random();
                ip = "192.168.1." + r.nextInt(256);

                Long hll = redisTemplate.opsForHyperLogLog().add(MYKEY, ip);
                log.info("ip={},该ip地址访问首页的次数={}", ip, hll);
                //暂停2秒钟线程
                try {
                    TimeUnit.SECONDS.sleep(2);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }, "t1").start();
    }
}

 

posted @ 2022-01-19 00:02  幻月hah  阅读(405)  评论(0编辑  收藏  举报