錯誤描述
今天在 使用 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);
}