背景

最近使用impala做查询的时候,遇到一个sql 中使用多个列 count(distinct) 查询导致报错的情况,报错内容如下:

org.apache.hive.service.cli.HiveSQLException: AnalysisException: all DISTINCT aggregate functions need to have the same set of parameters as count(DISTINCT ); deviating function: count(DISTINCT ) Consider using NDV() instead of COUNT(DISTINCT) if estimated counts are acceptable. Enable the APPX_COUNT_DISTINCT query option to perform this rewrite automatically.

上面的报错,简单来说就是count(distinct column)需要换成NDV(column)。

使用方法

如果设置如下参数:
set APPX_COUNT_DISTINCT=true;
则所有的count(distinct col)会在底层计算的时候转成 ndv() 函数,也就是说,在sql中可以直接使用count(distinct col)

如果不配置上述参数,则在sql 中直接写 ndv(col) 也可以

误差精度

由于ndv采用的是 HyperLogLog (HLL) 算法来对数据进行估算,所以会存在一定的误差。之所以使用 HLL算法,是因为其带来的好处是 极大的速度提升,以及很小的计算内存。

通过官方文档可以知道:
impala4.0.0之后,可以使用scala 参数控制计算精度,使用方法为 ndv(col, scala), 其中scala 为 1-10的整数
其它版本或者不写的情况下改参数默认值为 10.
scala值越大,精度也高,但是所带来的内存消耗越大,占用的内存计算公式是 2^(8+scala) bytes, 当scala = 10 的时候,大约占用内存 0.25 M。

当scala=10 时, 最坏误差为 0.42%,平均误差为 0.17%,占用内存 256KB(测试用一组10亿条整数 的数据集)

官方还给了一些测试案例:
select count(distinct col1) from sample_data;
±--------------------+
| count(distinct col1)|
±--------------------+
| 100000 |
±--------------------+
Fetched 1 row(s) in 20.13s

select cast(ndv(col1) as bigint) as col1 from sample_data;
±---------+
| col1 |
±---------+
| 139017 |
±---------+
Fetched 1 row(s) in 8.91s

误差将近 39% !!! 可见这个函数并不适用于数据量比较小 或者 数据要求完全准确 的情况。

参考官方文档:
https://impala.apache.org/docs/build/html/topics/impala_ndv.html
https://issues.apache.org/jira/browse/IMPALA-10538

替换方案

上一点误差精度中提到,对于 要求数据准确的场景,ndv 并不适合,那怎么办呢? 这里提供一种解决思路:

假设有一张student 表,有 name、age、…等多个字段,没有主键,求 不同年龄段的学生数。
sql如下:

select sum(age_0), sum(age_1), sum(age_2), sum(age_3)
from (SELECT 1 age_0 , max(case when age <= 10 then 1 end) age_1, max(case when age > 10 and age <= 20 then 1 end)age_2, max(case when age > 20 and age <= 30 then 1 end)age_3from student
)

上面的思路就是先通过 group by 求max,再取sum

原理

通过官网文档,ndv使用的是 HyperLogLog (HLL) 来进行计算的,这是一个通过概率来估算唯一值的算法,所以天然会产生误差。那HLL是何方算法呢?

为了更好地了解这个算法,先通过一个故事了解下其中的精髓:

收集电话号码 (Collecting phone numbers)
我和 汤米 一起参加一个会议,当了场地之后,我俩决定打个赌:看谁遇到的新人最多! 然后我们开始 四处闲聊,并统计我们遇到了多少人。

会议结束后,汤米走到我的身边,比了个手势,说 17. 然后我跟他说,我与 47 人聊过天, 显然我赢了 !

但是 汤米 有点沮丧,因为他始终认为我 多次 跟相同的人聊过天,他坚信我总共大约跟 15-17个人聊过天。所以,赌约失效了。

我们决定在,下一次的活动中,记下我们交流过的人的姓名,以确保我们在计算的是唯一身份的人,而不只是对话的总数。在最后碰头的时候,我们都拿着一个很长的名字清单, 汤米比我多遇到了两次! 我们笑了起来,在讨论计算唯一性的方法时,汤米提出了一个好主意:

“我们不能简单地用笔和纸来统计不同的名字统计,这是不切实际的! 今天,我与65位不同的人进行了交谈,而在纸上统计他们的名字实在是很痛苦。 我漏掉了3次,所以不得不从头开始!”

我说: “是的,我知道,但是我们还有其他选择吗?”

汤米: “如果在下一次会议上,我们不问姓名,而是问人们电话号码的后5位如何? 获胜的条件是 手上号码数中 在前面 0 最多的人 ”

我 : “等等汤米,你太快了! 慢一点,给我一个例子……”

汤米: “当然,我们只要问每个人的后5位数字,好吗? 假设他们回答“ 54701”。 没有前导零,因此前导零的最长序列是0。您与之交谈的下一个人说 ‘02561’ 这是前导零! 因此,您最长的序列现在是1。”

我: “现在,假设您告诉我您最长的零序是5-您必须已经与成千上万的人交谈,以找到电话号码为00000的人!”

end.

这就是 HLL 简单的工作原理。

大概计算步骤如下:

1、计算 输入值得 hash,得到比特串(由 1 和 0 组成)
2、通过第一个1出现位置的最大值,来预估总共有多少个不同的数字(整体基数)。

