第107课: Spark Streaming电商广告点击综合案例底层数据层的建模和编码实现(基于MySQL)

有兴趣想学习国内整套Spark+Spark Streaming+Machine learning顶级课程的,可加我qq  471186150。共享视频,性价比超高!

1:传统的关系型数据库都是伪分布式,在100个使用了底层数据持久化层的案例中,至少有90个底层都是基于数据库的。当然可以选择基于Hbase或者Redis,但是实际上大多数人底层都会基于数据库。因为1,数据库可以非常高效的读写,尤其频繁的批量的插入,数据库特别适合。2,传统的it系统大多数都是基于数据库,所以spark处理的结果,如果仍给了数据库,传统的javaee系统例如spring,这些系统可以直接基于数据库的驱动,或者三方框架去操作数据库中的数据。而数据库中的i数据是spark计算后的结果,就可以操作这些数据绘制趋势图等。如股票一直变化的趋势图,用spark+数据库+javaee绘制就特别适合。如每隔十秒更新一次,图是一直在动的。这就是为什么很多人都使用数据库来做大数据系统计算后的结果。

package com.dt.streaming;

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.LinkedBlockingQueue;

import org.apache.spark.SparkConf;
import org.apache.spark.api.java.JavaPairRDD;
import org.apache.spark.api.java.JavaRDD;
import org.apache.spark.api.java.function.Function;
import org.apache.spark.api.java.function.Function2;
import org.apache.spark.api.java.function.PairFunction;
import org.apache.spark.api.java.function.VoidFunction;
import org.apache.spark.streaming.Durations;
import org.apache.spark.streaming.api.java.JavaDStream;
import org.apache.spark.streaming.api.java.JavaPairDStream;
import org.apache.spark.streaming.api.java.JavaPairInputDStream;
import org.apache.spark.streaming.api.java.JavaStreamingContext;
import org.apache.spark.streaming.kafka.KafkaUtils;

import kafka.serializer.StringDecoder;
import scala.Tuple2;
/**
 * 
 * 第107讲:在线处理广告点击流
 * 广告点击的基本数据格式:timestamp、ip、userID、adID、province、city
 * 
 * @author hp
 *
 */
public class AdClickedStreamingStats {

