关于Spark批处理读写Phoenix,我找到两种方法,整理成笔记,用作备忘。
方法一
Phoenix官方提供了Spark插件,可以激活Spark和Phoenix的交互。
地址:http://phoenix.apache.org/phoenix_spark.html
如果使用CDH,Cloudera也提供了相应的工具,来实现Spark和Phoenix的交互。
地址:https://docs.cloudera.com/documentation/enterprise/6/6.3/topics/phoenix_spark_connector.html
这两个工具都是通过zookeeper的方式连接Phoenix的。
如果集群启用了Kerberos,如果Spark和Phoenix在同一个集群中,访问应该没问题,但是如果Spark和Phoenix不在同一个集群中,那么使用这两个工具可能会因为Kerberos认证问题,无法正常进行交互。
这时候可以使用Spark访问Phoenix客户端的方式进行交互。这也是下面要介绍的方法二。
方法二
Spark通用输出方案 foreachPartition
,这个方法来源于 Spark Streaming通用的输出算子foreachRDD,可参考我的另一篇文章 Spark Streaming保存数据到HBase。
这里以CDH平台为例,描述配置方法及操作步骤。
平台启用了FreeIPA,FreeIPA是Kerberos的高级管理工具,但不只是拥有Kerberos的能力。
1、oozie-share-lib/spark 添加phoenix-client.jar
2、spark需要hive表分区目录rx权限
3、oozie任务配置,凭证勾选hcat
4、spark使用foreachPartition,每个partition连接phoenix,写入数据
关键代码示例:
public static void saveToPhoenix(Dataset<Row> df) {
df.foreachPartition(partitionOfRecords -> {
String columns = "id,name";
String phoenixUrl = "jdbc:phoenix:master1.bigdata.com:2181:/hbase";
String phoenixSQL = String.join("",
"UPSERT INTO test.test (",
columns,
") VALUES(?,?)");
Properties props = new Properties();
props.setProperty("phoenix.schema.isNamespaceMappingEnabled", "true");
props.setProperty("phoenix.schema.mapSystemTablesToNamespace", "true");
Connection phoenixConn = null;
PreparedStatement phoenixPS = null;
try {
phoenixConn = PhoenixUtils.getConn(phoenixUrl, props);
phoenixConn.setAutoCommit(true); // phoenix默认不启用自动提交,这里启用自动提交,方便批操作
phoenixPS = phoenixConn.prepareStatement(phoenixSQL);
Integer cnt = 0;
while (partitionOfRecords.hasNext()) {
Row line = partitionOfRecords.next();
phoenixPS.setObject(1, line.getAs("id"));
phoenixPS.setObject(2, line.getAs("name"));
phoenixPS.addBatch();
cnt += 1;
if (cnt >= 5000) {
phoenixPS.executeBatch();
cnt = 0;
}
}
if (cnt > 0) {
phoenixPS.executeBatch();
}
} finally {
if (phoenixPS != null) {
try {
phoenixPS.close();
} catch (Exception e) {}
}
if (phoenixConn != null) {
try {
phoenixConn.setAutoCommit(false);
phoenixConn.close();
} catch (Exception e) {}
}
}
});
}