Spark_08

SparkSql案例

SparkSQL实现最受欢迎的老师
package day8
import java.net.URL
import org.apache.spark.sql.{DataFrame, Dataset, SparkSession}

object SQLFavTeache {

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

    val spark = SparkSession.builder().appName("RowNumberDemo")
      .master("local[4]")
      .getOrCreate()

    val lines: Dataset[String] = spark.read.textFile(args(0))

    import spark.implicits._
    val df: DataFrame = lines.map(line => {
      val tIndex = line.lastIndexOf("/") + 1
      val teacher = line.substring(tIndex)
      val host = new URL(line).getHost
      val sIndex = host.indexOf(".")
      val subject = host.substring(0, sIndex)
      (subject, teacher)
    }).toDF("subject", "teacher")

    df.createTempView("v_sub_teacher")

    //该学科下的老师的访问次数
    val temp1: DataFrame = spark.sql("SELECT subject, teacher, count(*) counts FROM v_sub_teacher GROUP BY subject, teacher")

    //求每个学科下最受欢迎的老师的topn
    temp1.createTempView("v_temp_sub_teacher_counts")

    //所有学科按照出现的次数排序
    //val temp2 = spark.sql("SELECT subject, teacher, counts, row_number() over(order by counts desc) rk FROM v_temp_sub_teacher_counts")

    //学课相同的老师,进行局部排序,同时进行全局配许
    //val temp2 = spark.sql("SELECT subject, teacher, counts, row_number() over(partition by subject order by counts desc) sub_rk, row_number() over(order by counts desc) g_rk FROM v_temp_sub_teacher_counts")

    val topN = args(1)
    //前n名老师
    //做全局排序可能某些序列号会被删除掉
    //val temp2 = spark.sql(s"SELECT *  FROM (SELECT subject, teacher, counts, row_number() over(partition by subject order by counts desc) sub_rk, row_number() over(order by counts desc) g_rk FROM v_temp_sub_teacher_counts) WHERE sub_rk <= $topN")
    
    //val temp2 = spark.sql(s"SELECT *  FROM (SELECT subject, teacher, counts, rank() over(partition by subject order by counts desc) sub_rk, rank() over(order by counts desc) g_rk FROM v_temp_sub_teacher_counts) WHERE sub_rk <= $topN")

    //将入选的老师进行全局排序
    
    //val temp2 = spark.sql(s"SELECT *, dense_rank() over(order by counts desc) g_rk  FROM (SELECT subject, teacher, counts, rank() over(partition by subject order by counts desc) sub_rk FROM v_temp_sub_teacher_counts) WHERE sub_rk <= $topN")
    val temp2 = spark.sql(s"SELECT *, row_number() over(order by counts desc) g_rk  FROM (SELECT subject, teacher, counts, rank() over(partition by subject order by counts desc) sub_rk FROM v_temp_sub_teacher_counts) WHERE sub_rk <= $topN")

    temp2.show()
    spark.stop()
  }
}

Spark SQL的Join

broadcast join

适用于小表与大表的join,其中小表小于10M,本质上是去用空间换时间,
也就是将小表发送到每个大表对应的节点上,然后依次去遍历大表中的每个关联键去和对应小表中的关联键去匹配,找到相等的进行关联,并不会走shuffle过程,因此最终的文件数取决于block的个数,每个bolck是一个逻辑上的概念,
生成的规则是在建表并落HDFS文件的时候按256M去拆分,得到最终的文件数
在这里插入图片描述

shuffle hash join示意图

适合于没有特别小的两个表进行关联的时候,默认设置的shuffle partition的个数
是200,也就是分了200个区,然后两张表的key值分别去基于200做hash取余然后散步在每个区域中了,这样的思想先把相近的合并在一个区内,再在每个分区内去做比较key值的等值比较,就避免了大范围的遍历比较,节省了时间和内存。
在这里插入图片描述

sort-merge-join示意图

特点:同一个范围内的数据shuffle到同一个分区。
这种适用于关联的两张表都特别大时,使用上述的两种方法加载到内存的时候对于内存的压力都非常大时,因此在2方法的基础上,hash取余之后还要分别对两张表的key值进行排序,这样去做等值比较的时候就不需要将某一方的全部数据都加载到内存进行计算了,只需要取一部分就能知道是否有相等的(比如按升序排列,某个值明显比它大了,后面肯定就不会有相等的,就不用继续比较了,节省了时间和内存),也就是在进行等值比较的时候即用即丢的。这个方法在前面进行排序的时候可能会消耗点时间,但相对于后面的时间来说,总体是大大节省了时间。