   public static void main(String[] args) {
      
      /*
       * 第一步:配置SparkConf:
       * 1,至少2条线程:因为Spark Streaming应用程序在运行的时候,至少有一条
       * 线程用于不断的循环接收数据,并且至少有一条线程用于处理接受的数据(否则的话无法
       * 有线程用于处理数据,随着时间的推移,内存和磁盘都会不堪重负);
       * 2,对于集群而言,每个Executor一般肯定不止一个Thread,那对于处理Spark Streaming的
       * 应用程序而言,每个Executor一般分配多少Core比较合适?根据我们过去的经验,5个左右的
       * Core是最佳的(一个段子分配为奇数个Core表现最佳,例如3个、5个、7个Core等);
       */
      SparkConf conf = new SparkConf().setMaster("local[5]").
            setAppName("AdClickedStreamingStats");
      
      /*SparkConf conf = new SparkConf().setMaster("spark://Master:7077").
            setAppName("SparkStreamingOnKafkaReceiver");*/
      
      /*
       * 第二步:创建SparkStreamingContext:
       * 1,这个是SparkStreaming应用程序所有功能的起始点和程序调度的核心
       * SparkStreamingContext的构建可以基于SparkConf参数,也可基于持久化的SparkStreamingContext的内容
       * 来恢复过来(典型的场景是Driver崩溃后重新启动,由于Spark Streaming具有连续7*24小时不间断运行的特征,
       * 所有需要在Driver重新启动后继续上衣系的状态,此时的状态恢复需要基于曾经的Checkpoint);
       * 2,在一个Spark Streaming应用程序中可以创建若干个SparkStreamingContext对象,使用下一个SparkStreamingContext
       * 之前需要把前面正在运行的SparkStreamingContext对象关闭掉,由此,我们获得一个重大的启发SparkStreaming框架也只是
       * Spark Core上的一个应用程序而已,只不过Spark Streaming框架箱运行的话需要Spark工程师写业务逻辑处理代码;
       */
      JavaStreamingContext jsc = new JavaStreamingContext(conf, Durations.seconds(10));
      
      /*
       * 第三步:创建Spark Streaming输入数据来源input Stream:
       * 1,数据输入来源可以基于File、HDFS、Flume、Kafka、Socket等
       * 2, 在这里我们指定数据来源于网络Socket端口,Spark Streaming连接上该端口并在运行的时候一直监听该端口
       *        的数据(当然该端口服务首先必须存在),并且在后续会根据业务需要不断的有数据产生(当然对于Spark Streaming
       *        应用程序的运行而言,有无数据其处理流程都是一样的); 
       * 3,如果经常在每间隔5秒钟没有数据的话不断的启动空的Job其实是会造成调度资源的浪费,因为并没有数据需要发生计算,所以
       *        实例的企业级生成环境的代码在具体提交Job前会判断是否有数据,如果没有的话就不再提交Job;
       * 4,在本案例中具体参数含义:
       *        第一个参数是StreamingContext实例;
       *        第二个参数是ZooKeeper集群信息(接受Kafka数据的时候会从ZooKeeper中获得Offset等元数据信息)
       *        第三个参数是Consumer Group
       *        第四个参数是消费的Topic以及并发读取Topic中Partition的线程数
       */


      
      /**
       * 创建Kafka元数据,来让Spark Streaming这个Kafka Consumer利用
       */
      Map<String, String> kafkaParameters = new HashMap<String, String>();
      kafkaParameters.put("metadata.broker.list", 
            "Master:9092,Worker1:9092,Worker2:9092");
      
      Set<String> topics =  new HashSet<String>();
      topics.add("AdClicked");
      
      JavaPairInputDStream<String, String> adClickedStreaming = KafkaUtils.createDirectStream(jsc, 
            String.class, String.class, 
            StringDecoder.class, StringDecoder.class,
            kafkaParameters, 
            topics);
      /*
       * 第四步:接下来就像对于RDD编程一样基于DStream进行编程!!!原因是DStream是RDD产生的模板(或者说类),在Spark Streaming具体
       * 发生计算前,其实质是把每个Batch的DStream的操作翻译成为对RDD的操作!!!
       *对初始的DStream进行Transformation级别的处理,例如map、filter等高阶函数等的编程,来进行具体的数据计算
        *     广告点击的基本数据格式:timestamp、ip、userID、adID、province、city
        */
   
      
      JavaPairDStream<String, Long> pairs = adClickedStreaming.mapToPair(new PairFunction<Tuple2<String,String>, String, Long>() {

         @Override
         public Tuple2<String, Long> call(Tuple2<String, String> t) throws Exception {
            String[] splited = t._2.split("\t");
            
            String timestamp = splited[0]; //yyyy-MM-dd
            String ip = splited[1];
            String userID = splited[2];
            String adID = splited[3];
            String province = splited[4];
            String city = splited[5];
            
            String clickedRecord = timestamp + "_" + ip + "_" + userID + "_" + adID + "_" 
                  + province + "_" + city;
            
            return new Tuple2<String, Long>(clickedRecord, 1L);
         }
      });
      
       /*
          * 第四步:对初始的DStream进行Transformation级别的处理,例如map、filter等高阶函数等的编程,来进行具体的数据计算
          *   计算每个Batch Duration中每个User的广告点击量
          */
      JavaPairDStream<String, Long> adClickedUsers = pairs.reduceByKey(new Function2<Long, Long, Long>(){

         @Override
         public Long call(Long v1, Long v2) throws Exception {
            // TODO Auto-generated method stub
            return v1 + v2;
         }
            
      });
      
      
      /**
       * 
       * 计算出什么叫有效的点击?
       * 1,复杂化的一般都是采用机器学习训练好模型直接在线进行过滤;
       * 2,简单的?可以通过一个Batch Duration中的点击次数来判断是不是非法广告点击,但是实际上讲非法广告
       * 点击程序会尽可能模拟真实的广告点击行为,所以通过一个Batch来判断是 不完整的,我们需要对例如一天(也可以是每一个小时)
       * 的数据进行判断!
       * 3,比在线机器学习退而求次的做法如下:
       *        例如:一段时间内,同一个IP(MAC地址)有多个用户的帐号访问;
       *        例如:可以统一一天内一个用户点击广告的次数,如果一天点击同样的广告操作50次的话,就列入黑名单;
       * 
       * 黑名单有一个重点的特征:动态生成!!!所以每一个Batch Duration都要考虑是否有新的黑名单加入,此时黑名单需要存储起来
       * 具体存储在什么地方呢,存储在DB/Redis中即可;
       * 
       * 例如邮件系统中的“黑名单”,可以采用Spark Streaming不断的监控每个用户的操作,如果用户发送邮件的频率超过了设定的值,可以
       * 暂时把用户列入“黑名单”,从而阻止用户过度频繁的发送邮件。
       */
       
      JavaPairDStream<String, Long>  filteredClickInBatch = adClickedUsers.filter(new Function<Tuple2<String,Long>, Boolean>() {
         
         @Override
         public Boolean call(Tuple2<String, Long> v1) throws Exception {
            if ( 1 < v1._2){
               //更新一下黑名单的数据表
               return false;
            } else {
               return true;
            }
            
         }
      });
      
      // Todo。。。。
      
      /*
       * 此处的print并不会直接出发Job的执行,因为现在的一切都是在Spark Streaming框架的控制之下的,对于Spark Streaming
       * 而言具体是否触发真正的Job运行是基于设置的Duration时间间隔的
       * 
       * 诸位一定要注意的是Spark Streaming应用程序要想执行具体的Job,对Dtream就必须有output Stream操作,
       * output Stream有很多类型的函数触发,类print、saveAsTextFile、saveAsHadoopFiles等,最为重要的一个
       * 方法是foraeachRDD,因为Spark Streaming处理的结果一般都会放在Redis、DB、DashBoard等上面,foreachRDD
       * 主要就是用用来完成这些功能的,而且可以随意的自定义具体数据到底放在哪里!!!
       *
       */
//    filteredClickInBatch.print();
      
      filteredClickInBatch.foreachRDD(new Function<JavaPairRDD<String,Long>, Void>() {

         @Override
         public Void call(JavaPairRDD<String, Long> rdd) throws Exception {
            rdd.foreachPartition(new VoidFunction<Iterator<Tuple2<String,Long>>>() {
               
               @Override
               public void call(Iterator<Tuple2<String, Long>> partition) throws Exception {
                  /**
                   * 在这里我们使用数据库连接池的高效读写数据库的方式把数据写入数据库MySQL;
                   * 由于传入的参数是一个Iterator类型的集合,所以为了更加高效的操作我们需要批量处理
                   * 例如说一次性插入1000条Record,使用insertBatch或者updateBatch类型的操作;
                   * 插入的用户信息可以只包含:useID、adID、clickedCount、time
                   * 这里面有一个问题:可能出现两条记录的Key是一样的,此时就需要更新累加操作
                   */
               }
            });
            return null;
         }
      });
      
      
      JavaPairDStream<String, Long> blackListBasedOnHistory = filteredClickInBatch.filter(new Function<Tuple2<String,Long>, Boolean>() {

         @Override
         public Boolean call(Tuple2<String, Long> v1) throws Exception {
            //广告点击的基本数据格式:timestamp、ip、userID、adID、province、city
            String[] splited = v1._1.split("\t");
            
            String date = splited[0];
            String userID = splited[2];
            String adID = splited[3];
            
            /**
             * 接下来根据date、userID、adID等条件去查询用户点击广告的数据表,获得总的点击次数
             * 这个时候基于点击次数判断是否属于黑名单点击             * 
             */
            
            int clickedCountTotalToday = 81;
                  
            if (clickedCountTotalToday > 50)
            {
               return true;
            } else {
               return false;
            }
                     
         }
      });
      
      
      /**
       * 必须对黑名单的整个RDD进行去重操作!!!
       */
      
      
      JavaDStream<String> blackListuserIDtBasedOnHistory = blackListBasedOnHistory.map(new Function<Tuple2<String,Long>, String>() {

         @Override
         public String call(Tuple2<String, Long> v1) throws Exception {
            // TODO Auto-generated method stub
            return v1._1.split("\t")[2];
         }
      });
      
      JavaDStream<String> blackListUniqueuserIDtBasedOnHistory = blackListuserIDtBasedOnHistory.transform(new Function<JavaRDD<String>, JavaRDD<String>>() {

         @Override
         public JavaRDD<String> call(JavaRDD<String> rdd) throws Exception {
            // TODO Auto-generated method stub
            return rdd.distinct();
         }
      });
      
      
      
      //下一步写入黑名单数据表中
      
      blackListUniqueuserIDtBasedOnHistory.foreachRDD(new Function<JavaRDD<String>, Void>() {

         @Override
         public Void call(JavaRDD<String> rdd) throws Exception {
            rdd.foreachPartition(new VoidFunction<Iterator<String>>() {
               
               @Override
               public void call(Iterator<String> t) throws Exception {
                  /**
                   * 在这里我们使用数据库连接池的高效读写数据库的方式把数据写入数据库MySQL;
                   * 由于传入的参数是一个Iterator类型的集合,所以为了更加高效的操作我们需要批量处理
                   * 例如说一次性插入1000条Record,使用insertBatch或者updateBatch类型的操作;
                   * 插入的用户信息可以只包含:useID
                   * 此时直接插入黑名单数据表即可。
                   */
               }
            });
            return null;
         }
      });
      
      /*
       * Spark Streaming执行引擎也就是Driver开始运行,Driver启动的时候是位于一条新的线程中的,当然其内部有消息循环体,用于
       * 接受应用程序本身或者Executor中的消息;
       */
      jsc.start();
      
      jsc.awaitTermination();
      jsc.close();

   }

}

