object Collection
{
def main(args: Array[String]): Unit = {
val env: ExecutionEnvironment = ExecutionEnvironment.getExecutionEnvironment
val data: DataSet[String] = env.readTextFile("hdfs://zhen:8020/data/wordcount.txt")
data.print()
}
}
1,语文
2,数学
3,英语
4,物理
5,化学
6,生物
object Collection
{
def main(args: Array[String]): Unit = {
val env: ExecutionEnvironment = ExecutionEnvironment.getExecutionEnvironment
case class subject(id:Long,name:String)
val value: DataSet[subject] = env.readCsvFile[subject]("data/subject.csv")
value.print()
}
}
flink支持对一个文件目录内的所有文件,包括所有子目录中的所有文件的遍历访问方式。
对于从文件中读取数据,当读取的数个文件夹的时候,嵌套的文件默认是不会被读取的,只会读取第一个文件,其他的都会被忽略。所以我们需要使用recursive.file.enumeration进行递归读取
/**
* 遍历目录的批次数据
*/
object BatchFromFolder {
def main(args: Array[String]): Unit = {
//初始化环境
val env = ExecutionEnvironment.getExecutionEnvironment
val parameters = new Configuration
// recursive.file.enumeration 开启递归
parameters.setBoolean("recursive.file.enumeration", true)
val result = env.readTextFile("D:\\data\\dataN").withParameters(parameters)
//触发程序执行
result.print()
}
}
Transformation | 说明 |
---|---|
map | 将DataSet中的每一个元素转换为另外一个元素 |
flatMap | 将DataSet中的每一个元素转换为0…n个元素 |
mapPartition | 将一个分区中的元素转换为另一个元素 |
filter | 过滤出来一些符合条件的元素 |
reduce | 可以对一个dataset或者一个group来进行聚合计算,最终聚合成一个元素 |
reduceGroup | 将一个dataset或者一个group聚合成一个或多个元素 |
aggregate | 按照内置的方式来进行聚合。例如:SUM/MIN/MAX… |
distinct | 去重 |
join | 将两个DataSet按照一定条件连接到一起,形成新的DataSet |
union | 将两个DataSet取并集,并不会去重 |
rebalance | 让每个分区的数据均匀分布,避免数据倾斜 |
partitionByHash | 按照指定的key进行hash分区 |
sortPartition | 指定字段对分区中的数据进行排序 |
将DataSet中的每一个元素转换为另外一个元素
object Collection
{
def main(args: Array[String]): Unit = {
case class User(id:String, name:String)
val env = ExecutionEnvironment.getExecutionEnvironment
val textDataSet: DataSet[String] = env.fromCollection(
List("1,张三", "2,李四", "3,王五", "4,赵六")
)
val userDataSet: DataSet[User] = textDataSet.map {
text =>
val fieldArr = text.split(",")
User(fieldArr(0), fieldArr(1))
}
userDataSet.print()
}
}
张三,中国,江西省,南昌市
李四,中国,河北省,石家庄市
Tom,America,NewYork,Manhattan
// 1. 构建批处理运行环境
val env = ExecutionEnvironment.getExecutionEnvironment
// 2. 构建本地集合数据源
val userDataSet = env.fromCollection(List(
"张三,中国,江西省,南昌市",
"李四,中国,河北省,石家庄市",
"Tom,America,NewYork,Manhattan"
))
// 3. 使用`flatMap`将一条数据转换为三条数据
val resultDataSet = userDataSet.flatMap{
text =>
// - 使用逗号分隔字段
val fieldArr = text.split(",")
// - 分别构建国家、国家省份、国家省份城市三个元组
List(
(fieldArr(0), fieldArr(1)), // 构建国家维度数据
(fieldArr(0), fieldArr(1) + fieldArr(2)), // 构建省份维度数据
(fieldArr(0), fieldArr(1) + fieldArr(2) + fieldArr(3)) // 构建城市维度数据
)
}
// 4. 打印输出
resultDataSet.print()
将一个分区
中的元素转换为另一个元素
// 3. 创建一个`User`样例类
case class User(id:String, name:String)
def main(args: Array[String]): Unit = {
// 1. 获取`ExecutionEnvironment`运行环境
val env = ExecutionEnvironment.getExecutionEnvironment
// 2. 使用`fromCollection`构建数据源
val userDataSet = env.fromCollection(List("1,张三", "2,李四", "3,王五", "4,赵六"))
// 4. 使用`mapPartition`操作执行转换
val resultDataSet = userDataSet.mapPartition{
iter =>
// TODO:打开连接
// 对迭代器执行转换操作
iter.map{
ele =>
val fieldArr = ele.split(",")
User(fieldArr(0), fieldArr(1))
}
// TODO:关闭连接
}
// 5. 打印测试
resultDataSet.print()
}
map和mapPartition的效果是一样的,但如果在map的函数中,需要访问一些外部存储。例如:访问mysql数据库,需要打开连接`, 此时效率较低。而使用mapPartition可以有效减少连接数,提高效率
过滤出来
一些符合条件的元素
def main(args: Array[String]): Unit = {
// 1. 获取`ExecutionEnvironment`运行环境
val env = ExecutionEnvironment.getExecutionEnvironment
// 2. 使用`fromCollection`构建数据源
val wordDataSet = env.fromCollection(List("hadoop", "hive", "spark", "flink"))
// 3. 使用`filter`操作执行过滤
val resultDataSet = wordDataSet.filter(_.startsWith("h"))
// 4. 打印测试
resultDataSet.print()
}
可以对一个dataset
或者一个group
来进行聚合计算,最终聚合成一个元素
// 1. 获取`ExecutionEnvironment`运行环境
val env = ExecutionEnvironment.getExecutionEnvironment
// 2. 使用`fromCollection`构建数据源
val wordCountDataSet = env.fromCollection(List(("java" , 1) , ("java", 1) ,("java" , 1) ))
// 3. 使用`redice`执行聚合操作
val resultDataSet = wordCountDataSet.reduce{
(wc1, wc2) =>
(wc2._1, wc1._2 + wc2._2)
}
// 4. 打印测试
resultDataSet.print()
// 1. 获取`ExecutionEnvironment`运行环境
val env = ExecutionEnvironment.getExecutionEnvironment
// 2. 使用`fromCollection`构建数据源
val wordcountDataSet = env.fromCollection(List(("java" , 1) , ("java", 1) ,("scala" , 1) ))
// 3. 使用`groupBy`按照单词进行分组
val groupedDataSet = wordcountDataSet.groupBy(_._1)
// 4. 使用`reduce`对每个分组进行统计
val resultDataSet = groupedDataSet.reduce{
(wc1, wc2) =>
(wc1._1, wc1._2 + wc2._2)
}
// 5. 打印测试
resultDataSet.print()
可以对一个dataset或者一个group来进行聚合计算,最终聚合成一个元素
代码
// 1. 获取`ExecutionEnvironment`运行环境
val env = ExecutionEnvironment.getExecutionEnvironment
env.setParallelism(1)
// 2. 使用`fromCollection`构建数据源
val wordcountDataSet = env.fromCollection(
List(("java" , 1) , ("java", 1) ,("scala" , 1) )
)
// 3. 使用`groupBy`按照单词进行分组
val groupedDataSet = wordcountDataSet.groupBy(_._1)
// 4. 使用`reduceGroup`对每个分组进行统计
val resultDataSet = groupedDataSet.reduceGroup{
iter =>
iter.reduce{(wc1, wc2) => (wc1._1,wc1._2 + wc2._2)}
}
// 5. 打印测试
resultDataSet.print()
按照内置的方式来进行聚合, Aggregate只能作用于元组
上。例如:SUM/MIN/MAX…
// 1. 获取`ExecutionEnvironment`运行环境
val env = ExecutionEnvironment.getExecutionEnvironment
// 2. 使用`fromCollection`构建数据源
val wordcountDataSet = env.fromCollection(
List(("java" , 1) , ("java", 1) ,("scala" , 1) )
)
// 3. 使用`groupBy`按照单词进行分组
val groupedDataSet = wordcountDataSet.groupBy(0)
// 4. 使用`aggregate`对每个分组进行`SUM`统计
val resultDataSet = groupedDataSet.aggregate(Aggregations.SUM, 1)
// 5. 打印测试
resultDataSet.print()
注意
要使用aggregate,只能使用字段索引名或索引名称来进行分组
groupBy(0)
,否则会报一下错误:Exception in thread "main" java.lang.UnsupportedOperationException: Aggregate does not support grouping with KeySelector functions, yet.
// 1. 获取`ExecutionEnvironment`运行环境
val env = ExecutionEnvironment.getExecutionEnvironment
// 2. 使用`fromCollection`构建数据源
val wordcountDataSet = env.fromCollection(
List(("java" , 1) , ("java", 1) ,("scala" , 1) )
)
// 3. 使用`distinct`指定按照哪个字段来进行去重
val resultDataSet = wordcountDataSet.distinct(0)
// 4. 打印测试
resultDataSet.print()
使用join可以将两个DataSet连接起来
示例:
在资料\测试数据源
中,有两个csv文件,有一个为score.csv
,一个为subject.csv
,分别保存了成绩数据以及学科数据。
打印出后得样式
// 学科Subject(学科ID、学科名字)
case class Subject(id:Int, name:String)
// 成绩Score(唯一ID、学生姓名、学科ID、分数)
case class Score(id:Int, name:String, subjectId:Int, score:Double)
def main(args: Array[String]): Unit = {
// 1. 获取`ExecutionEnvironment`运行环境
val env = ExecutionEnvironment.getExecutionEnvironment
// 3. 分别使用`readCsvFile`加载csv数据源
val scoreDataSet = env.readCsvFile[Score]("./data/join/input/score.csv")
val subjectDataSet = env.readCsvFile[Subject]("./data/join/input/subject.csv")
// 4. 使用join连接两个DataSet,并使用`where`、`equalTo`方法设置关联条件
val joinedDataSet = scoreDataSet.join(subjectDataSet).where(2).equalTo(0)
// 5. 打印关联后的数据源
joinedDataSet.print()
}
将两个DataSet取并集,不会去重。
// 1. 获取`ExecutionEnvironment`运行环境
val env = ExecutionEnvironment.getExecutionEnvironment
// 2. 使用`fromCollection`创建两个数据源
val wordDataSet1 = env.fromCollection(List("hadoop", "hive", "flume"))
val wordDataSet2 = env.fromCollection(List("hadoop", "hive", "spark"))
// 3. 使用`union`将两个数据源关联在一起
val resultDataSet = wordDataSet1.union(wordDataSet2)
// 4. 打印测试
resultDataSet.print()
Flink也会产生数据倾斜
的时候,例如:当前的数据量有10亿条,在处理过程就有可能发生如下状况:
rebalance
会使用轮询的方式将数据均匀打散,这是处理数据倾斜最好的选择。
代码
object Collection {
def main(args: Array[String]): Unit = {
// 1. 获取`ExecutionEnvironment`运行环境
val env = ExecutionEnvironment.getExecutionEnvironment
// 2. 使用`env.generateSequence`创建0-100的并行数据
val numDataSet = env.generateSequence(0, 100)
// 3. 使用`fiter`过滤出来`大于8`的数字
val filterDataSet = numDataSet.filter(_ > 8).rebalance()
// 4. 使用map操作传入`RichMapFunction`,将当前子任务的ID和数字构建成一个元组
val resultDataSet = filterDataSet.map(new RichMapFunction[Long, (Long, Long)] {
override def map(in: Long): (Long, Long) = {
(getRuntimeContext.getIndexOfThisSubtask, in)
}
})
// 5. 打印测试
resultDataSet.p
按照指定的key进行hash分区
// 1. 获取`ExecutionEnvironment`运行环境
val env = ExecutionEnvironment.getExecutionEnvironment
// 1. 设置并行度为`2`
env.setParallelism(2)
// 2. 使用`fromCollection`构建测试数据集
val numDataSet = env.fromCollection(List(1,1,1,1,1,1,1,2,2,2,2,2))
// 3. 使用`partitionByHash`按照字符串的hash进行分区
val partitionDataSet: DataSet[Int] = numDataSet.partitionByHash(_.toString)
// 4. 调用`writeAsText`写入文件到`data/parition_output`目录中
partitionDataSet.writeAsText("./data/parition_output")
// 5. 打印测试
partitionDataSet.print()
指定字段对分区中的数据进行排序
// 1. 获取`ExecutionEnvironment`运行环境
val env = ExecutionEnvironment.getExecutionEnvironment
// 2. 使用`fromCollection`构建测试数据集
val wordDataSet = env.fromCollection(List("hadoop", "hadoop", "hadoop", "hive", "hive", "spark", "spark", "flink"))
// 3. 设置数据集的并行度为`2`
wordDataSet.setParallelism(2)
// 4. 使用`sortPartition`按照字符串进行降序排序
val sortedDataSet = wordDataSet.sortPartition(_.toString, Order.DESCENDING)
// 5. 调用`writeAsText`写入文件到`data/sort_output`目录中
sortedDataSet.writeAsText("./data/sort_output/")
// 6. 启动执行
env.execute("App")
import org.apache.flink.api.scala.{DataSet, ExecutionEnvironment}
import org.apache.flink.core.fs.FileSystem.WriteMode
import org.apache.flink.api.scala._
object BatchSinkCollection {
def main(args: Array[String]): Unit = {
//1.定义环境
val env = ExecutionEnvironment.getExecutionEnvironment
//2.定义数据 stu(age,name,height)
val stu: DataSet[(Int, String, Double)] = env.fromElements(
(19, "zhangsan", 178.8),
(17, "lisi", 168.8),
(18, "wangwu", 184.8),
(21, "zhaoliu", 164.8)
)
//3.TODO sink到标准输出
stu.print
//3.TODO sink到标准error输出
stu.printToErr()
//4.TODO sink到本地Collection
print(stu.collect())
env.execute()
}
}
flink支持多种存储设备上的文件,包括本地文件,hdfs文件等。
flink支持多种文件的存储格式,包括text文件,CSV文件等。
writeAsText():TextOuputFormat - 将元素作为字符串写入行。字符串是通过调用每个元素的toString()方法获得的。
1.本地文件
import org.apache.flink.api.scala.{DataSet, ExecutionEnvironment}
import org.apache.flink.core.fs.FileSystem.WriteMode
import org.apache.flink.api.scala._
/**
* 将数据写入本地文件
*/
object BatchSinkFile {
def main(args: Array[String]): Unit = {
//1.定义环境
val env = ExecutionEnvironment.getExecutionEnvironment
//2.定义数据
val ds1: DataSet[Map[Int, String]] = env.fromElements(Map(1 -> "spark", 2 -> "flink"))
//3 .TODO 写入到本地,文本文档,NO_OVERWRITE模式下如果文件已经存在,则报错,OVERWRITE模式下如果文件已经存在,则覆盖
ds1.setParallelism(1).writeAsText("test/data1/aa", WriteMode.OVERWRITE)
env.execute()
}
}
2.将数据写入HDFS
import org.apache.flink.api.scala.{DataSet, ExecutionEnvironment}
import org.apache.flink.core.fs.FileSystem.WriteMode
import org.apache.flink.api.scala._
/**
* 将数据写入本地文件
*/
object BatchSinkFile {
def main(args: Array[String]): Unit = {
//1.定义环境
val env = ExecutionEnvironment.getExecutionEnvironment
//2.定义数据 stu(age,name,height)
val stu: DataSet[(Int, String, Double)] = env.fromElements(
(19, "zhangsan", 178.8),
(17, "lisi", 168.8),
(18, "wangwu", 184.8),
(21, "zhaoliu", 164.8)
)
val ds1: DataSet[Map[Int, String]] = env.fromElements(Map(1 -> "spark", 2 -> "flink"))
//1.TODO 写入到本地,文本文档,NO_OVERWRITE模式下如果文件已经存在,则报错,OVERWRITE模式下如果文件已经存在,则覆盖
ds1.setParallelism(1).writeAsText("hdfs://bigdata111:9000/a", WriteMode.OVERWRITE)
env.execute()
}
}
Flink支持两种不同的本地执行。
LocalExecutionEnvironment
是启动完整的Flink运行时(Flink Runtime),包括 JobManager 和 TaskManager 。 这种方式包括内存管理和在集群模式下执行的所有内部算法。
CollectionEnvironment
是在 Java 集合(Java Collections)上执行 Flink 程序。 此模式不会启动完整的Flink运行时(Flink Runtime),因此执行的开销非常低并且轻量化。 例如一个DataSet.map()
变换,会对Java list中所有元素应用 map()
函数。
LocalEnvironment
是Flink程序本地执行的句柄。可使用它,独立或嵌入其他程序在本地 JVM 中运行Flink程序。
本地环境通过该方法实例化ExecutionEnvironment.createLocalEnvironment()
。
默认情况下,启动的本地线程数
与计算机的CPU个数
相同。也可以指定所需的并行性。本地环境可以配置为使用enableLogging()/ 登录到控制台disableLogging()。
在大多数情况下,ExecutionEnvironment.getExecutionEnvironment()是更好的方式。LocalEnvironment当程序在本地启动时(命令行界面外),该方法会返回一个程序,并且当程序由命令行界面调用时,它会返回一个预配置的群集执行环境。
/**
* local环境
*/
object BatchCollectionsEven {
def main(args: Array[String]): Unit = {
// 开始时间
var start_time =new Date().getTime
//TODO 初始化本地执行环境
val env = ExecutionEnvironment.createLocalEnvironment
val list: DataSet[String] = env.fromCollection(List("1","2"))
list.print()
// 结束时间
var end_time =new Date().getTime
println(end_time-start_time) //单位毫秒
}
}
使用集合的执行CollectionEnvironment是执行Flink程序的低开销方法。这种模式的典型用例是自动化测试,调试和代码重用。
用户也可以使用为批处理实施的算法,以便更具交互性的案例
请注意,基于集合的Flink程序的执行仅适用于适合JVM堆的小数据。集合上的执行不是多线程的,只使用一个线程
/**
* local环境
*/
object BatchCollectionsEven {
def main(args: Array[String]): Unit = {
// 开始时间
var start_time =new Date().getTime
//TODO 初始化本地执行环境
val env = ExecutionEnvironment.createCollectionsEnvironment
val list: DataSet[String] = env.fromCollection(List("1","2"))
list.print()
// 结束时间
var end_time =new Date().getTime
println(end_time-start_time) //单位毫秒
}
}
Flink程序可以在许多机器的集群上分布运行。有两种方法可将程序发送到群集以供执行:
./bin/flink run ./examples/batch/WordCount.jar --input file:///home/user/hamlet.txt --output file:///home/user/wordcount_out
使用代码中远程环境提交
1.添加Maven插件
org.apache.maven.plugins
maven-jar-plugin
2.6
true
lib/
com.flink.DataStream.RemoteEven
org.apache.maven.plugins
maven-dependency-plugin
2.10
copy-dependencies
package
copy-dependencies
${project.build.directory}/lib
/**
创建远程执行环境。远程环境将程序(部分)发送到集群以执行。请注意,程序中使用的所有文件路径都必须可以从集群中访问。除非通过[[ExecutionEnvironment.setParallelism()]显式设置并行度,否则执行将使用集群的默认并行度。
* @param host JobManager的ip或域名
* @param port JobManager的端口
* @param jarFiles 包含需要发送到集群的代码的JAR文件。如果程序使用用户定义的函数、用户定义的输入格式或任何库,则必须在JAR文件中提供这些函数。
*/
def createRemoteEnvironment(host: String, port: Int, jarFiles: String*): ExecutionEnvironment = {
new ExecutionEnvironment(JavaEnv.createRemoteEnvironment(host, port, jarFiles: _*))
}