数据量大的时候,对数据进行采样,然后再做模型分析。作为数据仓库的必备品hive,我们如何对其进行采样呢?
假设有一张包含100亿行的Hive表,希望有效地随机抽样一个固定行数的数据 - 比如10000。最明显(而且显然是错误的)的方法是:
select * from my_table
limit 10000;
如果不对表进行排序,Hive不保证数据的顺序,但在实践中,它们按照它们在文件中的顺序返回,所以这远非真正随机。那么接着可以尝试:
select * from my_table
order by rand()
limit 10000;
这确实提供了真正的随机数据,但性能并不是那么好。为了实现总排序,Hive必须将所有数据强制传输到单个reducer。该reducer将对整个数据集进行排序。这很不好。幸运的是,Hive有一个非标准SQL“sort by”子句,它只在单个reducer中排序,并且不保证数据跨多个reducers中排序:
select * from my_table
sort by rand()
limit 10000;
这要好得多,但我不相信它真的是随机的。问题是Hive的将数据拆分为多个reducer的方法是未定义的。它可能是真正随机的,它可能基于文件顺序,它可能基于数据中的某些值。Hive如何在reducers中实现limit子句也是未定义的。也许它按顺序从reducer中获取数据 - 即,reducer 0中的所有数据,然后全部来reducer1,等等。也许它通过它们循环并将所有内容混合在一起。
在最坏的情况下,假设reduce 的key是基于数据列,而limit子句是reducers的顺序。然后样品会非常倾斜。
解决方案是另一个非标准的Hive功能:“distribute by”。对于reduce key不是由查询结构确定的查询(没有“group by”,没有join),可以准确指定reduce key的内容。如果我们随机分布,并在每个reducer中随机排序,那么“limit”功能如何无关紧要。
select * from my_table
distribute by rand()
sort by rand()
limit 10000;
最后,作为最后一次优化,可以在map-side做一些过滤。如果表的总大小是已知的,轻松设置一个随机阈值条件来进行数据过滤,如下所示:
select * from my_table
where rand() <= 0.0001
distribute by rand()
sort by rand()
limit 10000;
在这种情况下,由于总大小是100亿,样本大小是一万,我可以很容易地计算出样本占总数据的0.000001。但是,如果where子句是“rand()<0.000001”,则最终输出的行数可能少于10000行。“rand()<0.000002”可能会起作用,但这确实依赖于rand()有非常好的实现。最后它并不重要,因为瓶颈是全表扫描,而不是传输给reducer的这点数据。
网友评论