原文链接:https://blog.csdn.net/rms1800201760/article/details/90970932
在这里插入图片描述

package day8
import org.apache.spark.sql.SparkSession
object JoinTest {

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

    val spark = SparkSession.builder().master("local[*]").appName("JoinTest").getOrCreate()

    import spark.implicits._
    val df1: DataFrame = Seq(
      (0, "playing"),
      (1, "with"),
      (2, "join")
    ).toDF("id", "token")

    val df2: DataFrame = Seq(
      (0, "P"),
      (1, "W"),
      (2, "S")
    ).toDF("aid", "atoken")

    val query: DataFrame = df1.join(df2, $"id" === $"aid")

    //查看物理计划
    query.explain()
    query.show()
    spark.stop()
  }
}

Spark shell演示

val df1 = Seq(
| (0, “peon”),
| (1, “with”),
| (2, “join”)
| ).toDF(“id”, “token”)
val df2 = Seq(
| (0, “P”),
| (1, “W”),
| (2, “S”)
| ).toDF(“aid”, “atoken”)

在这里插入图片描述

BroadcastHashJoin

默认使用BroadcastHashJoin
在这里插入图片描述
在这里插入图片描述

Spark SQL整合Hive

Hive On Spark(跟Hive没有太大的关系,就是使用了Hive的标准(HQL,元数据库,UDF,序列化,反序列化机制))
在公司中使用hive还是特别多的。

Hive原来的执行计算模型是MR,有点慢(中间结果写入到HDFS)。

Hive On Spark 使用RDD(DataFrame)运行在Spark集群上。
Hive元数据库中不包含真正要计算的数据。

1.安装MySQL(hive的元数据库)并创建一个普通用户,并且授权
CREATE USER ‘xiaoniu’@’%’ IDENTIFIED BY ‘123568’;
GRANT ALL PRIVILEGES ON hivedb.* TO ‘xiaoniu’@’%’ IDENTIFIED BY ‘123568’ WITH GRANT OPTION;
FLUSH PRIVILEGES;

#在spark的conf目录下创建一个hive的配置文件
2.添加一个hive-site.xml

<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<?xml-stylesheet type="text/xsl" href="configuration.xsl"?>
<!--
   Licensed to the Apache Software Foundation (ASF) under one or more
   contributor license agreements.  See the NOTICE file distributed with
   this work for additional information regarding copyright ownership.
   The ASF licenses this file to You under the Apache License, Version 2.0
   (the "License"); you may not use this file except in compliance with
   the License.  You may obtain a copy of the License at

       http://www.apache.org/licenses/LICENSE-2.0

   Unless required by applicable law or agreed to in writing, software
   distributed under the License is distributed on an "AS IS" BASIS,
   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   See the License for the specific language governing permissions and
   limitations under the License.
-->
<configuration>
  <property>
    <name>javax.jdo.option.ConnectionURL</name>
    <value>jdbc:mysql://node-6:3306/hivedb?createDatabaseIfNotExist=true</value>
    <description>JDBC connect string for a JDBC metastore</description>
  </property>

   <property>
    <name>javax.jdo.option.ConnectionDriverName</name>
    <value>com.mysql.jdbc.Driver</value>
    <description>Driver class name for a JDBC metastore</description>
  </property>

  <property>
    <name>javax.jdo.option.ConnectionUserName</name>
    <value>xiaoniu</value>
    <description>username to use against metastore database</description>
  </property>

  <property>
    <name>javax.jdo.option.ConnectionPassword</name>
    <value>123568</value>
    <description>password to use against metastore database</description>
  </property>

</configuration>

3.上传一个mysql连接驱动(sparkSubmit也要连接MySQL,获取元数据信息)
./spark-sql --master spark://node-4:7077,node-5:7077 --driver-class-path /home/xiaoniu/mysql-connector-java-5.1.7-bin.jar

4.sparkSQL会在mysql上创建一个database,需要手动改一下DBS表中的DB_LOCATION_UIR改成hdfs的地址
hdfs://ns1/user/hive/warehome

5.要在/etc/profile中配置一个环节变量(让sparkSQL知道hdfs在哪里,其实就是namenode在哪里)
exprot HADOOP_CONF_DIR=/appdata/hadoop/etc/hadoop/

6.重新启动SparkSQL的命令行

注意:真正要计算的数据保存在hdfs中,mysql这个元数据库保存的是hive表的描述信息,描述了有那些database,table,以及表有多少列,每一列是什么类型,还要描述表的数据保存在hdfs的什么位置。

应用

