错误描述
今天在 使用 MapReduce 将 Person 表中的 name 字段 的数据 写入到 Person_mr 表中 报错
代码 如下 :
Mapper
package com.wangt.habse.mr01;
import org.apache.hadoop.hbase.Cell;
import org.apache.hadoop.hbase.CellUtil;
import org.apache.hadoop.hbase.client.Put;
import org.apache.hadoop.hbase.client.Result;
import org.apache.hadoop.hbase.io.ImmutableBytesWritable;
import org.apache.hadoop.hbase.mapreduce.TableMapper;
import org.apache.hadoop.hbase.util.Bytes;
import java.io.IOException;
/**
* 从 Person 表中读取数据 写到 Person_mr 表中
*
* @author 王天赐
* @create 2019-08-02 20:08
*/
public class PersonMapper extends TableMapper<ImmutableBytesWritable, Put> {
// 输出类型是 ImmutableBytesWritable(序列化的Byte 数组) 和 Put
/**
* 从 Hbase 的 Person 表中读取数据
*
* @param key rowkey
* @param value 从 Hbase 表中读取数据的 Result 对象
* @param context
* @throws IOException
* @throws InterruptedException
*/
@Override
protected void map(ImmutableBytesWritable key, Result value, Context context)
throws IOException, InterruptedException {
// 构建 Put 对象
// 注意 : 这个key是序列化的 rowkey
Put put = new Put(key.get());
System.out.println(Bytes.toString(key.get()));
// 遍历数组
Cell[] cells = value.rawCells();
for (Cell cell : cells) {
// 获取 HBase 的列
String qualifier = Bytes.toString(CellUtil.cloneQualifier(cell));
// 列族
String family = Bytes.toString(CellUtil.cloneFamily(cell));
switch (qualifier) {
// 判断字段 是否是 name
// 如果是 name 列 ,则将 name列的数据 添加到 Put 对象中 注意 一个 map 只读一条数据
case "name":
put.add(cell);
break;
}
}
context.write(key, put);
}
}
Reducer
package com.wangt.habse.mr01;
import org.apache.hadoop.hbase.client.Put;
import org.apache.hadoop.hbase.client.Result;
import org.apache.hadoop.hbase.io.ImmutableBytesWritable;
import org.apache.hadoop.hbase.mapreduce.TableReducer;
import org.apache.hadoop.io.NullWritable;
import java.io.IOException;
/**
* 将数据写入到 Person_mr 表中
*
* @author 王天赐
* @create 2019-08-02 20:12
*/
public class PersonReducer extends TableReducer<ImmutableBytesWritable, Put, ImmutableBytesWritable> {
@Override
protected void reduce(ImmutableBytesWritable key, Iterable<Put> values, Context context) throws IOException, InterruptedException {
// 将 put 直接输出即可
// put 中封装了 存入新表的数据
for (Put value : values) {
context.write(key, value);
}
}
}
然后就 报错
Error: java.lang.IllegalArgumentException: No columns to insert
一开我以为 是没读到数据 ,但是我看了下 打印的日志
发现读到数据了 , 又检查下代码 ,怎么想都想不明白 明明读入数据了 , 但是还是不明白 为什么会报这种错误 .,然后我根据报错位置查了下这个报错触发的原因
当 满足 Put.isEmpty 为 true 的时候 则会抛出异常 ,然后我看了下 put 为 空的条件
找到 familyMap
可以看到这里存储的 是 Cell 对象 ,
如下 图 , 可以看到 我们使用 put 添加的数据最终都添加到 familyMap
familymap的k是 列族 , KeyValue 是 Cell的 子类 KeyValue 的集合
那么接下来 分析我们的代码 , 这里已经可以确定报错的大致原因 :
- Map 阶段是 从 Person 中读取数据 Person 表中有 两个列 分别是 name 和 age 我现在需要把 Person 表中的Name列的数据输出到 Person_mr 表中, 报错,出现读取的某一个 Cell 或者 某几个 不存在 name 字段
- 猜测 : 可能是 某一条数据中没有 name 字段
然后 我仔细检查了下 之前的数据 , 然后果然发现问题
可以看到 : 最后一条数据没有 name 列 ,在 读取的时候 是读取不到的 ,又因为 一个 put 只添加了 一条 name 列的数据, 所以 出现问题
所以 只需要在 输出到 Reduce 时检查下 put 是否为空 即可
if(!put.isEmpty()){
// 输出
context.write(key, put);
}