spark之读写Elasticsearch

最近有需求,要将spark的数据写入elasticsearch。亲自测试后,将结果进行分享:
直接上代码:

创建Maven工程pom文件:

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>org.example</groupId>
    <artifactId>estest</artifactId>
    <version>1.0-SNAPSHOT</version>

    <properties>
        <spark.version>2.4.4</spark.version>
        <scala.version>2.11.12</scala.version>
        <hadoop.version>2.6.0</hadoop.version>
        <elasticsearch.version>7.12.0</elasticsearch.version>
    </properties>

    <dependencies>

        <dependency>
            <groupId>org.apache.spark</groupId>
            <artifactId>spark-core_2.11</artifactId>
            <version>${spark.version}</version>
            <scope>provided</scope>
        </dependency>

        <dependency>
            <groupId>org.apache.spark</groupId>
            <artifactId>spark-sql_2.11</artifactId>
            <version>${spark.version}</version>
        </dependency>

        <dependency>
            <groupId>org.elasticsearch</groupId>
            <artifactId>elasticsearch-spark-20_2.11</artifactId>
            <version>${elasticsearch.version}</version>
        </dependency>

        <!-- https://mvnrepository.com/artifact/org.json4s/json4s-native -->
        <dependency>
            <groupId>org.json4s</groupId>
            <artifactId>json4s-native_2.12</artifactId>
            <version>3.6.11</version>
            <scope>provided</scope>
        </dependency>

    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>net.alchim31.maven</groupId>
                <artifactId>scala-maven-plugin</artifactId>
                <version>3.2.2</version>
                <configuration>
                    <recompileMode>incremental</recompileMode>
                </configuration>
                <executions>
                    <execution>
                        <goals>
                            <goal>compile</goal>
                            <goal>testCompile</goal>
                        </goals>
                    </execution>
                </executions>
            </plugin>

            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-assembly-plugin</artifactId>
                <version>2.2.1</version>
                <configuration>
                    <descriptorRefs>
                        <descriptorRef>jar-with-dependencies</descriptorRef>
                    </descriptorRefs>
                </configuration>
                <executions>
                    <execution>
                        <id>make-assembly</id>
                        <phase>package</phase>
                        <goals>
                            <goal>single</goal>
                        </goals>
                    </execution>
                </executions>
            </plugin>

        </plugins>
    </build>
    
</project>

主要提供了两种读写方式:一种是通过DataFrameReader/Writer传入ES Source实现;另一种是直接读写DataFrame实现。在实现前,还需要知道一些相关的配置:

参数描述
es.nodes.wan.onlytrue or false,在此模式下,连接器禁用发现,并且所有操作通过声明的es.nodes连接
es.nodesES节点
es.portES端口
es.index.auto.createtrue or false,是否自动创建index
es.resource资源路径
es.mapping.ides会为每个文档分配一个全局id。如果不指定此参数将随机生成;如果指定的话按指定的来
es.batch.size.byteses批量API的批量写入的大小(以字节为单位)
es.batch.write.refresh批量更新完成后是否调用索引刷新
es.read.field.as.array.include读es的时候,指定将哪些字段作为数组类型

注意:运行程序时需要ES的jar包,可以将elasticsearch-spark-20_2.11-7.12.0.jar打进包里,也可以将es的包上传到本地服务器,在shell中指定jar包(--jars /home/pro/muzili/applications/sbin/elasticsearch-spark-20_2.11-7.12.0.jar)或将jar包上传到spark安装目录的jars下。要不然会报类找不到异常。es.index.auto.create设置为true时写数据时没有库则会自动创建。

1.DataFrameReader读ES

法一:

package com.muzili.applications

import org.apache.spark.SparkConf
import org.apache.spark.sql.{DataFrame, SparkSession}

object Spark_Read_Es2 {
  def main(args: Array[String]): Unit = {

    val conf = new SparkConf().setAppName("Spark_Read_Es2").setMaster("local[*]")
    conf.set("es.index.auto.create","true")
    conf.set("es.nodes","190.191.200.141,190.191.200.142,190.191.200.143")
    conf.set("es.port","9200")
    conf.set("es.nodes.wan.only","true")
    conf.set("es.read.field.as.array.include","array名字")//
    val spark: SparkSession = SparkSession.builder().config(conf).getOrCreate()
    val ess: DataFrame = spark.sqlContext.read.format("org.elasticsearch.spark.sql")
      .option("inferSchema", "true")
      .load("es_test/_doc")
    ess.show(false)

  }

}

法二:

package com.muzili.applications

import org.apache.spark.sql.{DataFrame, SparkSession}

