用大白话告诉你没有人看得懂的Hadoop架构原理资源调度系统)、MapReduce(分布式计算系统)等等。有的朋友可能听说过Hadoop,但是并不清楚它到底是什么。本文将用通俗易懂的语言为您讲解。如果你公司的所有数据都存储在MySQL中,那么就把它们都放在一台数据库服务器上。假设这台服务器的磁盘空间是2T。让我们看看下面的图片。现在问题来了,你一直往这台服务器的mysql里放数据,结果数据量越来越大,超过了2T的大小,怎么办?你说,我可以跑多个MySQL数据库服务器,分库分表!将一部分数据放在每个服务器上是不够的。如图!好的,没问题,那我们建3个数据库服务器,3个MySQL实例,每个服务器能放2T的数据。现在我问大家一个问题,所谓的大数据是干什么的?下面说说大数据最基本的一个使用场景。假设你有一个电商网站,你想存储和分析用户在电商网站上的页面和应用的所有点击、购买、浏览行为日志。现在你把这些数据全部放在3台MySQL服务器上,数据量巨大,但勉强能放下。一天早上,你的老板来了。你要看一个报表,比如你要看网站的X指标、Y指标、Z指标等等,每天二十、三十个数据指标。行了,大哥,你现在试试写个SQL,把点击、购买、浏览的日志里的二十个、三十个指标分析出来?我跟你打赌,你一定会写出几百行甚至上千行的超级复杂的大SQL。分库分表后,你觉得这条SQL能跑在三台MySQL服务器上吗?如果你觉得可以,那你一定不明白MySQL在分库分表后有多么坑,大SQL跨库join几百行,各种复杂的计算简直不现实。因此,大数据的存储和计算根本不是MySQL来完成的,于是Hadoop、Spark等大数据技术体系应运而生。本质上,Hadoop、Spark等大数据技术实际上是一系列的分布式系统。例如Hadoop中的HDFS是大数据技术体系的核心基石,负责数据的分布式存储。这是什么意思?别担心,继续阅读。HDFS的全称是HadoopDistributedFileSystem,即Hadoop的分布式文件系统。它由很多机器组成,每台机器运行一个DataNode进程,负责管理部分数据。然后在一台机器上运行着一个NameNode进程。NameNode大致可以认为是这样一个负责管理整个HDFS集群的进程,它存储了HDFS集群的所有元数据。然后就是很多台机器,每台机器存储一部分数据!那么,HDFS现在可以很好地存储和管理大量数据。这时候你肯定会有疑问:MySQL服务器不也是一样吗?如果你这么想,那你就大错特错了。这件事没有你想的那么简单。HDFS天生就是一种分布式技术,所以你可以上传大量的数据,存储数据,管理数据,自然可以使用HDFS来做。如果硬要基于MySQL分库分表,会痛苦很多倍,因为MySQL并不是设计成分布式系统架构的,在分布式数据存储上缺少很多数据保护机制。好了,既然已经使用HDFS分布式存储了数据,那是不是还要对数据进行分布式计算呢?对于分布式计算:很多公司使用Hive来写几百行的大SQL(底层是基于MapReduce的)。也有很多公司慢慢用Spark来写几百行的大SQL(底层是SparkCore引擎)。简而言之,就是写一个大的SQL,然后拆分成很多计算任务,放到每台机器上。每个计算任务负责计算一小部分数据。这就是所谓的分布式计算。这绝对比分库分表的MySQL跑几百行大SQL靠谱。至于上面提到的分布式存储和分布式计算,老规矩也给大家一张图,大家跟着图仔细回顾一下整个过程。HDFS的NameNode架构原理就讲完了。前奏说完,我们就进入正题。本文主要讨论HDFS集群中NameNode的核心架构原理。NameNode有一个非常核心的功能:管理整个HDFS集群的元数据,比如文件目录树、权限设置、副本数设置等等。下面就用最典型的文件目录树维护来举个例子。让我们看看下面的图片。现在有一个客户端系统要上传一个1TB的大文件到HDFS集群。这时候它会先和NameNode通信,说:大哥,我要新建一个文件,名字是“/usr/hive/warehouse/access_20180101.log”,大小是1TB,你觉得呢作品?然后NameNode会在自己内存的文件目录树中的指定目录下新建一个文件对象,名称为“access_20180101.log”。这个文件目录树不就是HDFS中很核心的一个元数据,维护着HDFS分布式文件系统中的目录和文件对吧?但是有个问题,这个文件目录树是在NameNode的内存中的!这是一个骗局,你把重要的元数据都放在内存里,万一NameNode不小心挂了怎么办?不是所有的元数据都丢失了吗?但是如果每次都频繁修改磁盘文件中的元数据,那性能肯定是极低的!毕竟这是大量的随机磁盘读写!没关系,我们来看看HDFS的优雅解决方案。每次在内存中完成修改,都会写入一个editslog,将元数据修改的操作日志保存到磁盘文件中。不修改磁盘文件的内容,而是顺序追加磁盘文件的内容。这个性能要高得多。每次NameNode重启,把editslog中的操作日志读到内存中回放,metadata能恢复吗?照着上面的文字,用下图来跟进整个过程:但是问题又来了,如果editslog越来越大,每次重启岂不是很慢?因为你必须阅读大量编辑日志回放才能恢复元数据!于是HDFS说,我可以这样,我引入一个新的磁盘文件叫fsimage,然后引入一个JournalNodes集群和一个StandbyNameNode(备用节点)。ActiveNameNode(主节点)每次修改元数据,都会产生一个editslog,除了写入本地磁盘文件外,还会写入JournalNodes集群。然后StandbyNameNode可以从JournalNodes集群中拉取editslog,应用到自己内存中的文件目录树中,与ActiveNameNode保持一致。然后每隔一段时间,StandbyNameNode将自己内存中的文件目录树的副本写入磁盘上的fsimage。这不是日志,而是一套完整的元数据。这个操作就是所谓的checkpoint检查点操作。然后将这个fsimage上传到ActiveNameNode,然后清除ActiveNameNode旧的editslog文件。这里可能有100万行的修改日志!然后ActiveNameNode继续接收修改元数据的请求,然后写入编辑日志。写了一会儿,这里的修改日志可能只有几十行!如果此时,ActiveNameNode重启,Bingo!没关系,只要将StandbyNameNode传过来的fsimage直接读入内存即可,这个fsimage直接就是metadata,不需要额外的操作,纯读取非常高效!然后把新的editslog里面的少量几十行的修改日志replay到内存里就OK了!这个过程开始得更快!因为不需要重放大量百万行的编辑日志来恢复元数据!如下所示。另外请看上图,现在我们有两个NameNode:一个是master节点,对外提供服务,接收请求。另一个是接收并同步主节点和执行定期检查点的备用节点的编辑日志。你发现了吗!两人记忆中的元数据,几乎一模一样!那么,如果ActiveNameNode宕机了,是否可以立即切换到StandbyNameNode对外提供服务呢?这不就是所谓的NameNode主备高可用故障切换机制吗!接下来,再想一想,HDFS客户端又在NameNode内存中的文件目录树中添加了一个新的文件。但是这时候,人们又想把数据上传到多台DataNode机器上。这是一个1TB的大文件!如何传承?很简单,将一个1TB的大文件拆分成N块,每块128MB。1TB=1024GB=1048576MB,一个block是128MB,那么对应8192个block。这些块将分布在不同的机器上进行管理。比如一个集群总共有100台机器,那么每台机器放80个左右的block就可以了。但是问题又来了,如果这时候一台机器宕机,那80个块不就丢了吗?也就是说,上传一个1TB的大文件,会丢失一小部分数据。没关系!HDFS统统考虑!默认情况下,它将为每个块制作3个副本。副本一模一样,分布在不同的机器上。如果一台机器出现故障,则在其他机器上还有同一个块的另外两个副本!看看下面的图片。每个block在不同的机器上有3份,任何机器下来都可以!您也可以从其他机器获取该块。现在,如果你上传一个1TB的大文件到HDFS,你就可以高枕无忧了!OK,以上就是白话加上一系列的手绘图,说说大家能看懂的Hadoop基本架构原理。HadoopNameNode是如何在大规模集群下承载每秒数千次的高并发访问上面我们已经为大家讲解了HadoopHDFS的整体架构原理,相信大家也有一定的了解和了解。下面我们来看一下,如果大量客户端发起高并发(比如每秒上千次)访问NameNode修改元数据,此时NameNode如何抵抗?问题根源我们先来分析一下高并发请求的NameNode会遇到什么样的问题。现在大家都知道了,每次请求NameNode修改一个元数据(比如申请上传一个文件,需要在内存目录树中添加一个文件),都要写一个editslog。它包括以下两个步骤:写入本地磁盘。通过网络传输到JournalNodes集群。但是如果对Java有一定了解的同学应该知道多线程并发的安全问题吧?NameNode写入editslog的第一个原则:必须保证每条editslog都有一个全局顺序递增的transactionId(简称txid),这样才能识别每条editslog的顺序。所以如果要保证每次editslog的txid都递增,就必须加锁。每个线程修改元数据,当要写入editslog时,必须排队获取锁,然后生成增量txid,代表本次要写入的editslog的序号。好了,那么问题来了,我们来看下图。如果每次都在一个锁定的代码块中生成txid,然后写入磁盘文件的editslog,网络请求写入JournalNodes的一个editslog怎么办?不用说,这个绝对搞砸了!NameNode本身使用多线程来接收来自多个客户端的并发请求。结果,修改了内存中的元数据后,多个线程排队写入editslog!而且你要知道写本地磁盘+网络传输到JournalNodes是非常耗时的!两大性能杀手:磁盘写入+网络写入!HDFS的架构如果真这么设计的话,基本上NameNode每秒能承载的并发数是很小的,可能每秒能处理几十个并发请求。HDFSElegantSolution所以,针对这个问题,HDFS做了很多优化!首先想一想,既然我们不想让每个线程都去写editslog,序列化排队生成txid+写磁盘+写JournalNode,能不能有个内存缓冲?也就是说,多个线程可以快速获取锁,生成txid,然后快速将editslog写入内存缓冲区。然后快速释放锁,让下一个线程继续获取锁,生成id+writeeditslog到内存缓冲区。然后有一个线程可以将内存中的editslog刷新到磁盘,但是在这个过程中,它仍然允许其他线程将editslog写入内存缓冲区。但是这里还有一个问题。如果有人同时写同一个内存缓冲区,有人同时读和写磁盘,那么也有问题,因为一块共享内存数据不能并发读写!所以HDFS这里采用double-buffer双缓冲机制来处理!将内存缓冲区分为两部分:一部分可以写入。另一部分用于读写磁盘和JournalNodes。大家可能会觉得文字描述不够直观,老规矩,先上图,按顺序给大家讲解一下。①分段锁机制+内存双缓冲机制首先,每个线程轮流第一次获取锁,产生顺序递增的txid,然后将editslog写入内存双缓冲的区域1,然后立即释放第一次锁定。利用这个间隙,后续线程可以立即再次第一次获取锁,然后立即将自己的editslog写入内存缓冲区。写内存这么快,可能只需要几十微秒,然后第一时间就立马释放锁。所以这个并发优化肯定是有效的。你感受到了吗?然后各个线程第二次竞争获取锁。一个线程获取到锁后,我们看看有没有人在写磁盘和网络?如果没有,好吧,那么这个线程是幸运的!直接交换双缓冲区域1和2,然后第二次释放锁。这个过程相当快,不到几微秒就可以判断出内存中的几个条件。好了,至此,内存缓冲区已经交换完毕,后续线程可以立即快速的一个一个获取锁,然后将editslog写入内存缓冲区的区域2。区域1中的数据被锁定,无法写入。怎么样,是不是又有点多线程并发优化的感觉了?②多线程并发吞吐百倍优化接下来,之前的幸运线程读取内存缓冲区1区的数据(此时没有人在写1区,都在写2区),edtisinside日志通过网络写入磁盘文件和JournalNodes集群。这个过程很费时间!不过没关系,人家已经优化过了,在写磁盘和网络的过程中不持有锁!所以后面的线程可以快速快速的第一次获取到锁,立即写入内存缓冲区的区域2,然后释放锁。这时候大量线程可以快速写入内存,不会阻塞,不会滞后!这个怎么样?感受并发优化的感觉吗?③批量数据闪存盘+网络优化然后在幸运线程向磁盘和网络写入数据的过程中,后面的大量线程快速的第一时间获取锁,写入内存缓冲区2,然后释放锁,之后这些线程第二次获取到锁后会做什么呢?他们会发现有人正在写入磁盘,伙计们!所以它会立即休眠1秒并释放锁。这时候如果有大量线程并发过来,就会快速在这里第二次获取锁,然后发现有人在写磁盘和网络,快速释放锁,休眠。这个过程怎么样,谁也挡不了别人半天!因为锁会很快释放,所以后续线程在第一次获取到锁后仍然可以快速写入内存缓冲区!再次!感受并发优化的感觉吗?而这个时候肯定有很多线程发现之前的幸运线程的txid好像排在了自己的后面,所以必须把自己的editslog从buffer写到磁盘和网络中。这些线程甚至不休眠和等待,它们只是回去做其他事情,它们根本不会卡在这里。在这里感受到并发的优化了吗?然后当幸运线程写完磁盘和网络后,它会唤醒那些之前处于休眠状态的线程。那些线程会在第二次获取到锁后依次排队进入判断,哎!发现没人再写磁盘和网络了!然后会判断后面有没有线程把它的edtis日志写到磁盘和网络:有就直接返回。如果没有,那就成为第二个幸运线程,交换两个缓冲区,交换区域1和区域2。然后释放锁,开始将区域2的数据写入磁盘和网络。不过此时无所谓。如果后续线程要写editslog,仍然可以在第一次获取锁后立即写内存缓冲区,然后释放锁。等等。综上所述,这个机制比较复杂,涉及到分段锁定和内存双缓冲两种机制。NameNode通过这种机制保证了在高并发下多线程修改元数据后写入editslog时,不会说一个线程一次一个线程写入磁盘和网络。那样性能太差,并发能力太弱!因此,通过上面提到的复杂机制,我们尽量保证一个线程可以将缓冲区中的多个编辑日志批量刷新到磁盘和网络。在这个漫长的过程中,其他线程可以快速、高并发地将editslog写入内存缓冲区,而不会阻塞其他线程写入editslog。因此,依靠上述机制,NameNode处理修改元数据的高并发访问的能力得到了最大化!中华石山:十余年BAT架构经验,一线互联网公司技术总监。带领数百人团队开发过亿级大流量高并发系统。多年工作积累的研究手稿和经验总结,现整理成文,一一传授。微信公众号:石山的建筑笔记(ID:shishan100)。
