Cardinality Estimation

文章目录

  • 前言
  • UV统计的难点
    • 时空复杂度
    • UV合并
  • 相关算法
    • HashSet
    • Bitmap
          • Bitmap和HashSet对比
    • LPC
          • Bitmap和Linear对比
    • PC
          • 估算
    • LogLog
    • HyperLogLog
    • 总结

前言

最近做UV近似统计的需求,整理了UV统计相关的算法,Cardinality Estimation的相关研究可以用于UV近似统计。下面所列出算法重在实现逻辑,相关证明过程可以参考对应论文。我们的UV统计采用了HyperLogLog算法,因此会对HyperLogLog算法做详细介绍。

首先,Linux的sort命令和SQL语言中的distinct关键字可以实现UV统计,如下

sort -u log.txt | wc -l
SELECT
DATE_TRUNC(day,event_time),
COUNT(DISTINCT user_id),
COUNT(DISTINCT url)
FROM weblog

UV统计的难点

时空复杂度

部分UV统计,数据量大,对实时性要求高,这就要求统计程序要尽可能的降低时空复杂度。

UV合并

和PV不同,UV无法直接进行时间或其他维度上的合并,例如,有前一分钟和当前时刻的UV,要计算这两分钟的UV,不能直接将结果合并。

相关算法

HashSet

HashSet有去重功能,所以使用HashSet即可实现UV统计,Java的代码如下

	public static long count(Iterable<String> stream) {
		 HashSet<String> hset = new HashSet<>();
		 for (String x : stream)
			 hset.add(x);
		 return hset.size();
	}

HashSet使用对象存储,内存开销大,仅适用于小数据量计算。

Bitmap

统计UV时,使用位图存储用户信息,而不是HashSet的对象,极大节省内存空间,Java中的位图实现是java.util.BitSet,如下代码,创建长度为1024的位图,第十位设置为true,返回uv

	BitSet bset = new BitSet(1024);
	bset.set(10);
	int uv = bset.cardinality();
2^32≈42亿,2^32bit≈537M

从上图可以看出,只需约537M的空间就可统计42亿用户的UV,但是我们如果要多维度统计UV,就需要多个位图结构,所需空间是n * 537M,维度较多时内存开销还是很大。

Bitmap和HashSet对比

Bitmap在处理非数值类型时,需要使用hash函数将其转换为数值,此过程可能产生hash冲突,使得结果出现误差,hash函数选择较为重要。

此外,极端情况下,若Bitmap处理的数据仅有少量的大树时,仍然要申请足够长度的bit,造成浪费。

LPC

那么有没有比位图省空间,并且还是准确计算UV的算法吗,目前是没有的,接下来介绍的都是估计算法,用精度换空间。
Linear Probabilistic Counter是基于Bitmap的变形。
Cardinality Estimation_第1张图片
该算法分为两部分

  • 申请长度为m的bit map,每一位初始化为0(上图before scan),对每个输入(上图column C),使用hash函数处理,然后将其对应的bit位置为1(结果如after scan)。
  • 计算,bit map中0的个数为 U n U{_n} Un,长度为m,则估算结果n如下计算
    V n = U n / m V{_n} = U{_n} / m Vn=Un/m
    n = − m l n V n n = -m ln V{_n} n=mlnVn
Bitmap和Linear对比

和前面介绍的Bitmap相比,Linear算法的位图长度可以小于已知的基数(例如UV统计,已知最大Uid,Bitmap的位长度要cover住Uid,而Linear则不需要),起到了减小了内存开销的作用。

位图长度可以小于已知的基数,就会有冲突的情况,hash函数的选择变得重要,合适的hash函数能够有效降低hash冲突,同时,m的大小也决定着hash冲突的频率,显然m越大hash冲突的概率就越小,计算结果也更精确。

PC

Cardinality Estimation_第2张图片
probabilistic counting algorithms for database applications算法流程如上

  • 初始化长度为L - 1的bit map,每一位为0
  • 使用hash函数处理输入x得到y,index等于y的二进制表示中1出现的最低位
  • 将BITMAP[index]设置为1
估算

在这里插入图片描述
R表示在BITMAP中最左位0的位置,根据上面公式求出n。

LogLog

见HyperLogLog

HyperLogLog

见HyperLogLog

总结

介绍了几种用于计算UV的算法,PCLPC算法只讨论了实现过程,数学证明和偏差计算参考给出的论文连接,LogLogHyperLogLog算法下一篇详细介绍。

参考
1.Cardinality Estimation (princeton)
2.A Linear-Time Probabilistic Counting Algorithm for Database Applications
3.loglog counting of large cardinalities
4.Big Data Counting: How To Count A Billion Distinct Objects Using Only 1.5Kb Of Memory
5.probabilistic counting algorithms for database applications

你可能感兴趣的:(uv)