近两年AI成了最火热领域的代名词,各大高校纷纷推出了人工智能专业。但其实,人工智能也好,仍是前两年的深度学习或者是机器学习也罢,都离不开底层的数据支持。对于动辄数以TB记级别的数据,显然常规的数据库是知足不了要求的。今天,咱们就来看看大数据时代的幕后英雄——Hadoop。
Hadoop这个关键词其实有两重含义,最先它其实指的就是单纯的分布式计算系统。可是随着时代的发展,Hadoop系统扩大,现在hadoop已是成了一个完整的技术家族。从底层的分布式文件系统(HDFS)到顶层的数据解析运行工具(Hive、Pig),再到分布式系统协调服务(ZooKeeper)以及分布式数据库(HBase),都属于Hadoop家族,几乎涵盖了大半大数据的应用场景。在Spark没有流行以前,Hadoop一直是大数据应用中的绝对主流,即便是如今,依旧有大量的中小型公司,仍是依靠Hadoop搭建大数据系统。
现在的Hadoop虽然家族庞大,可是早年Hadoop的结构很是简单,几乎只有两块,一块是分布式文件系统,这个是整个数据的支撑,另外一个就是MapReduce算法。
大数据时代,数据的量级大规模增加,动辄以TB甚至PB计。对于这么海量的数据,若是咱们还使用常规的方法是很是困难的。由于即便是 O(n) 的算法,将全部的数据遍历一遍,所消耗的时间也确定是以小时计,这显然是不能接受的。不只如此,像是MySQL这样的数据库对于数据规模也是有限制的,一旦数据规模巨大,超过了数据库的承载能力,那几乎是系统级的噩梦(重要的数据不能丢弃,可是如今的系统没法支撑)。
既然咱们把数据所有存储在一块儿,会致使系统问题,那么咱们可不能够把数据分红不少份分别存储,当咱们须要处理这些数据的时候,咱们对这些分红许多小份的数据分别处理,最后再合并在一块儿?
答案固然是可行的,Hadoop的文件系统正是基于这个思路。
在HDFS当中,将数据分割成一个一个的小份。每一个小份叫作一个存储块,每一个存储块为64MB。这样一个巨大的文件会被打散存储在许多存储块当中。当咱们须要操做这些数据的时候,Hadoop会同时起动许多个执行器(executor)来并发执行这些存储块。理论上来讲,执行器的数量越多,执行的速度也就越快。只要咱们有足够多的执行器,就能够在短期内完成海量数据的计算工做。
可是有一个小问题,为何每一个存储块恰恰是64MB,而不是128MB或者256MB呢?
缘由也很简单,由于数据存储在硬盘上,当咱们查找数据的时候,CPU实际上是不知道数据究竟存放在什么地方的。须要有一个专门的程序去查找数据的位置,这个过程被称为寻址。寻址的时候会伴随着硬盘的高速旋转。硬盘的旋转速度是有限的,天然咱们查找文件的速度也会存在瓶颈。若是存储块过小,那么存储块的数量就会不少,咱们寻址的时间就会变长。
若是存储块设置得大一些行不行?也不行,由于咱们在执行的时候,须要把存储块的数据拷贝到执行器的内存里执行。这个拷贝伴随着读写和网络传输的操做,传输数据一样耗时很多。存储块过大,会致使读写的时间过长,一样不利于系统的性能。根据业内的说法,但愿寻址的耗时占传输时间的1%,目前的网络带宽最多能够作到100MB/s,根据计算,每一个存储块大约在100MB左右最佳。也许是程序员为了凑整,因此选了64MB这个大小。
目前为止,咱们已经搞清楚了Hadoop内部的数据存储的原理。那么,Hadoop又是怎么并发计算的呢?这就下一个关键词——MapReduce出场了。
严格提及来MapReduce并非一种算法, 而是一个计算思想。它由map和reduce两个阶段组成。
先说map,MapReduce中的map和Java或者是C++以及一些其余语言的map容器不一样,它表示的意思是映射。负责执行map操做的机器(称做mapper)从HDFS当中拿到数据以后,会对这些数据进行处理,从其中提取出咱们须要用到的字段或者数据,将它组织成key->value的结构,进行返回。
为何要返回key->value的结构呢?直接返回咱们要用到的value不行吗?
不行,由于在map和reduce中间,Hadoop会根据key值进行排序,将key值相同的数据归并到一块儿以后,再发送给reducer执行。也就是说,key值相同的数据会被同一个reducer也就是同一台机器处理,而且key相同的数据连续排列。reducer作的是经过mapper拿到的数据,生成咱们最终须要的结果。
这个过程应该不难理解, 可是初学者可能面临困惑,为何一开始的时候,要处理成key-value结构的呢?为何又要将key值相同的数据放入一个reducer当中呢,这么作有什么意义?
MapReduce有一个经典的问题,叫作wordCount,顾名思义就是给定一堆文本,最后计算出文本当中每一个单词分别出现的次数。Map阶段很简单,咱们遍历文本当中的单词,每遇到一个单词,就输出单词和数字1。写成代码很是简单:
def map(text): for line in text: words = line.split(' ') for w in words: print(w, 1)
这样固然仍是不够的,咱们还须要把相同的单词聚合起来,清点一下看看究竟出现了多少次,这个时候就须要用到reducer了。reducer也很简单,咱们读入的是map输出的结果。因为key相同的数据都会进入同一个reducer当中,因此咱们不须要担忧遗漏,只须要直接统计就行:
def reduce(text): wordNow = None totCount = 0 for line in text: elements = line.split(' ') word, count = elements[0], int(elements[1]) # 碰到不一样的key,则输出以前的单词以及数量 if word != wordNow: if wordNow is not None: print(wordNow, totCount) wordNow = word totCount = 1 #不然,更新totCount else: totCount += count
若是咱们map的结果不是key-value结构,那么Hadoop就没办法根据key进行排序,并将key相同的数据归并在一块儿。那么咱们在reduce的时候,同一个单词就可能出如今不一样的reducer当中,这样的结果显然是不正确的。
固然,若是咱们只作一些简单的操做,也能够舍弃reduce阶段,只保留map产出的结果。
如今看MapReduce的思想其实并不复杂,可是当年大数据还未兴起的时候,MapReduce横空出世,既提高了计算性能,又保证告终果的准确。一举解决了大规模数据并行计算的问题,回想起来,应该很是惊艳。虽然现在技术更新,尤为是Spark的流行,抢走了Hadoop许多荣光。但MapReduce的思想依旧在许多领域普遍使用,好比Python就支持相似的MapReduce操做,容许用户自定义map和reduce函数,对数据进行并行处理。
不过,MapReduce也有短板,好比像是数据库表join的操做经过MapReduce就很难实现。并且相比于后来的Hive以及Spark SQL来讲,MapReduce的编码复杂度仍是要大一些。但无论怎么说,瑕不掩瑜,对于初学者而言,它依旧很是值得咱们深刻了解。