среда, 20 февраля 2013 г.

Интеграция Hive c HBase (грабли)

Hive - замечательная вещь. Про то, что это и зачем это, написано уже не мало.
В основном все примеры описывают, как делать запросы по хадуповским файлам.
А вот как делать HQL запросы в HBase - информации мало.

Т.к без слёз делать выборки в HBase не получается, пришлось прикручивать Hive

Порядок действий прост.
Создаём в консоли Hive, таблицу связанную с таблицей в HBase, настраиваем мэппинг полей
hive> CREATE EXTERNAL TABLE hive_raw_data(key string,  dmp_id string,  referrer string,  source_id string,  source_name string)
    > STORED BY 'org.apache.hadoop.hive.hbase.HBaseStorageHandler'
    > WITH SERDEPROPERTIES ("hbase.columns.mapping" = "data:dmp_id,  data:referrer,  data:source_id,  data:source_name")      
    > TBLPROPERTIES("hbase.table.name" = "raw_data");

Ууупс, первые грабли. Система не внятно ругается, и никак не создает таблицу. Понять в чем дело - трудно. Оказывается при описании мэппинга ("hbase.columns.mapping" = "data:dmp_id,  data:referrer,  data:source_id,  data:source_name"), не должно быть пробелов между полями! Должно быть вот так: "hbase.columns.mapping" = "data:dmp_id,data:referrer,data:source_id,data:source_name"

Исправляем, и... таблица создана.
запускаем тест select * from hive_row_data и видим что всё работает.
Но радоваться рано, простой select запускает обычный перебор, а как только запрос становится чуть сложнее например select * from hive_row_data where key = 'xxx' вот и начинает всё валится (ибо запускается механизм Map - reduce).
В итоге получаем исключение, вот такого плана:
Exception in thread "Thread-48" java.lang.NullPointerException
    at org.apache.hadoop.hive.shims.Hadoop23Shims.getTaskAttemptLogUrl(Hadoop23Shims.java:44)
    at  org.apache.hadoop.hive.ql.exec.JobDebugger$TaskInfoGrabber.getTaskInfos
    (JobDebugger.java:186)
    at org.apache.hadoop.hive.ql.exec.JobDebugger$TaskInfoGrabber.run(JobDebugger.java:142)
    at java.lang.Thread.run(Thread.java:662) 
На самом деле, те исключения которые валятся в консоль, можно игнорировать. Смотреть надо лог джоба. Залазим - смотрим:
INFO org.apache.hadoop.mapred.TaskInProgress: Error from attempt_201302201311_0003_m_000000_3: java.io.IOException: Cannot create an instance of InputSplit class = org.apache.hadoop.hive.hbase.HBaseSplit:Class     org.apache.hadoop.hive.hbase.HBaseSplit not found at       org.apache.hadoop.hive.ql.io.HiveInputFormat$HiveInputSplit.readFields(HiveInputFormat.java:146)
at org.apache.hadoop.io.serializer.WritableSerialization$WritableDeserializer.deserialize(WritableSerialization.java:73)
at org.apache.hadoop.io.serializer.WritableSerialization$WritableDeserializer.deserialize(WritableSerialization.java:44)
at org.apache.hadoop.mapred.MapTask.getSplitDetails(MapTask.java:351)
at org.apache.hadoop.mapred.MapTask.runOldMapper(MapTask.java:367)
at org.apache.hadoop.mapred.MapTask.run(MapTask.java:327)
at org.apache.hadoop.mapred.Child$4.run(Child.java:268)
at java.security.AccessController.doPrivileged(Native Method)
at javax.security.auth.Subject.doAs(Subject.java:396)
at org.apache.hadoop.security.UserGroupInformation.doAs(UserGroupInformation.java:1332)
at org.apache.hadoop.mapred.Child.main(Child.java:2
Это значит что map-reduce задачи, запускаемые Hive-ом ничего не знают про HBase, и просто не могут найти нужные jar. Беда в том, что эти библиотеки нужны на всех нодах.

Решение здесь такое:
1. Сохраняем нужные Jar в HDFS (/user/hive/)
hadoop dfs -put /usr/lib/hive/lib/hbase-VERSION.jar /user/hive/hbase-VERSION.jar
hadoop dfs -put /usr/lib/hive/lib/hive-hbase-handler-VERSION.jar /user/hive/hive-hbase-handler-VERSION.jar

в моем случае это выглядит так:

hadoop dfs -put /usr/lib/hive/lib/hive-hbase-handler-0.9.0-cdh4.1.3.jar /user/hive/hive-hbase-handler-0.9.0-cdh4.1.3.jar
hadoop dfs -put /usr/lib/hive/lib/hbase-0.92.1-cdh4.1.3-security.jar /user/hive/hbase-0.92.1-cdh4.1.3-security.jar

Затем правим hive-site.xml:
<property>
    <name>hive.aux.jars.path</name>
    <value>/user/hive/hive-hbase-handler-0.9.0-cdh4.1.3.jar,/user/hive/hbase-0.92.1-cdh4.1.3-security.jarN.jar</value>
</property>
Вот и всё. После этого Hive начинает исправно выполнять сложные выборки.
Работаем и наслаждаемся.

https://cwiki.apache.org/Hive/hbasebulkload.html





6 комментариев:

  1. Добрый день.

    А hive-site.xml правили на каждой ноде?
    Или как то глобально установили для всех нод?

    ОтветитьУдалить
  2. Добрый день.
    Я использовал Клаудеровский пакет. Он позволяет централизованно управлять настройками кластера.

    В случае "классического" Хадупа, надо подумать... Возможно и не придется везде править hive-site.xml. Попробуйте его изменить, для начала на той ноде, откуда будете запускать hive - консоль.

    ОтветитьУдалить
  3. О, спасибо за ответ.
    У меня тоже cloudera. :)
    Общая проблема в том, что к нам приходит файл, для парсинга которого используется внешний Serde, который и подключается в hive-site.xml.
    Если на каждной ноде прописать локальный путь к jar-файлу, то hive-консоль отлично работает.
    Но при попытке достучаться к таблице через jdbc (утилитка beeline, собственный java класс) или через ODBC, то подключаемый serde-класс не виден. :(
    Я исправлял hive-site.xml и через "Cloudera Manager" и у HiveServer2, и у Hive Metastore Server, не цепляется класс.
    Есть еще другие способы? :)

    ОтветитьУдалить
    Ответы
    1. Похоже у кого то в classpath, не хватает вашего serde :)

      Не пробовали предварительно скопировать jar на мастер, в Hive и через консоль сделать "ADD JAR /path/your_serde.jar" с абсолютным путем ?

      Удалить
  4. ADD JAR работает и путем на одной ноде file:///bla1
    и HDFS hdfs:///bla2
    Только все работает в пределах сессии. Стоит выйти из локального hive и заново зайти, то уже не работает. А хочется, чтобы работало всегда.

    ОтветитьУдалить
    Ответы
    1. Как разберетесь, обязательно напишите.
      Полезный опыт :)

      Удалить