1.连接
/appdata/spark/bin/spark-sql --master spark://n1:7077 (不指定driver-class-path的前提是,spark/jars下导入mysql驱动的jar)
2.创建表
create table person(id int,name string,fv int)
> row format delimited
> fields terminated by ‘,’;

3.导入数据(数据源hdfs)
load data inpath ‘/person.txt’ into table person;
在这里插入图片描述
4.查看数据
在这里插入图片描述

Hive跟mysql的区别?

Hive是一个数据仓库(存储数据并分析数据,分析数据仓库中的数据量很大,),
MySQL是一个关系型数据库(关系型数据的增删改查(低延迟))。

hive的元数据库中保存要计算的数据吗?
不保存,保存hive仓库的表,字段,等描述信息。

hive的元数据库的功能?
建立了一种映射关系,执行HQL时,先到Mysql元数据库中查找描述信息,然后根据描述信息生成任务,然后将任务发到spark集群中执行。

spark sql整合Hive,将hive的sql写在一个文件中执行(用-f这个参数)

/appdata/spark/bin/spark-sql --master spark://node1:7077 -e “select * from t_boy order by fv desc”
/appdata/spark/bin/spark-sql --master spark://node1:7077 -f demo.sql

idea开发整合

在idea开发整合,hive需要依赖:

    <dependency>
        <groupId>org.apache.spark</groupId>
        <artifactId>spark-hive_2.11</artifactId>
        <version>2.2.0</version>
    </dependency>

//如果想让hive运行在spark上,一定要开启spark对hive的支持。
val spark = SparkSession.builder()
.appName(“HiveOnSpark”)
.master(“local[*]”)
.enableHiveSupport()//启用spark对hive的支持(可以兼容hive的句法了)
.getOrCreate()

package day8

import org.apache.spark.sql.SparkSession

object HiveOnSpark {

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

    //如果想让hive运行在spark上,一定要开启spark对hive的支持。
    val spark = SparkSession.builder()
      .appName("HiveOnSpark")
      .master("local[*]")
      .enableHiveSupport()//启用spark对hive的支持(可以兼容hive的句法了)
      .getOrCreate()

    //想要使用hive的元数据库,必须指定元数据库的位置,添加一个hive-site.xml到当前程序的classpath下即可。
   
    //有t_boy这个表或视图吗?
    val result = spark.sql("SELECT * FROM t_boy ORDER BY fv DESC")

    result.show()
    spark.stop()
  }
}

导入集群的配置文件:hdfs-site.xml,core-site.xml
在这里插入图片描述

实时计算

实时计算相关技术
Strom / JStromSpark streaming()Flink
实时性高有延迟实时性高
吞吐量较低吞吐量高吞吐量高
只能实时计算离线+实时离线+实时
算子较少算子丰富算子丰富
没有机器学习没有
没有机器学习没有
使用比较少非常火一般

一个完整的生态时非常重要的,Spark生态比较完善。

消息中间件

在这里插入图片描述
消息中间件的作用:临时存储数据,很重要。
拉取数据的有点:能有控制Spark Streaming的处理速度,不会出现大量的堆积到Spark Streaming的情况。

Kafka

简介

Kafka是由Apache软件基金会开发的一个开源流处理平台,由Scala和Java编写。Kafka是一种高吞吐量的分布式发布订阅消息系统,它可以处理消费者在网站中的所有动作流数据。 这种动作(网页浏览,搜索和其他用户的行动)是在现代网络上的许多社会功能的一个关键因素。 这些数据通常是由于吞吐量的要求而通过处理日志和日志聚合来解决。 对于像Hadoop一样的日志数据和离线分析系统,但又要求实时处理的限制,这是一个可行的解决方案。Kafka的目的是通过Hadoop的并行加载机制来统一线上和离线的消息处理,也是为了通过集群来提供实时的消息。

特征

Kafka 是一种高吞吐量 的分布式发布订阅消息系统,有如下特性:
1.通过O的磁盘数据结构提供消息的持久化,这种结构对于即使数以TB的消息存储也能够保持长时间的稳定性能。
2.高吞吐量:即使是非常普通的硬件Kafka也可以支持每秒数百万 [2] 的消息。
3.支持通过Kafka服务器和消费机集群来分区消息。
4.支持Hadoop并行数据加载。
Kafka通过官网发布了最新版本2.0.0

Kafka的一些概念
Broker:安装了Kafka服务的那台集群就是一个broker(broker的id要全局唯一)
Producer:消息的生产者,负责将数据写入broker中(push)
Consumer:消息的消费者,负责从Kafka中读取数据(pull),老版本的消费者需要依赖zk,新版本的不需要。
Topic:主题,数据的一个分类,不同的topic存放不同的数据。
Consumer Group:消费者组,一个topic可以有多个消费者同时消费,多个消费者如果在一个消费者组中,那么他们不能重复消费数据。