object Spark_Read_Es1 {
  def main(args: Array[String]): Unit = {

    val spark: SparkSession = SparkSession.builder().appName("Spark_Read_Es1").getOrCreate()

    val options = Map(
      "es.nodes.wan.only" -> "true",
      "es.nodes" -> "190.191.200.141,190.191.200.142,190.191.200.143",
      "es.port" -> "9200",
      "es.read.field.as.array.include" -> "arr1, arr2"
    )

    val df: DataFrame = spark
      .read
      .format("org.elasticsearch.spark.sql")
      .options(options)
      .load("es_test/_doc")
    
    df.show()
  }
}

2.DataFrameWriter写ES

package com.muzili.applications

import org.apache.spark.sql.{SaveMode, SparkSession}

object Spark_To_Es1 {
  def main(args: Array[String]): Unit = {

    val spark: SparkSession = SparkSession.builder().appName("Spark_To_Es1").getOrCreate()

    val options = Map(
      "es.index.auto.create" -> "true",
      "es.nodes.wan.only" -> "true",
      "es.nodes" -> "190.191.200.141,190.191.200.142,190.191.200.143",
      "es.port" -> "9200",
      "es.mapping.id" -> "id"
    )

    val sourceDF = spark.read.parquet("/user/pro/tmp/20200521")

    sourceDF
      .write
      .format("org.elasticsearch.spark.sql")
      .options(options)
      .mode(SaveMode.Append)
      .save("es_test/_doc")
  }
}

3.读DataFrame

jar包中提供了esDF()方法可以直接读es数据为DataFrame,以下是源码。

  class SparkSessionFunctions(ss: SparkSession) extends Serializable {
    def esDF() = EsSparkSQL.esDF(ss)
    def esDF(resource: String) = EsSparkSQL.esDF(ss, resource)
    def esDF(resource: String, query: String) = EsSparkSQL.esDF(ss, resource, query)
    def esDF(cfg: scala.collection.Map[String, String]) = EsSparkSQL.esDF(ss, cfg)
    def esDF(resource: String, cfg: scala.collection.Map[String, String]) = EsSparkSQL.esDF(ss, resource, cfg)
    def esDF(resource: String, query: String, cfg: scala.collection.Map[String, String]) = EsSparkSQL.esDF(ss, resource, query, cfg)
  }

简单说一下各个参数:

resource:资源路径,例如hive_table/docs

cfg:一些es的配置,和上面代码中的options差不多

query:指定DSL查询语句来过滤要读的数据,例如"?q=user_group_id:3"表示读user_group_id为3的数据

val options = Map(
  "pushdown" -> "true",
  "es.nodes.wan.only" -> "true",
  "es.nodes" -> "190.191.200.141,190.191.200.142,190.191.200.143",
  "es.port" -> "9200"
)

val df = spark.esDF("es_test/docs", "?q=user_group_id:3", options)
df.show()

4.写DataFrame

jar包中提供了saveToEs()方法可以将DataFrame写入ES,以下是源码。

  // the sparkDatasetFunctions already takes care of this
  // but older clients might still import it hence why it's still here
  implicit def sparkDataFrameFunctions(df: DataFrame) = new SparkDataFrameFunctions(df)

  class SparkDataFrameFunctions(df: DataFrame) extends Serializable {
    def saveToEs(resource: String): Unit = { EsSparkSQL.saveToEs(df, resource) }
    def saveToEs(resource: String, cfg: scala.collection.Map[String, String]): Unit = { EsSparkSQL.saveToEs(df, resource, cfg) }
    def saveToEs(cfg: scala.collection.Map[String, String]): Unit = { EsSparkSQL.saveToEs(df, cfg)    }
  }

resource:资源路径,例如hive_table/docs

cfg:一些es的配置,和上面代码中的options差不多

package com.muzili.applications

import org.apache.spark.sql.SparkSession

import org.elasticsearch.spark.sql._

object Spark_To_Es2 {
  def main(args: Array[String]): Unit = {

    val spark: SparkSession = SparkSession.builder().appName("Spark_To_Es2").getOrCreate()

    val options = Map(
      "es.index.auto.create" -> "true",
      "es.nodes.wan.only" -> "true",
      "es.nodes" -> "190.191.200.141,190.191.200.142,190.191.200.143",
      "es.port" -> "9200",
      "es.mapping.id" -> "id"
    )

    val df = spark.read.parquet("/user/pro/tmp/20200521")

    df.saveToEs("es_test/docs", options)

  }
}

5.Structured Streaming - ES

es也提供了对Structured Streaming的集成,使用Structured Streaming可以实时的写入ES。

