我正在用marklogic数据库评估spark。我已经阅读了一个csv文件,现在我有一个JavaRDD对象,我必须将其转储到marklogic数据库。
SparkConf conf = new SparkConf().setAppName("org.sparkexample.Dataload").setMaster("local");
JavaSparkContext sc = new JavaSparkContext(conf);
JavaRDD<String> data = sc.textFile("/root/ml/workArea/data.csv");
SQLContext sqlContext = new SQLContext(sc);
JavaRDD<Record> rdd_records = data.map(
new Function<String, Record>() {
public Record call(String line) throws Exception {
String[] fields = line.split(",");
Record sd = new Record(fields[0], fields[1], fields[2], fields[3],fields[4]);
return sd;
}
});
这个JavaRDD对象我想写入marklogic数据库。
是否有任何spark api可以更快地写入marklogic数据库?
让我们说,如果我们不能直接将JavaRDD写入marklogic,那么实现这一目标的当前方法是什么?
以下是我用来将JavaRDD数据写入marklogic数据库的代码,让我知道这是否是错误的方法。
final DatabaseClient client = DatabaseClientFactory.newClient("localhost",8070, "MLTest");
final XMLDocumentManager docMgr = client.newXMLDocumentManager();
rdd_records.foreachPartition(new VoidFunction<Iterator<Record>>() {
public void call(Iterator<Record> partitionOfRecords) {
while (partitionOfRecords.hasNext()) {
Record record = partitionOfRecords.next();
System.out.println("partitionOfRecords - "+record.toString());
String docId = "/example/"+record.getID()+".xml";
JAXBContext context = JAXBContext.newInstance(Record.class);
JAXBHandle<Record> handle = new JAXBHandle<Record>(context);
handle.set(record);
docMgr.writeAs(docId, handle);
}
}
});
client.release();
我已经使用java客户端api来编写数据,但即使POJO类记录正在实现Serializable接口,我也会遇到异常。请让我知道可能是什么原因&amp;如何解决这个问题。
org.apache.spark.sparkexception任务不可序列化。
答案 0 :(得分:1)
将数据导入MarkLogic的最简单方法是通过HTTP和客户端REST API - 特别是/ v1 / documents端点 - http://docs.marklogic.com/REST/client/management。
有多种方法可以优化它,例如通过写集,但根据你的问题,我认为首先要决定的是 - 你想为每个记录写什么样的文档?您的示例显示CSV中的5列 - 通常,您将编写具有5个字段/元素的JSON或XML文档,每个字段/元素都基于列索引进行命名。因此,您需要编写一些代码来生成JSON / XML,然后使用您喜欢的任何HTTP客户端(以及一个选项是MarkLogic Java Client API)将该文档写入MarkLogic。
这解决了如何将JavaRDD写入MarkLogic的问题 - 但如果您的目标是尽快将CSV中的数据传输到MarkLogic,那么请跳过Spark并使用mlcp - https://docs.marklogic.com/guide/mlcp/import#id_70366 - 这涉及零编码。
答案 1 :(得分:0)
来自spark streaming guide的修改示例,在这里您必须实现特定于数据库的连接和编写逻辑。
public void send(JavaRDD<String> rdd) {
rdd.foreachPartition(new VoidFunction<Iterator<String>>() {
@Override
public void call(Iterator<String> partitionOfRecords) {
// ConnectionPool is a static, lazily initialized pool of
Connection connection = ConnectionPool.getConnection();
while (partitionOfRecords.hasNext()) {
connection.send(partitionOfRecords.next());
}
ConnectionPool.returnConnection(connection); // return to the pool
// for future reuse
}
});
}
答案 2 :(得分:0)
我想知道你是否只需要确保你在VoidFunction内访问的所有内容都是可序列化的(参见this page)。 DatabaseClient和XMLDocumentManager当然不是可序列化的,因为它们是连接的资源。但是,你没有在你的VoidFunction中实例化DatabaseClient,因为效率会降低(尽管它会起作用)。我不知道以下想法是否适合火花。但我猜你可以创建一个保持单个DatabaseClient实例的类:
public static class MLClient {
private static DatabaseClient singleton;
private MLClient() {}
public static DatabaseClient get(DatabaseClientFactory.Bean connectionInfo) {
if ( connectionInfo == null ) {
throw new IllegalArgumentException("connectionInfo cannot be null");
}
if ( singleton == null ) {
singleton = connectionInfo.newClient();
}
return singleton;
}
}
然后你只需在你的VoidFunction之外创建一个可序列化的DatabaseClientFactory.Bean,这样你的身份验证信息仍然是集中的
DatabaseClientFactory.Bean connectionInfo =
new DatabaseClientFactory.Bean();
connectionInfo.setHost("localhost");
connectionInfo.setPort(8000);
connectionInfo.setUser("admin");
connectionInfo.setPassword("admin");
connectionInfo.setAuthenticationValue("digest");
然后在你的VoidFunction中你可以得到那个单例DatabaseClient和新的XMLDocumentManager,如下所示:
DatabaseClient client = MLClient.get(connectionInfo);
XMLDocumentManager docMgr = client.newXMLDocumentManager();