class JDBCWrapper {

   private static JDBCWrapper jdbcInstance = null;
   private static LinkedBlockingQueue<Connection> dbConnectionPool = new LinkedBlockingQueue<Connection> ();
   //底层访问mysql,首先通过静态代码块来加载驱动,这里先硬编码,实际肯定结合架构设计做配置字符串
   static {
      try {
         Class.forName("com.mysql.jdbc.Driver");
      } catch (ClassNotFoundException e) {
         // TODO Auto-generated catch block
         e.printStackTrace();
      }
   }
   
   //有了驱动,获取数据库的句柄,基于句柄做事情,下面是个单例
   public static JDBCWrapper getJDBCInstance(){
      if (jdbcInstance == null){
         //同时三个线程来请求,如果不用同步synchronized,就会3个位null,弄出3个实例。
         synchronized(JDBCWrapper.class){
            if (jdbcInstance == null){
               jdbcInstance = new JDBCWrapper();
            }           
         }
         
      }
      
      return jdbcInstance;
   }
   
   private JDBCWrapper(){
      
      for (int i = 0; i < 10; i++){
            
         //这里创建10个connection
         try {
            Connection conn = DriverManager.getConnection("jdbc:mysql://Master:3306/sparkstreaming","root","root");
            dbConnectionPool.put(conn);//数据库更高效,使用连接池
         } catch (Exception e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
         }
            
      }
         
   }
   
