作者:仲颖凯翰奕颖 | 来源:互联网 | 2023-06-19 13:29
Hadoop只是一套工具的总称,它包含三部分:HDFS,Yarn,MapReduce,功能分别是分布式文件存储、资源调度和计算。仓库管理中心(namenode)每次入库,需要管理中心安排自我复制多份放
Hadoop只是一套工具的总称,它包含三部分:HDFS,Yarn,MapReduce,功能分别是分布式文件存储、资源调度和计算。仓库管理中心(namenode)每次入库,需要管理中心安排自我复制多份放到不同的仓库,每次有人来取,需要去管理中心查询在哪些仓库并就近获取。(namenode管理元数据,负责HDFS上数据的读写)
HDFS(分布式文件系统) :
分布式文件系统,将不同服务器的硬盘连接起来,在外面看起来就好像一块巨大的硬盘。然后构建与其上的MapReduce协同各个服务器运算。
MapReduce(分布式计算模型) :
用来解决大规模集群协同运算的问题,海量数据计算,Map阶段把数据分发给多个处理站分批统计处理,Reduce阶段把多个处理站处理的解决汇总给一个处理站再处理统计,最后放到HDFS就是资源。
Yarn :
用Yarn调度资源,读取HDFS文件内容进行MR计算。要写Java代码,但做数据的最好的工具是什么?SQL!所以Hive相当于这一套标准流程的SQL化。
Hive :
可以简单理解为基于HDFS的数据仓库工具,可以将Hive SQL转化为MapReduce进行数据处理查询的工具。Hadoop之上添加了自己的SQL解析和优化器,写一段SQL,解析为Java代码,然后去执行MR,底层数据还是在HDFS上。它们把脚本和SQL语言翻译成MapReduce程序,丢给计算引擎去计算,而你就从繁琐的MapReduce程序中解脱出来,用更简单更直观的语言去写程序了。
它的最大贡献有两个:
提供了一个存储和管理元数据的HiveMetastore,以库和表的形式来组织存储在HDFS上面的数据; 实现了一套将SQL转换为分布式执行的MapReduce的执行引擎。这么看起来,Hive就是一个分布式的数据库嘛,但是它是OLAP,适合在海量数据上做大范围的SCAN和聚合,不擅长做OLTP数据(如MySQL)那样频繁的CRUD数据。 Hbase :
是Hadoop项目的子项目,是一个分布式的、面向列的NoSQL数据库,虽然不是基于MapReduce,但是也是可以存储、处理大规模的数据,是Hadoop家族重要的一员。
Spark :
spark streaming 微批次准实时处理数据吞吐量巨大,应用于很多的实时数据统计场景,基于内存。
从hive + 元数据管理 + HDFS 变成 spark + 元数据管理 + HDFS工作量太大,就想了一个妥协的办法,在元数据管理上沿用Hive Metastore,但是在执行引擎上用Spark SQL。
Flink :对流批关系、有状态计算等做了更合理的抽象,有状态计算是指下一次计算依赖上一次结算输出的结果,Flink里面把它叫做State。Flink 支持三种时间机制:事件时间,注入时间,处理时间,同时支持 watermark 机制处理迟到的数据,说明Flink在处理乱序大实时数据的时候,优势比较大。
举例使用场景(订单) :
订单数据插入数据库时一般会有binlog,即记录插入、更新或删除的数据,我们只要能实时拿到这一条binlog,就相当于拿到了实时数据。数据库的主从备份机制,一般本身就是拿主库的binlog同步到备份库,canal可以把自己伪装成备份库,来拉取主库的binlog,再解析、包装最后抛出,就相当于实时拿到数据。canal抛出的消息下游无法一次消费完,可以放入消息队列,Kafka和rocketmq 就是起这样的作用:异步、解耦、消峰。canal的数据一般会抛到kafka或RocketMQ,可以保存一段时间。然后下游程序再去实时拉取消息来计算。下游有Spark Streaming 就是用来微批地处理流式数据的,Flink 不同于 Spark Streaming 的微批次处理,它是一条一条数据处理的。但难免会有些数据沿途受阻晚来了几秒钟,这就会导致两个问题:数据延迟和乱序数据。这也是做实时数据的非常关注的问题。
数据延迟 :如果是上游数据迟了,就加大上游资源;如果是数据突然激增,Flink 处理不过来导致任务出现延迟,就加大 Flink 的资源,比如并发。 数据乱序 :一般也通过上游和 Flink 本身来分别保证。 Kafka 有分区的概念,就像是不同的通道,一条消息来了后,可以走 A,也可以走 B,也可以走 C。那么问题来了,业务数据抛入 Kafka,如何保证消息的顺序性呢?
顺序性一般有两方面需要保证。我们举一个小小的例子,一个用户下单的场景,有两个基本共识:
(1)同一个用户的订单状态会先后变化;
那么如何保证同一用户的订单顺序呢?很简单,前面我们提到的链路是,数据库中插入或更新数据时,会实时产生该条数据的 binlog,canal 获取、解析、包装这条 binlog 并抛入 Kafka。Kafka 由于有分区的存在,很可能同一个订单的消息会被发送到不同的分区中,这样的话,如果下游的 Flink 任务消费不同分区速率不同,就可能导致先到的数据反而被后消费,产生顺序误差。解决的办法即保证同一订单的消息进入 Kafka 的同一分区即可。Kafka 的每一条消息都会有 messageKey 和 message 两个结构,如果没有直接给消息指定分区,那么 messageKey 决定了消息进入哪个分区,在 canal 中,我们便可以设定消息如何进入 Kafka。数据库中的业务数据,会存在一张张的表中,表一般都会有主键,来唯一标识一条数据,我们一般也就是通过设定 canal 选择 binlog 所在表的主键来决定其进入 Kafka 的分区。这样,就基本解决了第一个问题。
(2)不同用户的不同订单也有先后之分。
但这只保证了同一订单数据的顺序性,并未保证不同订单之间的顺序性。在 Flink 中,有种机制就叫做 watermark。每一条数据一般都会自带一个时间字段,来标志这条数据的业务时间,即什么时候发生的。然后 Flink 提取这个时间字段,就知道了目前 Flink 任务进行到几点了。在实时的流数据中,如果想要拿到 T 时刻的数据,只要等一小会儿比如 1s,就能保证在 T+1s 的时刻拿到 T 时刻的所有数据。考虑乱序或迟到数据,我们一般也会让 Flink 当前的时间稍微迟几秒钟。
数据湖框架采用批式和流式共存的 Lambda 架构 1 批式模型就是使用 MapReduce、Hive、Spark 等典型的批计算引擎,以小时任务或者天任务的形式来做数据计算。 2 流式模型就是使用 Flink 来进行实时的数据计算。
额外补充 :
Copy On Write(写时才拷贝) :优雅地解决读多写少场景下的并发问题,懒惰行为,拖延战术,每次写文件操作都写在特定大小的一块内存中(磁盘缓存),只有当我们关闭文件时,才写到磁盘上(这就是为什么如果文件不关闭,所写的东西会丢失的原因),更有甚者是文件关闭时都不写磁盘,而一直等到关机或是内存不够时才写磁盘,Unix就是这样一个系统。仅使用列式文件(parquet)存储数据。在写入/更新数据时,直接同步合并原文件,生成新版本的基文件(需要重写整个列数据文件,即使只有一个字节的新数据被提交)。
CopyOnWriteArrayList:总体设计思想是在读的过程中去掉了锁,而在写的过程中则需要引入互斥锁,但是这个锁不会影响到读本身。
通过OS copy-on-write 的过程我们可以总结出两个重要的特性:
父子进程的内存共享的数据仅仅是fork那一时间点的数据,fork 后的数据不会有任何共享; 所谓 lazy copy,就是在需要修改的时候拷贝一个副本出来,如果没有任何改动,则不会占用额外的物理内存。 COW核心思想: 如果有多个调用者(callers)同时请求相同资源(如内存或磁盘上的数据存储),他们会共同获取相同的指针指向相同的资源,直到某个调用者试图修改资源的内容时,系统才会真正复制一份专用副本(private copy)给该调用者,而其他调用者所见到的最初的资源仍然保持不变。
Merge On Read(读时合并) :更新被写入到增量日志文件中,该文件以 avro 格式存储。这些增量日志文件始终与基本文件相关联。假设有一个名为 data_file_1 的数据文件,对 data_file_1 中记录的任何更新都将写入到新的增量日志文件。在服务读取查询时,实时合并基础文件及其相应的增量日志文件中的记录。