import org.elasticsearch.spark.sql._
val options = Map(
  "es.index.auto.create" -> "true",
  "es.nodes.wan.only" -> "true",
  "es.nodes" -> "190.191.200.141,190.191.200.142,190.191.200.143",
  "es.port" -> "9200",
  "es.mapping.id" -> "id"
)
val df = spark
  .readStream
  .format("kafka")
  .option("kafka.bootstrap.servers", "a:9092,b:9092,c:9092")
  .option("subscribe", "test")
  .option("failOnDataLoss", "false")
  .load()

df
  .writeStream
  .outputMode(OutputMode.Append())
  .format("es")
  .option("checkpointLocation", s"hdfs://hadoop:8020/checkpoint/test01")
  .options(options)
  .start("test_streaming/docs")
  .awaitTermination()

6.实战

1.将hdfs上的数据写入Es:

import org.apache.spark.sql.{DataFrame, SparkSession}
import org.elasticsearch.spark.sql._

object Spark_To_Es {
  def main(args: Array[String]): Unit = {

    val spark: SparkSession = SparkSession.builder().appName("Spark_To_Es")  //.master("local[*]")
      .config("spark.es.nodes", "190.191.200.141,190.191.200.142,190.191.200.143")
      .config("spark.es.port", "9200")
      .config("spark.es.mapping.id","id")
      .config("es.batch.size.bytes","0.5mb")
      .config("es.batch.size.entries","500")
      .config("es.batch.write.retry.count","5")
      .config("es.write.operation","upsert") // update/upsert/default
      .getOrCreate()

    val EsReadPath1 = "/user/pro/muzili/picture_code_in_one/full_update/01/result/face_archive.json/*"

    val sourceDF1: DataFrame = spark.read.json(EsReadPath1)
    sourceDF1.printSchema()
    sourceDF1.repartition(1).saveToEs("figure_code/_doc")
    println("------------数据写入成功----------------")

  }

}

2.将数据库mysql中的数据写入Es:


import java.util.Properties

import org.apache.spark.SparkConf
import org.apache.spark.sql.{DataFrame, SparkSession}
import org.elasticsearch.spark.sql.EsSparkSQL

/**
  * author:muzili
  * name:ESDemo
  */
object ESDemo {
  def main(args: Array[String]): Unit = {
    val sparkConf = new SparkConf().setAppName(ESDemo.getClass.getName).setMaster("local")
    sparkConf.set("es.nodes","190.191.200.141,190.191.200.142,190.191.200.143")
    sparkConf.set("es.port","9200")
    sparkConf.set("es.index.auto.create", "true")
    sparkConf.set("es.write.operation", "index")
    val sparkSession: SparkSession = SparkSession.builder().config(sparkConf).getOrCreate()

    val url: String = "jdbc:mysql://localhost:3306/testdb"
    val table: String = "courses"
    val properties: Properties = new Properties()
    properties.put("user","root")
    properties.put("password","123456")
    properties.put("driver","com.mysql.jdbc.Driver")
    val course: DataFrame = sparkSession.read.jdbc(url,table,properties)
    course.show()
    EsSparkSQL.saveToEs(course,"course")

    sparkSession.stop()
  }
}

3.将postgresql中的数据写入Es:

package com.muzili.applications

import org.apache.spark.SparkConf
import org.apache.spark.sql.{DataFrame, SparkSession}
import org.elasticsearch.spark.sql.EsSparkSQL

/**
 * author:muzili
 * name:ESDemo
 */
object ESDemo {
  def main(args: Array[String]): Unit = {

    val sparkConf = new SparkConf().setAppName(ESDemo.getClass.getName)//.setMaster("local")
    sparkConf.set("es.nodes","190.191.200.141,190.191.200.142,190.191.200.143")
    sparkConf.set("es.port","9200")
    sparkConf.set("es.index.auto.create", "true")
    sparkConf.set("es.write.operation", "index")

    val spark: SparkSession = SparkSession.builder().config(sparkConf).getOrCreate()
    

    val df: DataFrame = read_pgsql(spark, "device_report_20210413")

    df.show()

    EsSparkSQL.saveToEs(df,"test")  //resource:资源路径,例如hive_table/docs

    spark.stop()
  }

  def read_pgsql(spark:SparkSession,table_name:String): DataFrame = {
    import java.util.Properties
    val url = "jdbc:postgresql://190.176.35.140:5432/data_governance_db?user=root&password=123456"
    val connectionProperties = new Properties()
    connectionProperties.setProperty("Driver","org.postgresql.Driver")
    val df = spark.read.jdbc(url, table_name, connectionProperties)
    df
  }
}

4.将json数据写入Es:

package com.muzili.applications

