hadoop性能调优
从三个方面着手优化 : 1. hadoop配置 2. 设计mapred/job 3. 代码级别. 4. 改造hadoop 一. conf/hadoop-site.xml配置. 经验要求高, 特别需要结合实际情况. 典型参数如 复制因子, mapred.child.java.opts, mapred.tasktracker.map.tasks.maximum, mapred.tasktracker.reduce.tasks.maximum, mapred.map.tasks, mapred.reduce.tasks, fs.inmemory.size.mb, dfs.block.size 等等 二. 在同一个job内完成尽可能多的计算任务, 主要是设计key和自定义OutputFormat, 将能合并的计算任务合并. 举例 : 用户访问行为(userid, ip, cookie), 分别统计每个用户的ip数和cookie数. 最简单的设计, 是使用量个job, 分别计算ip数和cookie数.但是我们可以按照下面的思路, 在一个job中完成这两项计算 : (a). 把userid和字段存储到key中
public class UserKey implements WritableComparable<UserKey>{
int userId;//userid
byte field;//0 代表ip, 1代表cookie
@Override
public int compareTo(UserKey o) {
if(userId > o.userId)return 1;
if(userId < o.userId)return -1;
if(field > o.field)return 1;
if(field < o.field)return -1;
return 0;
}
@Override
public void readFields(DataInput in) throws IOException {
// TODO Auto-generated method stub
}
@Override
public void write(DataOutput out) throws IOException {
// TODO Auto-generated method stub
}
}
(b). 实现自定义的OutputFormat, 下面是两处关键代码如下 : (x). SequenceFile.Writer[] writers = new SequenceFile.Writer[2]; writers[0] = SequenceFile.createWriter(FileSystem.get(conf), conf, "ip", IntWritable.class, IntWritable.class, CompressionType.BLOCK, new DefaultCodec()); writers[1] = SequenceFile.createWriter(FileSystem.get(conf), conf, "field", IntWritable.class, IntWritable.class, CompressionType.BLOCK, new DefaultCodec()); (xx). writers[key.field].append(key.userId, value.get()); 三. 避免不必要的reduce任务. (1). 假定要处理的数据是排序且已经分区的. 或者对于一份数据, 需要多次处理, 可以先排序分区. (2). 自定义InputSplit, 将单个分区作为单个mapred的输入. (3). 在map中处理数据, Reducer设置为空. 这样, 既重用了已有的 "排序", 也避免了多余的reduce任务. 四. 使用自定义的MapRunnable. hadoop自带了两个MapRunnable, (1). 一个是默认的单线程MapRunnable, org.apache.hadoop.mapred.MapRunner (2).另一个是多线程的, org.apache.hadoop.mapred.lib.MultithreadedMapRunner. 根据特定情况, 可以自定义MapRunnable, (1). 启用多线程, 比如web爬行时, 可启用多线程抓取网页. (2). 避免map时, 单台tasktracker上辅助数据冗余, 比如在多模匹配时, 避免生成多份DFA. 五. 在某些情况下, 利用数据分布特性设计PARTITIONER的分区算法, 避免单个mapred消耗时间过长. 这跟木桶原理有些神似. 比如处理大量字符串时, (1). 已知首字不同的字符串之间不存在任何关联关系 (2). 原始数据在某些 "首字" 上分布密集, 另一些 "首字" 上分布稀疏. 例如, 原始数据中, 1亿个以3开头, 1亿个以7开头, 3个以6开头. 那么, (1). 如果以首字对4求余分区, 则 "1亿个以3开头" 和 "1亿个以7开头"将落在同一分区.若hadoop群集只支持同时2个map任务, 则... (2). 如果以首字对3求余分区, 则 "1亿个以3开头" 和 "1亿个以7开头"将落在不同分区. 六. 最大限度地重用对象, 避免对象的生成/销毁开销. 该点在hadoop自带的 org.apache.hadoop.mapred.MapRunner中尤为突出, 它使用同一个key对象和同一个value对象不停读取原始数据, 再将自身交给mapper处理. (此处注意, 若要保留该对象的即时状态, 需要clone, 深克隆或浅克隆.) 七. 在逻辑意义上, 合并同一对象. 如dotnet和java中的字符串INTERN技术. 八. 根据已有条件, 简化循环判定. 比如, for(int i = 0; i < end && i < size; i++); 可以改成 : end = end < size ? end : size; for(int i = 0; i < end; i++); 九. 降低多线程数目, 而让固定数目的线程循环处理. 比如, 一台机器8个CPU, 现在需要处理80亿个数据, 那么下面两个方案 : (1). 启动800个线程, 每个线程处理80亿/800个数据. (2). 启动8个线程(注意, 此处是8个), 每个线程循环处理, 每次循环处理100万个. 通常我个人选择方案(2).因为 : (1). 最大限度利用了CPU. (2). 避免了线程调度. (3). 在java中, 可以使用AtomicInteger控制线程循环, AtomicInteger的效率很高. (4). 有时, 还可以避免单个线程消耗时间过长. 十. 使用位移替代浮点数计算. 比如用 100 >> 3替代100 * 0.125. (另外, 我们会需要将某个中间值乘以一个调节因子(经验值), 比如乘以0.12, 如果乘以0.12和0.124 "差不多" 时, 可以考虑直接使用位移). 十一. 避免循环体内不必要的判断逻辑, 与第八条不同. 比如, 处理10亿个数据, 每遇到一个有效数据时, 需要同前一个有效数据进行关联处理(或与前一个中间值进行关联处理),
for(int i = 0; i < size; i++)
{
//1. 判定是否存在前一个有效数据
//2. 如果不存在前一个有效数据, 则continue;
//3. 如果存在前一个有效数据, 则进行关联处理, 再continue.
}
通常在此种需求下, 一旦遇到一个有效数据, 必定会产生一个可供后续紧邻数据关联的值, 那么 :
int i = 0;
for(int i = 0; i < size; i++)
{
//1. data[i]是否有效?
//2. data[i]无效, continue;
//3. data[i]有效, break;
}
for(; i < size; i++)
{
//与前一个有效数据进行关联处理, 再continue.
}
十二. 方法调用过程, 辅助数据尽量放在方法体内, 避免使用全局辅助数据, 一来节省内存, 二来提高对象可重用性. 十三. 尽量不要生成转瞬即逝的对象, 或者专门构建专属对像来完成这一任务. 比如 : 1). 提供直接使用构造函数参数进行序列化的静态方法, 避免先使用参数构造对象再进行序列化. 2). 参考上述第六点. 十四. 利用-1 和 1的关联性, 减少内存使用量, 或携带更多的信息. 比如java.util.Arrays.binarySearch方法的返回值. 十五. 对于方方正正的多位数组Arr[d0][d1][d2]..[dn], 且di >> d(i+1)时, 可以考虑使用一维数组替代, 减少对象. 这是因为java中多位数组实际上使用 "数组的数组" 实现的. 十六. 尽量使key的WritableComparable性能最佳, 尽量使value的Writable性能最佳. 比如使用掩码操作. 十七. 尽早丢弃无关对象. 见 "使用hadoop/mapred的典型计数问题". 十八. 改造hadoop, 使merge过程更具弹性, 或更符合实际需求. 比如 : 1). 使reduce的<key, values>中的values按照顺序迭代. 2). 见 "使用hadoop/mapred的典型计数问题". 十九. 有效设计mapred中的combiner, 尽早降低I/O等操作. 此过程中, 可以结合自定义OutputFormat, 使得同一个Recuder类可同时充当map->merge->reduce中的后两个过程. 见 "使用hadoop/mapred的典型计数问题".
- 半自动化运维之动态添加数据文件(二) (r5笔记第56天)
- 11g Active DataGuard初探(r5笔记第54天)
- Github 项目推荐 | 用于构建端对端对话系统和训练聊天机器人的开源库 —— DeepPavlov
- 我身边的一些数据库事故 (r5笔记第52天)
- 一个清理脚本的改进思路(r5笔记第51天)
- 【专业技术】Python爬虫:抓取手机APP的传输数据
- 海量数据迁移之传输表空间(一) (r5笔记第71天)
- 一条sql语句的改进探索(r5笔记第70天)
- 【专业技术】Node.js 究竟是什么?
- Github 项目推荐 | 用 Pytorch 实现的 WaveNet-Vocoder
- 重启数据库的一场闹剧(r5笔记第68天)
- 【C语言系列】基础语法案例分析(初级篇)
- 一次ORA-00600问题的排查和分析(r5笔记第64、65天)
- SpringMVC入门就这么简单
- JavaScript 教程
- JavaScript 编辑工具
- JavaScript 与HTML
- JavaScript 与Java
- JavaScript 数据结构
- JavaScript 基本数据类型
- JavaScript 特殊数据类型
- JavaScript 运算符
- JavaScript typeof 运算符
- JavaScript 表达式
- JavaScript 类型转换
- JavaScript 基本语法
- JavaScript 注释
- Javascript 基本处理流程
- Javascript 选择结构
- Javascript if 语句
- Javascript if 语句的嵌套
- Javascript switch 语句
- Javascript 循环结构
- Javascript 循环结构实例
- Javascript 跳转语句
- Javascript 控制语句总结
- Javascript 函数介绍
- Javascript 函数的定义
- Javascript 函数调用
- Javascript 几种特殊的函数
- JavaScript 内置函数简介
- Javascript eval() 函数
- Javascript isFinite() 函数
- Javascript isNaN() 函数
- parseInt() 与 parseFloat()
- escape() 与 unescape()
- Javascript 字符串介绍
- Javascript length属性
- javascript 字符串函数
- Javascript 日期对象简介
- Javascript 日期对象用途
- Date 对象属性和方法
- Javascript 数组是什么
- Javascript 创建数组
- Javascript 数组赋值与取值
- Javascript 数组属性和方法
- Spring 自定义注解你了解过吗?
- 《Flutter 小技巧》一行禁用App,一行置灰App,致敬
- 字符串:总结篇!
- 3 个助你玩转正则表达式的利器
- Flutter 实现虎牙/斗鱼 弹幕效果
- 计算机视觉的数据增广技术大盘点!附涨点神器,已开源!
- 说一句最好的Flutter开源项目也不过分
- 超过百万的StackOverflow Flutter 问题
- Flutter 吐血整理组件继承关系图
- 单元测试框架怎么搭?新版的Junit5有哪些神奇之处?
- 开源179个Flutter组件的详细使用介绍
- 10 个你可能还不知道 VS Code 使用技巧
- Flutter 步骤进度组件
- 慌:一次订单号重复,差点被开除
- Flutter 分页功能表格控件