为了更好控制误差,采用了分桶平均的思想 以及 偏差修正 来消减误差。

详细的介绍见:https://en.wikipedia.org/wiki/HyperLogLog
参考:https://blog.csdn.net/cumian8165/article/details/108159272

详解 impala的ndv()函数 原理(HyperLogLog (HLL))、使用方法、精度误差及替换方案相关推荐

  1. 详解非局部均值滤波原理以及用MATLAB源码实现

    详解非局部均值滤波原理以及用MATLAB源码实现 序言 均值滤波.中值滤波.高斯滤波在滤除噪声的过程中,无可避免的使图像的边缘细节和纹理信息所被滤除.针对此问题,Buades[1]等人提出了非局部均值 ...

  2. oracle 总转横函数,详解Oracle行列转换函数-pivot函数和unpivot函数-多智时代

    今天主要介绍一下Oracle行转列及列转行常见函数,下面一起来看看吧! 行列转换 pivot函数:行转列函数 语法:pivot(任一聚合函数 for 需专列的值所在列名 in (需转为列名的值)): ...

  3. 网络知识详解之:HTTPS通信原理剖析(对称、非对称加密、数字签名、数字证书)

    网络知识详解之:HTTPS通信原理剖析(对称.非对称加密.数字签名.数字证书) 计算机网络相关知识体系详解 网络知识详解之:TCP连接原理详解 网络知识详解之:HTTP协议基础 网络知识详解之:HTT ...

  4. Pandas知识点-详解行列级批处理函数apply

    Pandas知识点-详解行列级批处理函数apply 在Pandas中,DataFrame和Series等对象需要执行批量处理操作时,可以借用apply()函数来实现. apply()的核心功能是实现& ...

  5. mysql数据库中count的作用_详解 MySQL中count函数的正确使用方法

    1. 描述 在MySQL中,当我们需要获取某张表中的总行数时,一般会选择使用下面的语句 select count(*) from table; 其实count函数中除了*还可以放其他参数,比如常数.主 ...

  6. python open 打开是什么类型的文件-详解Python中open()函数指定文件打开方式的用法...

    文件打开方式 当我们用open()函数去打开文件的时候,有好几种打开的模式. 'r'->只读 'w'->只写,文件已存在则清空,不存在则创建. 'a'->追加,写到文件末尾 'b'- ...

  7. php打印出函数的内容吗,PHP打印函数集合详解以及PHP打印函数对比详解(精)

    1 echo();2 print();3 die();4 printf();5 sprintf();6 print_r();7 var_dump(); 1 echo() 可以同时输出多个字符串,可以多 ...

  8. python中groupby()函数讲解与示例_详解python中groupby函数通俗易懂

    一.groupby 能做什么? python中groupby函数主要的作用是进行数据的分组以及分组后地组内运算! 对于数据的分组和分组运算主要是指groupby函数的应用,具体函数的规则如下: df[ ...

  9. python函数的使用场景_详解python中strip函数的使用场景

    python strip()函数 介绍,需要的朋友可以参考一下 函数原型 声明:s为字符串,rm为要删除的字符序列 s.strip(rm) 删除s字符串中开头.结尾处,位于 rm删除序列的字符 s.l ...

最新文章

  1. CALayer的基本操作
  2. microsoft mysql下载_Microsoft SQL Server 2018
  3. 不动产登记进入倒计时 哪些房子必须要卖掉?
  4. 微型计算机与裸机,第一章 微型计算机基础
  5. postfix搭建及配置
  6. 【MATLAB统计分析与应用100例】案例007:matlab数据的极差归一化变换
  7. unittest-读取yaml文件
  8. MySQL_管理与维护
  9. Android类似日历的翻转控件
  10. 找回丢失的QQ之普及知识全套
  11. 叶俊应浙江警官学院的邀请讲授《司法警官的心理调试实用策略》
  12. Golang【sgg】
  13. vulnhub 靶机渗透:Stapler
  14. 人类高质量程序员老李是如何过七夕的?
  15. OpenHarmonyOs / LiteOs-a 驱动开发
  16. 堵俊平:开放治理是开源社区的终极之路 | DEV. Together 2021 中国开发者生态峰会...
  17. Java/Swing 图形界面范例
  18. 深入探索Android启动速度优化
  19. 读《ACM图灵奖》有感
  20. 西藏生死书,万物皆奇迹,梁文道《我执》 书摘

热门文章

  1. 【mba项目管理论文】热力集团公司供热工程目项目管理研究(节选)
  2. win7发现不了无线网络_XP及WIN7系统原因导致蓝牙耳机与蓝牙5.0适配器不能正常工作...
  3. 青龙面板总教程(2.10.9版本-含多容器)
  4. evplay 翻录_翻录DVD并将其转换为ISO映像
  5. ac自动机 匹配最长前缀_Aho Corasick自动机结合DoubleArrayTrie极速多模式匹配
  6. 联想服务器如何设置uefi启动不了系统,联想uefi启动设置方法 联想电脑如何设置uefi启动...
  7. 发型匹配系统软件需求说明书
  8. 两台电脑用蓝牙传文件出现“系统资源不足,电脑之间互相传递单个大文件,例如单个文件50g,100g
  9. Java 中的并行处理
  10. github clone报错error: RPC failed; result=56, HTTP code = 200