HBase踩坑笔记 - 使用 MR 集成Hbase 报错 IllegalArgumentException: No columns to insert ⭐️⭐️⭐️⭐️

错误描述

今天在 使用 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 的集合
在这里插入图片描述

那么接下来 分析我们的代码 , 这里已经可以确定报错的大致原因 :

  1. Map 阶段是 从 Person 中读取数据 Person 表中有 两个列 分别是 name 和 age 我现在需要把 Person 表中的Name列的数据输出到 Person_mr 表中, 报错,出现读取的某一个 Cell 或者 某几个 不存在 name 字段
  2. 猜测 : 可能是 某一条数据中没有 name 字段

然后 我仔细检查了下 之前的数据 , 然后果然发现问题
在这里插入图片描述
可以看到 : 最后一条数据没有 name 列 ,在 读取的时候 是读取不到的 ,又因为 一个 put 只添加了 一条 name 列的数据, 所以 出现问题

所以 只需要在 输出到 Reduce 时检查下 put 是否为空 即可

 if(!put.isEmpty()){
            // 输出
            context.write(key, put);
        }

补充 :

如果 你遇到和 我相同的问题 , 建议不要上来直接拿我的解决办法 , 可以参考我的思路找到自己的问题所在, 这样才能解决问题

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章