龙空技术网

Redis使用管道(pipeline)优化写入性能,统计系统访问UV

流浪剑客26 667

前言:

此刻小伙伴们对“phpuv统计”大概比较着重,各位老铁们都想要剖析一些“phpuv统计”的相关资讯。那么小编同时在网摘上汇集了一些关于“phpuv统计””的相关内容,希望看官们能喜欢,我们快快来学习一下吧!

对于一个已经上线的系统,常常需要统计一些数据用于支撑运营团队的决策。

对于于不同的数据量,作为开发人员,我们应该如何选择合适的数据结构去完成需求。

开发人员考虑的层面,一是数据量,就是存储这些数据需要暂用内存块的大小;二是这种数据结构的插入、删除、修改、统计的时间复杂度的问题。

统计用户UV,从业务上我们常常会想到使用集合(Set),集合存储的数据不能重复,所以将用户IP直接存储到 Set 可以很完美地解决问题。

下面我们通过Set、HyperLogLogs 两个例子来体会一下。

数据量100W,存储1-100w的值。

有序列表Set

将100w的数据量写入Set结构。

代码

<?php//连接本地的 Redis 服务$redis = new Redis();$redis->connect('redis', 6379);// 因为写入数据量较大,所以我们采用 pipeline 来优化写入性能 $pipline = $redis->multi(Redis::PIPELINE);for($i = 0; $i < 1000000; $i++){    $pipline->sAdd('num' , $i); }$pipline->exec();

这里同时插入100W条数据,写入压力较大,所以使用 pipeline 作了优化。

命令行执行

php ceshi.php

结果截图

Set结构相关信息

截图里面最重要的信息是 set 占用了 4934341 B的内存空间,也就是 4M 的内存空间。

set在存储大数据量的时候使用的底层数据结构是 字典(dict),插入的时间复杂度为O(1),

统计的时间复杂度为O(1)。

HyperLogLogs

将100w的数据量写入 HyperLogLogs结构。

代码

<?php//连接本地的 Redis 服务$redis = new Redis();$redis->connect('redis', 6379);// 因为写入数据量较大,所以我们采用 pipeline 来优化写入性能 $pipline = $redis->multi(Redis::PIPELINE);for($i = 0; $i < 1000000; $i++){    $redis->rawCommand('pfadd','numpf', $i);}$pipline->exec();

命令行执行

php ceshi.php

结果截图

HyperLogLogs结构相关信息

这里我们程序插入的数据数量为 100w条, 这里却是 1009972 条。内存占用为 10587B,大约10KB。

hyperLogLogs是基于 概率统计 的,统计的结果数量误差在 1% 以内。这个数据结构是为了使用小的内存空间统计大量的元素。

hyperLogLogs使用的底层数据结构为 bit,该结构使用 2^14 个桶,2^6 表示一个桶,统计2^64 的数据,数据量较大时,单个key固定占用的内存为12K。

hyperLogLogs的插入时间复杂度为O(1),统计的 平均复杂度 为O(1)。

Set\HyperLogLogs对比两者在插入时的性能是一样的。统计时Set的性能会比HyperLogLogs高。因为 dict 结构体有一个字段 used 可以直接返回。HyperLogLogs 统计时会根据缓存是否过期(每次新增数据时会设置过期)来判断是否重新计算数量,如果过期重新计算,如果没有过期则直接返回 card 结构体字段。内存占用方面,同样100w数据,Set 占用 4M 空间, HyperLogLogs 占用 10K空间。dict 结构在数据逐渐增大的时候,会 rehash 扩容,对性能的损耗是比较大的。

综合对比之下,在统计系统UV的场景中,HyperLogLogs 结构更加合适。

标签: #phpuv统计