import org.apache.spark.sql.{SaveMode, SparkSession}

object Spark_To_Es_Test {
  //com.muzili.applications.Spark_To_Es_Test
  def main(args: Array[String]): Unit = {

    val spark = SparkSession.builder()
      .appName("Spark_To_Es")
      .getOrCreate()

    val options = Map(
      "es.index.auto.create" -> "true",
      "es.nodes.wan.only" -> "true",
      "es.nodes" -> "190.191.200.141,190.191.200.142,190.191.200.143",
      "es.port" -> "9200",
      "es.mapping.id" -> "aid"
    )

    val sourceDF = spark.read.format("json").load("/user/xdata/aid/picture_code_in_one/test/result.json/*")
    sourceDF.show(10,false)

    sourceDF
      .write
      .format("org.elasticsearch.spark.sql")
      .options(options)
      .mode(SaveMode.Append)
      .save("es_test/_doc")
  }
}

执行脚本:

#!/bin/bash
#部署在102的/home/pro/muzili上

BASE_HOME=/home/pro/muzili/DataFusion
LOGDIR=$BASE_HOME/logs/Spark_To_Es.out

spark-submit --master spark://190.176.35.102:7079 \
--conf spark.cores.max=71 \
--driver-memory 18g \
--jars /home/pro/muzili/DataFusion/sbin/elasticsearch-spark-20_2.11-7.12.0.jar \
--class com.muzili.applications.Spark_To_Es \
/home/pro/muzili/DataFusion/sbin/estest-1.0-SNAPSHOT.jar > $LOGDIR & \
echo "脚本执行成功!"

7.References

ES Spark Support文档:

https://www.elastic.co/guide/en/elasticsearch/hadoop/current/spark.html#spark

