聊聊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的这点数据。