Spark Streaming2.2.0兼容kafka0.8.21以上的版本,主要支持0.8和0.10

Kafka0.8集群安装

1.下载Kafka安装包
2.上传安装包
3.解压
4.修改配置文件 config/server.properties
	broker.id=0
	host.name=node1
	log.dirs=/data/kafka
	zookeeper.connect=node-1:2181,node-2:2181,node-3:2181
kafka0.10不需要指定host.name默认是主机名。
5.将配置好的kafka拷贝到其他机器上
6.修改broker.id和host.name
7.启动kafka(后台线程启动)
	/bigdata/kafka_2.11-0.8.2.2/bin/kafka-server-start.sh -daemon /bigdata/kafka_2.11-0.8.2.2/config/server.properties 

	关闭:
	/bigdata/kafka_2.11-0.8.2.2/bin/kafka-server-stop.sh

#查看topic信息
/bigdata/kafka_2.11-0.8.2.2/bin/kafka-topics.sh --list --zookeeper node-1:2181,node-2:2181

#创建topic
/bigdata/kafka_2.11-0.8.2.2/bin/kafka-topics.sh --create --zookeeper node-1:2181,node-2:2181 --replication-factor 3 --partitions 3 --topic xiaoniu

#往Kafka的topic中写入数据(命令行的生产者)
/bigdata/kafka_2.11-0.8.2.2/bin/kafka-console-producer.sh --broker-list node-4:9092,node-5:9092,node-5:9092 --topic xiaoniu

#启动消费者
/bigdata/kafka_2.11-0.8.2.2/bin/kafka-console-consumer.sh --zookeeper node-1:2181,node-2:2181 --topic xiaoniu --from-beginning
kafka0.10使用

启动生产者:/appdata/kafka/bin/kafka-console-producer.sh --broker-list n1:9092,n2:9092,n3:9092 --topic userif
启动消费者:/appdata/kafka/bin/kafka-console-consumer.sh --bootstrap-server n1:9092,n2:9092 --topic userif --from-beginning

Java代码实现kafka发送和接收消息

发送代码

package cn.edu360.kafka;
import java.util.Properties;
import kafka.javaapi.producer.Producer;
import kafka.producer.KeyedMessage;
import kafka.producer.ProducerConfig;
public class ProducerDemo {

	public static void main(String[] args) {
		Properties props = new Properties();
		props.put("metadata.broker.list", "node1:9092,node2:9092,node3:9092");
		props.put("serializer.class", "kafka.serializer.StringEncoder");
		ProducerConfig config = new ProducerConfig(props);
		Producer<String, String> producer = new Producer<String, String>(config);
		for (int i = 1001; i <= 1100; i++)
			producer.send(new KeyedMessage<String, String>("xiaoniu", "xiaoniu-msg" + i));
	}
}

接收代码

package cn.edu360.kafka;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import kafka.consumer.Consumer;
import kafka.consumer.ConsumerConfig;
import kafka.consumer.KafkaStream;
import kafka.javaapi.consumer.ConsumerConnector;
import kafka.message.MessageAndMetadata;

public class ConsumerDemo {
	private static final String topic = "xiaoniu";
	private static final Integer threads = 2;//一个消费者可以有多少个线程同时消费

	public static void main(String[] args) {
		
		Properties props = new Properties();
		props.put("zookeeper.connect", "node1:2181,node2:2181,node3:2181");
		props.put("group.id", "vvvvv");
		//smallest重最开始消费,largest代表重消费者启动后产生的数据才消费
		//--from-beginning
		props.put("auto.offset.reset", "smallest");

		ConsumerConfig config = new ConsumerConfig(props);
		ConsumerConnector consumer =Consumer.createJavaConsumerConnector(config);
		Map<String, Integer> topicCountMap = new HashMap<String, Integer>();
		topicCountMap.put(topic, threads);
		Map<String, List<KafkaStream<byte[], byte[]>>> consumerMap = consumer.createMessageStreams(topicCountMap);
		List<KafkaStream<byte[], byte[]>> streams = consumerMap.get(topic);
		
		for(final KafkaStream<byte[], byte[]> kafkaStream : streams){
			new Thread(new Runnable() {
				public void run() {
					for(MessageAndMetadata<byte[], byte[]> mm : kafkaStream){
						String msg = new String(mm.message());
						System.out.println(msg);
					}
				}
			}).start();
		}
	}
}
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值