ES Spark Configuration:

 https://www.elastic.co/guide/en/elasticsearch/hadoop/current/configuration.html

  • 0
    点赞
  • 20
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
### 回答1: Spark SQL 可以通过 Elasticsearch-Hadoop 插件来 Elasticsearch。该插件提供了一个 Elasticsearch 数据源,可以将 Elasticsearch 中的数据作为 Spark SQL 表进行查询和分析。 要使用 Elasticsearch-Hadoop 插件,需要在 Spark 配置文件中添加以下配置: ``` spark.es.nodes=<Elasticsearch 节点 IP> spark.es.port=<Elasticsearch 节点端口> ``` 然后,可以使用 Spark SQL 的 DataFrame API 或 SQL API 来 Elasticsearch 数据。以下是一些示例代码: ``` // Elasticsearch 中的数据 val df = spark.read.format("org.elasticsearch.spark.sql") .option("es.resource", "<Elasticsearch 索引>/<Elasticsearch 类型>") .load() // 将 DataFrame 中的数据Elasticsearch df.write.format("org.elasticsearch.spark.sql") .option("es.resource", "<Elasticsearch 索引>/<Elasticsearch 类型>") .save() ``` 需要注意的是,Elasticsearch-Hadoop 插件的版本需要与 Elasticsearch 版本匹配。具体的版本对应关系可以参考官方文档。 ### 回答2: Spark SQL是一款强大的数据处理工具,可以实现对不同数据源的取和处理,而Elasticsearch是一款流行的开源搜索引擎,在构建实时搜索和分析系统时非常有用。Spark SQL可以轻松地与Elasticsearch集成,方便地进行数据取和入操作。下面我们将详细介绍Spark SQLElasticsearch的过程。 一、安装Spark Elasticsearch插件 在使用Spark SQLElasticsearch之前,我们需要安装相应的插件以便于连接和处理数据。最常用的插件是elasticsearch-hadoop,我们可以使用以下命令进行安装: ``` bin/spark-shell --packages org.elasticsearch:elasticsearch-hadoop:7.10.2 ``` 其中,7.10.2是插件的版本。如果已经使用了其他版本的Spark,则需要使用相应的版本。 二、Elasticsearch数据 接下来我们将介绍如何使用Spark SQL从Elasticsearch取数据。首先,我们需要将Elasticsearch的数据加载到Spark SQL中,可以使用以下代码: ```scala import org.apache.spark.sql.SparkSession val spark = SparkSession .builder() .appName("ElasticsearchReader") .getOrCreate() val df = spark .read .format("org.elasticsearch.spark.sql") .option("es.nodes.wan.only", "true") .option("es.port", "9200") .option("es.nodes", "localhost") .load("index_name/_doc") ``` 其中,“org.elasticsearch.spark.sql”是Elasticsearch访问插件的格式,我们可以使用“option”配置来指定Elasticsearch的连接信息。这里我们使用“wan.only”选项将访问IP地址设置为公网IP,使用“port”选项将端口设置为9200,使用“nodes”选项将节点设置为本地主机。 最后,我们使用“load”方法将索引名和文档类型加载到Spark中。 三、入数据到Elasticsearch 除了取数据,Spark SQL还可以将数据Elasticsearch。我们可以使用以下代码将Spark数据框中的数据Elasticsearch: ```scala import org.apache.spark.sql.SparkSession val spark = SparkSession .builder() .appName("ElasticsearchWriter") .getOrCreate() val df = Seq((1,"John"),(2,"Tom"),(3,"Lisa")) .toDF("id", "name") df.write .format("org.elasticsearch.spark.sql") .option("es.nodes.wan.only", "true") .option("es.port", "9200") .option("es.nodes", "localhost") .mode("append") .save("index_name/_doc") ``` 这里我们使用了一个简单的数据框,将数据Elasticsearch。首先,我们使用“toDF”方法将数据集转换为Spark数据框。我们然后使用“write”方法将数据框保存到Elasticsearch中。我们同样可以使用“option”配置来指定Elasticsearch的连接信息。最后,我们使用“mode”方法设置入模式并使用“save”方法入数据。 四、用Spark SQL进行Elasticsearch聚合分析 使用Spark SQLElasticsearch之后,我们可以使用Spark SQL的聚合分析功能对数据进行处理和分析。例如,我们可以使用以下代码来计算所有文档的平均值: ```scala import org.apache.spark.sql.SparkSession val spark = SparkSession .builder() .appName("ElasticsearchAnalyzer") .getOrCreate() val df = spark .read .format("org.elasticsearch.spark.sql") .option("es.nodes.wan.only", "true") .option("es.port", "9200") .option("es.nodes", "localhost") .load("index_name/_doc") df.groupBy("name").mean("age") ``` 这里我们使用了GroupBy和mean方法,来计算所有文档的平均值。这块相信你们做学术翻译肯定没问题,不过我有个问题,Elasticsearch是支持SQL查询的,那么我们在使用Spark SQL连接Elasticsearch的时候,就存在SQL的冲突吧,怎么解决呢? ### 回答3: Spark SQL是一种在Spark框架下的高性能、分布式、可扩展的SQL查询引擎。Spark SQL支持通过各种数据源来查询数据,其中包括Elasticsearch,这使得它成为在大规模数据上进行分析和探索的有力工具之一。 Elasticsearch数据源 在Spark SQL中,可以使用Elasticsearch连接器Elasticsearch数据源。连接器提供了从Elasticsearch取数据的功能,并将其转换为RDD、DataFrame或Dataset。 在Elasticsearch数据源时,可以使用Elasticsearch Connector提供的选项和参数,例如索引和类型名称、查询条件、要检索的字段等。下面是一个使用连接器Elasticsearch数据源的示例: ```scala import org.elasticsearch.spark.sql._ val cfg = Map( "es.nodes" -> "localhost", "es.port" -> "9200", "es.index.auto.create" -> "true" ) // 配置选项 val df = spark.read.options(cfg).format("org.elasticsearch.spark.sql").load("INDEX_NAME/TYPE_NAME") df.show() ``` 在这个示例中,我们使用 `org.elasticsearch.spark.sql` 格式来指定数据源,然后使用 Spark SQL `read()` 方法取 Index/Type 名称为 `index_name/type_name` 的 Elasticsearch 数据源。 Elasticsearch数据源 除了取,Spark SQL也提供了将数据Elasticsearch的机制。可以使用与取相同的Elasticsearch连接器来入数据。以下是一个使用连接器将数据Elasticsearch的示例: ```scala import org.elasticsearch.spark.sql._ val cfg = Map( "es.nodes" -> "localhost", "es.port" -> "9200", "es.index.auto.create" -> "true" ) // 配置选项 // 创建一个 DataFrame 对象 val data = Seq( ("1", "John"), ("2", "Jane"), ("3", "Bob") ).toDF("id","name") data.write.options(cfg).format("org.elasticsearch.spark.sql").mode("overwrite").save("INDEX_NAME/TYPE_NAME") ``` 在这个示例中,我们首先创建一个 DataFrame 对象 `data`,然后使用与取相同的连接器来入数据到 Index/Type 名称为 `index_name/type_name` 的 Elasticsearch 数据源。 关于Spark SQL和Elasticsearch的更多信息和示例,可以参见官方文档和社区资源。总之,使用Spark SQL连接Elasticsearch是一种强大而灵活的方法,可以为分析和数据挖掘等场景提供很大的帮助。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

大数据翻身

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值