   //从池子中拿conn,考虑线程问题,加synchronized,让一个线程获得一个实例
   public synchronized Connection getConnection(){
      while (0 == dbConnectionPool.size()){//池子里面可能没东西,就死循环,确保下面能从池子里拿到实例
         try {
            Thread.sleep(20);
         } catch (InterruptedException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
         }
      }
      
      return dbConnectionPool.poll();//拿到实例后,拿实例进行操作
   }
   //拿到实例,做查询,插入,更新等操作,而且是批量处理,不是一条条处理。第1个参数是你的sql语句,第2个参数是一批参数,因为批量,返回数组
   public int[] doBatch(String sqlText, List<Object[]> paramsList, ExecuteCallBack callBack) {
      
      Connection conn = getConnection();
      PreparedStatement preparedStatement = null;
      int[] result = null;
      try {
         conn.setAutoCommit(false);//设置批量操作不能自动提交
         preparedStatement = conn.prepareStatement(sqlText);//写入statement,传入sql
         
         for (Object[] parameters : paramsList){
            for(int i = 0; i < parameters.length; i++){
               preparedStatement.setObject(i+1, parameters[i]);//这里索引必须加1
            }
            
            preparedStatement.addBatch();
         }
         
         result = preparedStatement.executeBatch();//针对处理的结果,一般会有回调函数,要写个接口
               
         
         conn.commit();//这里真正执行
         
      } catch (Exception e) {
         // TODO Auto-generated catch block
         e.printStackTrace();
      } finally {
         if (preparedStatement != null){
            try {
               preparedStatement.close();//要关闭prepareStatement
            } catch (SQLException e) {
               // TODO Auto-generated catch block
               e.printStackTrace();
            }
         }
         
         if (conn != null){
            try {
               dbConnectionPool.put(conn);//弄过之后,把conn放回池子中
            } catch (InterruptedException e) {
               // TODO Auto-generated catch block
               e.printStackTrace();
            }
         }
      }
      
      
      
      
      return result;
   }
   
   
public void doQuery(String sqlText, Object[] paramsList, ExecuteCallBack callBack) {
      
      Connection conn = getConnection();
      PreparedStatement preparedStatement = null;
      ResultSet result = null;
      try {
         
         preparedStatement = conn.prepareStatement(sqlText);
         
         
            for(int i = 0; i < paramsList.length; i++){
               preparedStatement.setObject(i+1, paramsList[i]);
            }
            
         
         
         result = preparedStatement.executeQuery();
               
         callBack.resultCallBack(result);
         
         
      } catch (Exception e) {
         // TODO Auto-generated catch block
         e.printStackTrace();
      } finally {
         if (preparedStatement != null){
            try {
               preparedStatement.close();
            } catch (SQLException e) {
               // TODO Auto-generated catch block
               e.printStackTrace();
            }
         }
         
         if (conn != null){
            try {
               dbConnectionPool.put(conn);
            } catch (InterruptedException e) {
               // TODO Auto-generated catch block
               e.printStackTrace();
            }
         }
      }
         
   }
}
//针对处理的结果,一般会有回调函数,要写个接口,调用回调函数,把result传过去
interface ExecuteCallBack {
   void resultCallBack(ResultSet result) throws Exception;
}



  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: 要将Spark Streaming中的数据更新到MySQL,可以使用以下步骤: 1. 在Spark Streaming中创建一个DStream,该DStream包含要更新到MySQL数据。 2. 使用foreachRDD函数将DStream转换为RDD,并在RDD上执行更新操作。 3. 在更新操作中,使用JDBC连接到MySQL数据库,并将数据插入到MySQL表中。 以下是一个示例代码,可以将Spark Streaming中的数据更新到MySQL: ```python from pyspark.streaming import StreamingContext from pyspark import SparkContext import mysql.connector # 创建SparkContext和StreamingContext sc = SparkContext(appName="SparkStreamingUpdateMySQL") ssc = StreamingContext(sc, 1) # 创建一个DStream,包含要更新到MySQL数据 lines = ssc.socketTextStream("localhost", 9999) # 将DStream转换为RDD,并在RDD上执行更新操作 def updateMySQL(rdd): if not rdd.isEmpty(): # 创建MySQL连接 cnx = mysql.connector.connect(user='root', password='password', host='localhost', database='test') cursor = cnx.cursor() # 更新MySQL表 for row in rdd.collect(): query = "UPDATE mytable SET value = %s WHERE id = %s" cursor.execute(query, (row[1], row[0])) # 提交更改并关闭连接 cnx.commit() cursor.close() cnx.close() # 应用更新操作到DStream lines.foreachRDD(updateMySQL) # 启动StreamingContext ssc.start() ssc.awaitTermination() ``` 在上面的代码中,我们首先创建了一个DStream,该DStream包含要更新到MySQL数据。然后,我们使用foreachRDD函数将DStream转换为RDD,并在RDD上执行更新操作。在更新操作中,我们使用JDBC连接到MySQL数据库,并将数据插入到MySQL表中。最后,我们将更新操作应用到DStream中,并启动StreamingContext。 请注意,在实际应用中,您需要根据自己的需求修改代码中的数据库连接信息和更新操作。 ### 回答2: 对于 Spark Streaming 应用程序来说,将更新的数据写入 MySQL 数据库是非常常见的需求,本文将介绍如何通过 Spark Streaming 在实时应用程序中将更新的数据写入到 MySQL 数据库中。 首先,让我们考虑如何连接 MySQL 数据库。在 Scala 中,我们可以使用 JDBC 连接 MySQL 数据库。需要注意的是,在批处理应用程序中,我们可以使用单个连接来处理一批数据,而在 Spark Streaming 应用程序中,我们需要在每个批次中使用一个新连接。这可以通过在 foreachRDD() 方法中为每个 RDD 创建新的连接来实现。以下是一个使用 Scala 连接 MySQL 数据库的示例代码: ``` import java.sql.{Connection, DriverManager, ResultSet} // Define the MySQL connection parameters val url = "jdbc:mysql://localhost/mydatabase" val driver = "com.mysql.jdbc.Driver" val username = "root" val password = "mypassword" // Define a function to create a new MySQL connection def createConnection(): Connection = { Class.forName(driver) DriverManager.getConnection(url, username, password) } // Define a function to execute a SQL statement on a MySQL connection def executeQuery(connection: Connection, sql: String): ResultSet = { val statement = connection.createStatement() statement.executeQuery(sql) } // Define a function to insert data into a MySQL table def insertData(connection: Connection, data: String): Unit = { val statement = connection.createStatement() statement.executeUpdate(s"insert into mytable values('$data')") statement.close() } ``` 接下来,让我们考虑如何将 Spark Streaming 输入 DStream 中的更新数据写入 MySQL 数据库。对于此操作,我们需要执行以下步骤: 1. 对于每个 RDD,创建一个新的 MySQL 数据库连接。 2. 对于 RDD 中的每个更新数据元素,执行插入操作。 3. 在完成 RDD 处理后,关闭 MySQL 数据库连接。 以下是一个使用 Scala 将 Spark Streaming 输入 DStream 中的数据插入 MySQL 数据库的示例代码: ``` // Define a function to handle each RDD def saveToMySQL(rdd: RDD[String]): Unit = { rdd.foreachPartition { partitionOfRecords => // Create a new MySQL connection val connection = createConnection() partitionOfRecords.foreach { record => // Insert the record into the MySQL table insertData(connection, record) } // Close the MySQL connection connection.close() } } // Create a Spark Streaming context val ssc = new StreamingContext(sparkConf, batchDuration) // Create a DStream from a Kafka topic val messages = KafkaUtils.createDirectStream[String, String]( ssc, LocationStrategies.PreferConsistent, ConsumerStrategies.Subscribe[String, String](topics, kafkaParams) ) // Extract the data from the DStream val data = messages.map(_.value()) // Save the data to MySQL data.foreachRDD { rdd => saveToMySQL(rdd) } // Start the Spark Streaming context ssc.start() ssc.awaitTermination() ``` 在上面的示例代码中,我们使用 foreachPartition() 方法为每个分区创建一个新的 MySQL 连接。由于这个过程是在本地执行的,因此没有任何网络开销。我们之后在新分区上运行相同的操作,并在处理完成后关闭连接。此外,我们可以在 saveToMySQL() 方法中使用一个 Try...Catch 块来处理连接中的任何异常。这些异常可能包括连接错误,插入重复值或插入空值等。 综上,我们可以使用上述步骤来将 Spark Streaming 输入 DStream 中的更新数据写入到 MySQL 数据库中。这也是一个通用的模式,可以用于将 Spark Streaming 数据写入其他类型的数据库或 NoSQL 存储中。需要注意的是,在处理大量数据时,我们需要考虑并行连接的性能问题,以避免出现资源瓶颈和连接池饥饿等问题。 ### 回答3: Spark StreamingSpark生态系统的一个组件,它提供了实时数据处理功能。在进行实时数据处理过程中,经常需要把结果写入数据库中,MongoDB、MySQL这些数据库管理系统具有易于扩展的功能,可以应对大规模实时数据处理的需求。那么在Spark Streaming中如何更新(Update)数据MySQL呢? 首先需要使用Spark JDBC驱动程序。Spark默认支持PostgreSQL和MySQL数据库。如果要使用其他数据库,需要手动下载JDBC驱动程序,然后通过“--jars”选项将其添加到应用程序的类路径。 其次需要定义MySQL的连接参数,如数据库的URL、用户名和密码等。在代码中可以使用Properties类存储连接参数。示例代码如下: val jdbcUsername ="root" val jdbcPassword ="123456” val jdbcHostname ="localhost” val jdbcPort ="3306" val jdbcDatabase ="test" val jdbcUrl =s"jdbc:mysql://$jdbcHostname:$jdbcPort/$jdbcDatabase" val connectionProperties =newProperties() connectionProperties.put("user",jdbcUsername) connectionProperties.put("password",jdbcPassword) 接下来,需要使用foreachRDD API编写将Spark Streaming中的结果更新到MySQL表中的代码。示例代码如下: processedStream.foreachRDD { rdd => //将结果保存到MySQL表“result”中 rdd.foreachPartition { partition => valconnection =DriverManager.getConnection(jdbcUrl,jdbcUsername,jdbcPassword) connection.setAutoCommit(false) valstatement =connection.prepareStatement( "UPDATEresultSETvalue=? WHEREkey=?") partition.foreach { case (key,value) => statement.setDouble(1,value) //设置value statement.setString(2,key) //设置key statement.executeUpdate() } connection.commit() connection.close() } } 在这段代码中,首先使用foreachRDD API遍历DStream中的每个RDD。然后使用foreachPartition API对每个分区内部的数据进行处理。因为MySQL的连接是非常昂贵的,所以将它们用时间和资源最少的方式传递给分区,这样可以减少连接的数量。 在foreachPartition内部,首先使用DriverManager.getConnection方法创建MySQL连接。如果连接成功,将其设置为手动提交模式,然后使用connection.prepareStatement方法创建statement对象。该对象是用于构建动态SQL语句的。在该语句中使用“?”来占位符,以便稍后填充。在Partion对象中,将从DStream中获取到的每个key-value对设置到statement中,然后执行statement.executeUpdate()方法来提交更改。最后,对于连接对象,使用connection.commit()方法提交所有更改,并使用connection.close()方法关闭连接对象。 因此,在Spark Streaming中更新数据MySQL是相对简单的。只需要使用Spark JDBC驱动程序、定义MySQL的连接参数、并使用foreachRDD API将结果更新到MySQL表中即可。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值