在阅读这篇文章前,推荐一篇“好”文章:Spring AI 再更新:如何借助全局参数实现智能数据库操作与个性化待办管理
这篇文章利用 Spring AI 实现当下热度非常高的生成式AI一个案例,对于Javaer来说,绝对是一个不错的学习机会。
第一次听说大数据是在18年左右,那个时候也才刚入行还在CRUD,对大数据这么高端的技术没有什么概念,只知道很牛逼,就买了一本《Hadoop 权威指南》。至今已经6、7年了,也只看了第一章的第一节《数据!数据!》,现在还陈列在我的书架上的第一排。
虽然这期间再也没有进行学习,但毕竟是程序员嘛,多少会接触到大数据相关的一些边缘工作,说起大数据的一些技术也都不陌生,比如Hadoop、Spark、Flink什么的,但总归是没有深入了解。这次花了1个月的时间,集中学习大数据的一些技术,一来看看这里面到底是怎么个事,二来作为一名内容创作者,顺便积攒一些题材(虽然不太新鲜)。
本文就来总结一下我学习大数据技术的内容、遇到的问题以及自己的理解,希望对想要了解大数据的小伙伴有一些借鉴。如果文中有描述不到位的知识点,也请各位大佬指正。
我一直秉持一个学习理念,就是学习某个技术先了解其历史,即:这个技术诞生的背景以及解决的问题(本文也会有体现),即便它已经被淘汰。因此,我先了解了大数据技术的发展历程,然后对当前市场上受欢迎的技术逐个深入。
这一个月下来,虽说没有掌握所有技术的思想和原理,但最起码清楚每个技术诞生的背景、解决的问题以及不足之处,也算是建立了我对大数据的认知。
下面是我对这些技术按照职能进行的分类。
在学习的过程中,【数据集成工具】类投入的时间最少,只是简单的了解了下功能。有两个原因:
这些数据集成工具在这里提一下,文中不再介绍。
其次是Hive、HBase基于存储和计算的上层应用。
【数据计算】类投入的时间最长,尤其是 Spark,占了一半的时间。主要是在这个部分总会冒出很多个为什么,一次次地花费时间去找答案。
下面对这些技术进行总结说明,希望可以为0基础的你构建对大数据技术的认知。
就如上图中的技术分类,大数据技术主要解决的就是海量数据的存储和计算问题。
这两个问题的解决方案最先被 Google 被提出,用于解决 Google 搜索引擎海量的网页存储和索引的构建。对应的技术就是日后被人所熟知的 HDFS 和 MapReduce。
不关注大数据的可能对这两个名词比较陌生,但是Hadoop大家一定都听说过吧。HDFS 和 MapReduce 就是 Hadoop 项目中的两个核心组件,一个用于解决海量数据存储问题,一个用来解决海量数据计算问题。
HDFS 全称为分布式文件系统,没错,只要是涉及到分布式,任何一个大问题都能分成多个小问题一一解决,所以大数据技术的本质的也是分而治之。
HDFS 的分而治之就是将一个大文件切分成多个数据块分布在不同服务器上存储,如下图
DataNodes 就是用来存储这些数据块的服务器集群,物理表现就是一群 JVM 进程。NameNode 是用来管理一个大文件被分了多少块、分别存在哪个服务器,方便查找定位。
HDFS 整体思想比较容易理解,但实现起来并不简单,背后还要考虑很多问题,例如文件如何分片、如何保证数据不丢失、某个服务器故障怎么办、如何实现数据的扩展性等等。
如果对这些实现细节感兴趣,请关注我,欢迎大家一起交流。
存储问题解决了,计算问题是如何解决的?
试想一下,要计算一个大小为100G的文件中某个字符出现的次数,应该怎么做?
用一个计算节点读取分布在 HDFS 中的一个个数据块进行计算?那这个计算节点需要多大的内存?即便有这么大的内存,多久能计算完?
如果你是这样的想法,请跳出单机思维。
看下 MapReduce 是怎么做的。
MapReduce 作为大规模计算框架,它的核心思想是这样的:既然一个大文件可以作为多个数据块存储在 HDFS 集群中,那何不将计算的程序直接传输到这些数据块所在的节点进行分布式计算?
以128M(HDFS 默认分割大小)为一个数据块,100G得有800个数据块。如果按照单机思维,最少要进行800次128M的传输。但如果把一个1M大小的程序传输800次,是不是比前者划算?这也是大数据计算中非常重要的一个思想:移动计算比移动数据更划算。
而之所以叫 MapReduce,是因为 MapReduce 将计算分为了 Map 和 Reduce 两个阶段。开发人员在编码时只需要编写 Mapper 和 Reducer 的实现即可,不用关注程序的移动、计算结果的聚合等分布式编程工作。
以统计字符出现次数的代码为例:
public class WordCount {
public static class TokenizerMapper
extends Mapper<Object, Text, Text, IntWritable>{
private final static IntWritable one = new IntWritable(1);
private Text word = new Text();
public void map(Object key, Text value, Context context
) throws IOException, InterruptedException {
StringTokenizer itr = new StringTokenizer(value.toString());
while (itr.hasMoreTokens()) {
word.set(itr.nextToken());
context.write(word, one);
}
}
}
public static class IntSumReducer
extends Reducer<Text,IntWritable,Text,IntWritable> {
private IntWritable result = new IntWritable();
public void reduce(Text key, Iterable<IntWritable> values,
Context context
) throws IOException, InterruptedException {
int sum = 0;
for (IntWritable val : values) {
sum += val.get();
}
result.set(sum);
context.write(key, result);
}
}
public static void main(String[] args) throws Exception {
Configuration conf = new Configuration();
String[] otherArgs = new GenericOptionsParser(conf, args).getRemainingArgs();
if (otherArgs.length < 2) {
System.err.println("Usage: wordcount <in> [<in>...] <out>");
System.exit(2);
}
Job job = Job.getInstance(conf, "word count");
job.setJarByClass(WordCount.class);
job.setMapperClass(TokenizerMapper.class);
job.setCombinerClass(IntSumReducer.class);
job.setReducerClass(IntSumReducer.class);
job.setOutputKeyClass(Text.class);
job.setOutputValueClass(IntWritable.class);
for (int i = 0; i < otherArgs.length - 1; ++i) {
FileInputFormat.addInputPath(job, new Path(otherArgs[i]));
}
FileOutputFormat.setOutputPath(job,
new Path(otherArgs[otherArgs.length - 1]));
System.exit(job.waitForCompletion(true) ? 0 : 1);
}
}
main
方法执行后,最终会将 Mapper 和 Reducer 的实现作为计算任务分发到数据所在的节点。这样,每个计算任务只用计算128M的数据,800个计算任务同时计算就达到了并行计算的效果,从而实现海量数据的快速计算。
并行计算大概是这样:
每个节点都会先执行 Map 任务(TokenizerMapper
) ,将字符出现的次数设置为1,并输出为map(key,value)格式。然后执行 Reduce 任务 (IntSumReducer
)将相同字符(key)的次数相加,最后将各节点的结果聚合。
总之,MapReduce 解决了海量数据计算的问题,提供 Map 和 Reduce 这样简单的编程模型,也简化了开发人员对大数据计算的编程难度。
MapReduce 是如何进行任务分发的、计算结果是如何聚合的?如果对这些实现细节感兴趣,请关注我,欢迎大家一起交流。
虽说 MapReduce 简化了大数据编程的难度,但是如果每来一个需求都要写一个 MapReduce 代码,那岂不是太麻烦了。尤其是在全民“CRM”的2000年代,对于像数据分析师已经习惯使用SQL进行分析和统计的工程师,让他们去 MapReduce 编程还是有一定的难度。
所以,又先后出现了 Pig、Hive 基于 MapReduce 的上层应用,前者通过将 Pig Latin 编程语言编写的脚本转换为 MapReduce 计算任务,后者通过将 HiveQL 转换为 MapReduce 程序。总之,都是为了再次简化大数据计算的难度。
而有着近似 SQL 语言的 Hive 就比较非常受欢迎(本文主要介绍 Hive),因为非 Java 编程者可以通过 SQL 语句对 HDFS 的数据实现 MapReduce 操作,从而完成大数据计算的工作。
通过实操更直观的感受一下 Hive 到底是啥。
既然涉及到SQL,那一定会存在表名、列名,Hive 自然而然承担了表名、列名、数据类型等元数据和 HDFS 文件数据映射关系的管理。
以下面的 HiveQL 为例,创建的表最终会根据hive.metastore.warehouse.dir
配置,在 HDFS 生成一个名为test
的目录。
CREATE TABLE test(id int,name string,age int) ROW FORMAT DELIMITED FIELDS TERMINATED BY '\t';
再以下面的 HiveQL 为例,添加的数据最终会在test
目录下体现。
insert into test values (1,'zs',18),(2,'ls',19),(3,'ww',20);
通过下图可以发现,在执行 insert
语句时会转化为 MapReduce 任务并执行,所以会发现这个过程比传统数据库慢很多,当然,Hive并不是为这么点数据量诞生的,也不仅仅只支持这样简单的场景。
通过上面的示例,是不是感觉 Hive 和数据库差不多?
Hive 本身的技术架构确实没有什么创新,就是成熟的数据库技术和HDFS、MapReduce的结合体。有 HDFS 这样的存储底座可以存储大量的数据,又可以通过 SQL 语句实现数据的统计和分析,Hive 又被称为数据仓库。
如果对数据仓库没有概念的可以看下oracle对数仓的定义。
思考:select avg(age) from test
这样的 SQL 会转换成什么样的 MapReduce 程序,欢迎评论区交流。
在 MapReduce 为海量数据的计算服务多年后,随着时代的发展和 Spark 等新技术的出现,它的劣势也慢慢的凸显出来了:
先看第一点
2000 年代诞生的 MapReduce ,因为计算资源有限,所以 MapReduce 在计算完成后会将结果写回HDFS中,也就是落盘。
以上面800个数据块为例子,MapReduce 会有800次的 Map 计算结果落盘以及多个 Reduce 计算结果聚合(这个行为有一个专业的术语: Shuffle,这里不具体说明)。
按照现在的思维,大家肯定认为结果应该写在内存中,但实际情况就像上面所说:计算资源有限。为了让大家有所概念,这里看下2000年的内存报价。
所以在那个时候,大家并不会觉得慢,也不会认为有什么问题。
再看第二点(编程复杂度过高)
虽说 Pig、Hive 已经对 MapReduce 编程进行封装降低了海量数据计算的难度,但是在构造一些复杂计算需求时依然需要进行 MapReduce 编程。
由于 MapReduce 的计算模型只有 Map 和 Reduce 两个阶段,在实现复杂计算需求时就要编写多个 Mapper 和 Reducer 的实现。不仅如此,还要协调这些 MapReduce 任务顺序,甚至要设计一个协调系统。如此一来,就增加整个系统的复杂度。
Spark 的诞生有效的解决了这两个问题。
Spark 是什么?
Spark 和 MapReduce 一样,都是一个计算引擎,都是为了解决海量数据计算。两者的工作流程大体类似,都是分而治之,都是移动计算。只不过 Spark 利用内存存储计算结果使得任务执行更高效,提供的编程模型使得编程更简单。
Spark 的高效通过文字很难体现,后面会专门写一篇相关的文章。
大家可以通过下面的代码直观的感受一下 Spark 编程的简易程度。
val textFile = sc.textFile("hdfs://...")
val counts = textFile.flatMap(line => line.split(" "))
.map(word => (word, 1))
.reduceByKey(_ + _)
counts.saveAsTextFile("hdfs://...")
这段和 MapReduce 的 WordCount 程序达到同样的目的,但是只用了三行代码(了解函数式编程应该不难理解)。
所以,Spark 出现后早就没有人去用 MapReduce 了。但这并不影响 MapReduce 的地位,毕竟它的设计思想影响了很多技术,例如Spark。
和 MapReduce 相比,Spark 确实有很多概念比较晦涩难懂,例如 RDD、DAG、Stage。还有在学习的过程中对 Spark 的一些描述产生的疑问,例如“为什么说 Spark 是内存计算?计算不都基于内存?”,以及随之而来的“Spark 的高效到底体现在哪里?”、“同一个 Stage 的多个算子是最终由几个任务执行?”等等。最后,只能通过一遍遍的的教程和源码来解答心中的疑问。
由于文章篇幅的原因,如果你和我一样有同样的疑问,请关注我,欢迎大家一起交流。
上面所说的 MapReduce 和 Spark ,都是在解决海量数据计算问题,这里说的海量数据有一个特点就是:已经存在的,例如历史数据。
对于 MapReduce 和 Spark 来讲,这些数据是一大批数据,也称之为有界数据,对这些数据的计算就称为批计算。
随着移动互联网、物联网的兴起,每分每秒都在产生大量的数据,例如传感器产生的数据、订单交易数据、用户行为记录等。这些无时不刻都在产生的数据,我们称之为无界数据或流数据。通常被用于用户行为分析进行实时推荐、销售数据实时分析进行营销策略调整等场景。
MapReduce 和 Spark 这样的批计算系统就满足不了实时计算的需求。所以就诞生了 Storm、Spark Streaming、Flink这些流计算系统。
批计算和流计算整体工作流程大体一致,都有任务调度、计算结果聚合等过程。只不过因为数据源的原因,执行细节上会有所不同。这里有几个概念需要进行区分,避免混淆:
由于 Spark Streaming 是建立在 Spark 基础上,所以任务执行还是 Spark 的逻辑,所以 Spark Streaming 算是一个“伪”流计算系统,属于批计算这一波。
简单来说 Spark Streaming 通过很小的时间间隔(例如1秒)将实时数据收集为“微批”数据,然后然后交给 Spark 处理。
因为是间隔一段时间再去计算,所以在实时性方面,Spark Streaming 就不如 Flink,现在说起流计算基本上就是在讨论 Flink 了。不过两者的架构和概念有很多相似的地方,也都是函数式编程。如果掌握了 Spark ,学习 Flink 也就非常简单轻松了。
至于 Storm ,作为早期的实时计算引擎并不支持有状态计算和exactly-once的语义,以及编码相对复杂,所以现在也慢慢被大家遗忘。
如果有“Spark Streaming 收集的数据是怎么分区的?”、“什么是有状态计算和exactly-once语义?”、“Flink 是如何做到数据、计算本地化?”等疑问的,请关注我,欢迎大家一起交流。
HBase 和 HDFS、MapReduce 是 Google 三篇大论文对应的开源实现。全称为 Hadoop DataBase ,看到这个名称也就不难理解,HBase 是一款数据库。
提到数据库大家应该就有概念了,只不过 HBase 是非关系型数据库、是基于 HDFS 实现的分布式 NoSQL 数据库。
NoSQL 大家应该都不陌生,Redis、MongoDB等一度要替代 SQL 的 No SQL 到后来的 Not Only SQL 的各式各样数据库。所以 HBase 本身也是不支持SQL语句的。
虽然基于 HDFS 存储,但是HBase却支持数据的实时增删改查。
这里有个概念需要知道:
传统的关系型数据库是单机的,想要做到实时的增删改查是比较容易的。但是 HDFS 中的一个文件是要分布在不同的节点上,实时添加一条数据相对容易,直接在文件末尾追加数据即可。删除、修改这样的更新操作还要去定位到数据,是怎么做到实时的?
带上问题去了解 HBase,欢迎大家一起交流。
其实在大数据技术中还有一个特别重要的技术,那就是Yarn。
作为 Hadoop2.0从 MapReduce 中分离出来的组件,Yarn 已经是一个通用的资源调度管理平台,MapReduce 也可以专注于计算任务的调度工作。
那资源调度是干什么的?
简单来说,大数据的存储和计算通常会由集群中的多个节点共同完成,这些节点就是资源。Yarn 知道集群中有哪些资源可用、哪些不可用,无论是 MapReduce、Spark 或 Flink 都可以通过 Yarn 统一安排资源,统一使用集群中的资源。这样,在一个大数据平台中,如果涉及到多个计算引擎,也不用部署多个资源管理集群。
在学习过程中可能会发现 MapReduce、Spark 和 Flink 也有自己的资源管理器,随之产生疑问:“各计算引擎知道自己的数据在哪,可以进行任务调度,Yarn 是怎么知道数据在哪?怎么进行任务调度?”
Yarn 确实不知道,但是你的程序会在 Yarn 中执行(ApplicationMaster),在申请资源时 Yarn 会返回一堆的资源交给计算引擎。任务调度最终还是各计算引擎自己去调度,各计算引擎需要做的就是实现 Yarn 提供的接口,例如 MapReduce 的 MRAppMaster。所以,只要实现了 Yarn 的接口规范,都可以被 Yarn 调度管理。
像 Yarn、K8s这样的资源调度平台,都是把资源的管理、分配以及计算任务调度、容错做到了自动化,可以提高资源的利用率,减少开发运维人员的工作量。
在实际的工作中,这些大数据技术都是协同工作的。例如,使用 Hive、Spark或Flink对数据进行分析,然后将结果存储到HBase中实现实时查询,再例如 Spark on Hive 或者 Hive on Spark等等,再例如 MapReduce、Spark、Storm、Flink这些计算任务都可以通过 Yarn 进行调度。
下图是一个典型的互联网大数据平台的架构。
下图是美团大数据平台的架构,感兴趣的可以了解一下。
最后,我回应网上流传的一句话结尾,那就是“大数据都凉了,学这干啥?”
我想说:
很多技术的思想是相通的,如果不了解过去?怎么探知未来?认知又如何提高呢?
Google发表的三篇论文开启了大数据的时代,在这20年里诞生了很多的大数据技术。有的技术尽管在初期发挥了重要作用,但因为时代的发展以及新技术的诞生已经边缘化(例如MapReduce、Storm)。有的技术在诞生至今一直是大数据领域的核心的一部分(例如HDFS)。也有些技术因为自身的优势以及丰富的生态,成为各自领域的热门选择(例如Spark、Flink)。无论如何,这都是技术发展演进的必经之路。
而大数据的分布式思想已经远远超出了本身,体现在了在不同领域。例如,在应用开发中涉及到的高可用、高并发、微服务等技术都有分布式的影子,都是分而治之的思想。所以我认为,学习一门技术就是在学习它的思想。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。