最近遇到了一个神奇的问题。我有一个5M的数据,用pig处理非常慢。分析mapreduce日志,发现是第一个处理数据的mapper非常慢。为什么呢?我一直找不到原因,直到我偶然把这5M数据的生成方式改为非压缩后,我立即明白了:这5M的数据非压缩情况下有900M!
也即是说,当第一个mapper把数据加载内存做解压后,一个5M的数据变成了900M。实际上在java对象中,磁盘上的900M加载的内存后大概会变成两倍。显然对于一个1.5G大小内存的mapper是处理不了的。于是这台机器就把数据换到磁盘上,这显然会极大的降低数据处理的速度。
为此一个直观的策略是,启动多个mapper。但是pig中不能直接指定mapper的数据,需要一些特别的mapreduce参数调整。具体的mapper数量计算方法,我暂时还不知道。但是如果设置上这两个参数后,真的就可以启动5个mapper来处理这5M数据了。
-- register
REGISTER DuckPigUdf.jar;
-- set
set mapred.min.split.size 1048576;
set pig.maxCombinedSplitSize 1048576;
-- define
DEFINE duck_storage duck.java.pig.udf.storage();
-- clean up
rmf $output_data_path;
-- load data
raw_events = LOAD '$input_data_path' USING duck_storage;
group_all = GROUP raw_events ALL;
count_all = FOREACH group_all GENERATE COUNT_STAR(raw_events) AS total_count;
group_events = GROUP raw_events BY (money) PARALLEL 10;
count_events = FOREACH group_events{
m_count = COUNT_STAR(raw_events);
GENERATE flatten(raw_events) AS (*), m_count AS m_count;
};
total_events = CROSS count_all, count_events;
result = FOREACH total_events {
rate = (double)count_events::m_count/count_all::total_count;
GENERATE money, rate;
};
-- store
STORE result INTO '$output_data_path' USING PigStorage('\u0001');
总结:对于高压缩比的数据,需要调整mapper读取数据块的分片大小,使mapper处理尽量小的数据,从而当数据在内存解压后,能完全容纳