hadoop相关(以期为单位)

学习Hadoop不错的系列文章

1)Hadoop学习总结

  (1)HDFS简介

  地址:http://forfuture1978.iteye.com/blog/615033

  (2)HDFS读写过程解析

  地址:http://blog.csdn.net/forfuture1978/article/details/6001302

  (3)Map-Reduce入门

  地址:http://blog.csdn.net/forfuture1978/article/details/6043926

  (4)Map-Reduce的过程解析

  地址:http://blog.csdn.net/forfuture1978/article/details/6043929

  (5)Hadoop的运行痕迹

  地址:http://blog.csdn.net/forfuture1978/article/details/6028559

2)Hadoop-0.20.0源代码分析

  (1)Hadoop-0.20.0源代码分析(01)

  地址:http://blog.csdn.net/shirdrn/article/details/4569702

  (2)Hadoop-0.20.0源代码分析(02)

  地址:http://blog.csdn.net/shirdrn/article/details/4571445

  (3)Hadoop-0.20.0源代码分析(03)

  地址:http://blog.csdn.net/shirdrn/article/category/595039/3

  (4)Hadoop-0.20.0源代码分析(04)

  地址:http://blog.csdn.net/shirdrn/article/details/4574402

  (5)Hadoop-0.20.0源代码分析(05)

  地址:http://blog.csdn.net/shirdrn/article/details/4577243

  (6)Hadoop-0.20.0源代码分析(06)

  地址:http://blog.csdn.net/shirdrn/article/details/4581473

  (7)Hadoop-0.20.0源代码分析(07)

  地址:http://blog.csdn.net/shirdrn/article/details/4581666

  (8)Hadoop-0.20.0源代码分析(08)

  地址:http://blog.csdn.net/shirdrn/article/details/4581666

  (9)Hadoop-0.20.0源代码分析(09)

  地址:http://blog.csdn.net/shirdrn/article/details/4590359

  (10)Hadoop-0.20.0源代码分析(10)

  地址:http://blog.csdn.net/shirdrn/article/details/4594892

  (11)Hadoop-0.20.0源代码分析(11)

  地址:http://blog.csdn.net/shirdrn/article/details/4598295

  (12)Hadoop-0.20.0源代码分析(12)

  地址:http://blog.csdn.net/shirdrn/article/details/4598419

  (13)Hadoop-0.20.0源代码分析(13)

  地址:http://blog.csdn.net/shirdrn/article/details/4604229

  (14)Hadoop-0.20.0源代码分析(14)

  地址:http://blog.csdn.net/shirdrn/article/details/4608377

  (15)Hadoop-0.20.0源代码分析(15)

  地址:http://blog.csdn.net/shirdrn/article/details/4610578

  (16)Hadoop-0.20.0源代码分析(16)

  地址:http://blog.csdn.net/shirdrn/article/details/4631518

  (17)Hadoop-0.20.0源代码分析(17)

  地址:http://blog.csdn.net/shirdrn/article/details/4634119

  (18)Hadoop-0.20.0源代码分析(18)

  地址:http://blog.csdn.net/shirdrn/article/details/4636169

  (19)Hadoop-0.20.0源代码分析(19)

  地址:http://blog.csdn.net/shirdrn/article/details/4639345


Hadoop集群(第1期)_CentOS安装配置

 

1、准备安装

 

1.1 系统简介

  CentOS 是什么?

 

  CentOS是一个基于Red Hat 企业级 Linux 提供的可自由使用的源代码企业级的 Linux 发行版本。每个版本CentOS 都会获得七年支持(通过安全更新方式)。版本的 CentOS两年发行一次,而每个版本的 CentOS 会定期(大概每六个月)更新一次,以便支持新的硬件。这样,建立一个安全、低维护、稳定、高预测性、高重复性的 Linux 环境。

  CentOS(Community Enterprise Operating System)是Linux发行版之一,它是来自于Red Hat Enterprise Linux依照开放源代码规定释出的源代码所编译而成。由于出自同样的源代码,因此有些要求高度稳定性的服务器以CentOS替代商业版的Red Hat Enterprise Linux使用。两者不同,在于CentOS不包含封闭源代码软件

  CentOS是一个开源软件贡献者和用户的社区。它对RHEL源代码进行重新编译,成为众多发布新发行版本的社区当中的一个,并且在不断的发展过程中,CentOS社区不断与其他的同类社区合并,使CentOS Linux逐渐成为使用最广泛的RHEL兼容版本。CentOS Linux的稳定性不比RHEL差,唯一不足的就是缺乏技术支持,因为它是由社区发布的免费版。

  CentOS LinuxRHEL产品有着严格版本对应关系,例如使用RHEL4源代码重新编译发布的是CentOS Linux 4.0,RHEL5对应的是CentOS Linux 5.0,RHEL6对应的是CentOS Linux 6。由于RHEL产品的生命周期较长(通常具有3~5年的官方支持),因此Red Hat公司在RHEL系列产品发布后每隔一段时间,都会将累积的更新程序重新打包成为更新的发行版进行发布,通常称为RHEL Update。

  例如,RHEL5的第1个更新版本叫做RHEL 5 Update 1,用户通常也称为RHEL 5.1。对Red Hat公司发布的每一个RHEL Update CentOS社区都会发布对应的更新发行版,例如根据RHEL 5的Update 1更新程序源码包,CentOS会重新编译并打包发布CentOS Linux 5.1版。CentOS Linux和与之对应版本号的RHEL发行版具有软件包级别的二进制兼容性,即某个RPM软件包如果可以安装运行在RHEL产品中,就可以正常地安装运行在对应版本的CentOS Linux中。CentOS Linux由于同时具有与RHEL的兼容性和企业级应用的稳定性,又允许用户自由使用,因此得到了越来越广泛的应用。

 

  CentOS特点

  • 可以把CentOS理解为Red Hat AS系列!它完全就是对Red Hat AS进行改进后发布的!各种操作、使用和RED HAT没有区别!
  • CentOS完全免费,不存在RED HAT AS4需要序列号的问题。
  • CentOS独有yum命令支持在线升级,可以即时更新系统,不像RED HAT那样需要花钱购买支持服务!
  • CentOS修正了许多RED HAT AS的BUG!
  • CentOS版本说明: CentOS3.1 等同于 RED HAT AS3 Update1 CentOS3.4 等同于 RED HAT AS3 Update4 CentOS4.0 等同于 RED HAT AS4。

 

1.2 安装版本

  本次安装我们选择CetOS6.0版本,下面从几个方面介绍一下这个版本。

  • 集成基于内核的虚拟化。CentOS 6.0集成了基于内核的虚拟化,将KVM管理程序完全集成到内核中。这样的功能可以帮助CentOS 6.0用户在主机之间轻松迁移虚拟机,更加灵活地部署和管理IT资源,有效为企业节省资源。利用内核的硬件抽象使应用程序能够独立于底层硬件,并且提高 CPU和内存可扩展性,使每个服务器可容纳更多虚拟机。
  • 提升系统和资源管理功能。基于标准的Linux可管理性规范(SBLIM)使用基于Web的企业管理(WBEM)来管理系统。用Dracut取代mkinitrd,最大限度地减少底层硬件改变的影响,更易于维护,并使支持第三方驱动程序更容易。
  • 加强电源管理,按时的内核改进使CentOS 6.0可以将没有活动任务的处理器置为空闲状态,以达到降低CPU的温度和更进一步减少能耗。
  • 在一种叫做控制组(即cgroups)的新框架的帮助下CentOS 6.0提供对硬件资源的细颗粒度控制、分配和管理。cgroups运行在进程组水平上,可被用于为应用管理从CPU、内存、网络和硬盘I/O的资源。
  • 增强了系统的可靠性、可用性和适用性 。CentOS 6.0利用新硬件能力提供热插拔特性,并且可以通过AER的PCIe设备的增强错误检查。CentOS 6.0包括高级数据完整性特性(DIF/DIX)。这类特性通过硬件检查和检验来自应用的数据。自动缺陷报告工具(ABRT)的引进提供了确定和报告系统 异常情况,包括内核故障和用户空间应用崩溃等。
  • 改进了可伸缩性和内核性能 。CentOS 6.0提供了适应未来系统的可伸缩性,其可伸缩性能力从对大量CPU和内存配置的优化的支持到处理更多数量的系统互联总线和外设的能力。在虚拟化变得同裸机部署一样无处不在之时,这些能力适合于裸机环境和虚拟化环境。
  • CentOS 6.0改进了内核性能 ,可以通过让更高优先级的进程在最低限度的较低优先级处理干扰的条件下,更公平地在处理器之间分配计算时间。同时CentOS 6.0将多种多处理器锁同步进行改进,以消除不必要的锁定事件、用睡眠锁定代替许多旋转(spin)锁定和采用更高效的锁定基元。
  • 稳定的应用程序开发与生产平台 。CentOS 6.0是一个高性能、高度可扩展、分布式、基于内存的对象缓存系统,大大提高了动态Web应用程序的速度。在Web基础架构上主要改进了Apache、 Squid和Memcached三个方面的改进。在Java性能支持上,CentOS 6.0和OpenJDK的紧密集成包括在SystemTap中支持Java探测器,从而可支持更好的Java调试。同时,CentOS 6.0也在逐步完善Tomcat 6的支持。

 

  官方网站与文档

 

官方主页: http://www.centos.org/

官方Wiki: http://wiki.centos.org/

官方中文文档 :http://wiki.centos.org/zh/Documentation

安装说明: http://www.centos.org/docs/

 

1.3 硬件信息

  现在Linux发行版安装程序硬件识别能力都比较强,即使不查询什么硬件信息,安装操作系统也没有什么大问题,不过对于非常新的硬件和笔记本电脑这样比较特殊的设备,还是建议在网络上查询一下相关硬件的信息,以确保硬件可以被驱动。这里列举几种可能需要重点考察的硬件,对于任何一种发行版都需要注意如下这些问题。

 

  • 主板芯片组支持。这是一组比较重要的硬件信息,可能影响到硬盘是否可以使用、可以启用UDMA,是否可以使用ACPI或APM电源管理等。这里的支持程序总是和内核版本相关的,考察的时候要注意对应的内核版本。
  • 网络设备支持。目前,对于一般用户主要是以太网卡和无线网卡,有些无线网卡的驱动还没有被整合进内核,但确实已经有驱动支持了。
  • 声卡。Linux对声音芯片的支持在历史上饱受诟病,但自从ALSA(Advanced Linux Sound Architecture)诞生以来,这个问题已经得到了很大程序的缓解,现在,ALSA是2.6内核的一部分。不过,仍然有一些声卡问题不时地出现。特别的,目前大多数的用户使用的是主板板载声卡,在安装系统之前应该考察一下Linux内核对该主板芯片声效功能的支持。
  • 显卡。主流的显卡目前都可以得到支持,如果需要的话,看一下显卡芯片的厂商、型号、显存大小基本就可以了,如果希望省事的话,可以考虑选择性能虽然不强,但开源驱动的稳定性和性能最好的Intel整合显卡,而追求高性能显卡的用户可以考虑NVIDIA芯片并在日后使用官方的驱动程序,来获得更好的3D性能,ATI/ADM显卡目前的Linux驱动是不理想的,但AMD已经开放了芯片的技术规范,未来一定会得到开源驱动的良好支持。
  • 其他外设。USB键盘和鼠标一般都可以被支持,少数最低端的硬盘盒的移动硬盘可能不会被支持,摄像头这些小硬件大部分都可以被支持了,另外就是一些笔记本电脑,需要考察一下。

  为了有备无患,要考察的信息还是比较多的,除了上网查找和询问周围的朋友外,使用Live CD进行体验也是一个很不错的办法。

 

1.4 硬盘分区

  在计算机上安装Linux系统,对硬盘进行分区是一个非常重要的步骤,下面介绍几个分区方案。

  •  方案1(桌面

/boot:用来存放与Linux系统启动有关的程序,比如启动引导装载程序等,建议大小为100MB。

/:Linux系统的根目录,所有的目录都挂在这个目录下面,建议大小为5GB以上。

/home:存放普通用户的数据,是普通用户的宿主目录,建议大小为剩下的空间。

swap:实现虚拟内存,建议大小是物理内存的1~2倍。

 

  • 方案2(服务器

 

/boot:用来存放与Linux系统启动有关的程序,比如启动引导装载程序等,建议大小为100MB。

/usr:用来存放Linux系统中的应用程序,其相关数据较多,建议大于3GB以上。

/var:用来存放Linux系统中经常变化的数据以及日志文件,建议大于1GB以上。

/home:存放普通用户的数据,是普通用户的宿主目录,建议大小为剩下的空间。

/:Linux系统的根目录,所有的目录都挂在这个目录下面,建议大小为5GB以上。

/tmp:将临时盘在独立的分区,可避免在文件系统被塞满时影响到系统的稳定性。建议大小为500MB以上。

swap:实现虚拟内存,建议大小是物理内存的1~2倍。

 

2、光盘安装

 

2.1 安装引导

  首先要设置计算机的BIOS启动顺序为光驱启动,保存设置后将安装光盘放入光驱,重新启动计算机。

  计算机启动以后会出现如下图所示的界面。

 

 

  你可以直接按下<Enter>来进入图形界面安装方式或者等待60

  下面是上图所示引导菜单选项如下:

 

  • Install or upgrade an existing system(安装或升级现有系统):

  这个选项是默认的。 选择此选项,安装到您的计算机使用CentOS的图形安装程序的系统。

  • Install system with basic video driver(安装系统,基本的视频驱动程序):

  此选项允许您安装CentOS的模式,即使在图形安装程序无法加载视频卡的正确驱动程序为您的。如果你的屏幕上出现扭曲或一片空白时使用的安装或升级现有系统的选项,重新启动计算机,并尝试此选项。

  • Rescue installed system(救援安装的系统):

  选择这个选项来修复您的安装CentOS系统,防止正常启动你一个问题。虽然CentOS是一个非常稳定的计算平台,它是偶然的问题仍有可能发生,防止启动。 救援环境包含实用程序,允许您解决这些问题,品种繁多。

  • Boot from local drive(从本地驱动器启动):

  此选项将引导从第一个安装的磁盘系统。 如果你意外地启动这个光盘,使用未立即启动安装程序这是从硬盘启动选项。

 

2.2 检测光盘介质

  如下图所示,如果是一张完整的安装盘,可以直接单击"Skip"按钮跳过,否则单击"OK"按钮检测安装盘的完整性。

 

 

  备注:如果你确定你所下载的DVD或光盘没有问题的话,那么这里可以选择'Skip', 不过,你也可以按下'OK'来进行DVD的分析,因为通过DVD的分析后,后续的安装比较不会出现奇怪的问题。 不过如果你按下'OK'后,程式会开始分析光盘内的所有文件的资讯,会花非常多的时间喔!

 

2.3 安装欢迎界面

  当检测完电脑硬件信息后,进入安装欢迎界面,如下图所示。

 

 

2.4 选择安装过程中的语言

  单击"Next"按钮进入如下图所示的界面,选择安装过程中使用的语言,此处选择"Chinese (Simplified)(中文(简体))"。

 

 

2.5 选择键盘布局类型

  选择完安装过程中的语言后,单击"下一步"按钮进入如下图所示的界面,选择键盘类型一般默认会选择"美国英语式(U.S.English)",即美式键盘,在此使用默认的选择。

 

 

2.6 选择设备

  选择一种存储设备进行安装。"基本存储设备"作为安装空间的默认选择,适合哪些不知道应该选择哪个存储设备的用户。而"指定的存储设备"则需要用户将系统安装指定到特定的存储设备上,可以是本地某个设备,当然也可以是SAN(存储局域网)。用户一旦选择了这个选项,可以添加FCoE/iSCSI/zFCP磁盘,并且能够过滤掉安装程序应该忽略的设备。这里选择"基本存储设备",单击"下一步"按钮。

 

  备注基本存储设备:用于台式机和笔记本等等;指定存储设备:用于服务器等等。

 

2.7 初始化硬盘

  如果硬盘上没有找到分区表,安装程序会要求初始化硬盘。此操作使硬盘上的任何现有数据无法读取。如果您的系统具有全新的硬盘没有操作系统安装,或删除硬盘上的所有分区,则单击 "重新初始化"。

 

  备注:安装程序将为您提供一个单独的对话框,为每个磁盘,它无法读取一个有效的分区表。单击"忽略所有"按钮,或"重新初始化所有"按钮,可将应用到所有设备相同的答案。

 

2.8 设置主机名与网络

  安装程序会提示您提供和域名为这台计算机的主机名格式,设置主机名和域名 。 许多网络有DHCP(动态主机配置协议)服务,它会自动提供域名系统的一个连接,让用户输入一个主机名。除非您有特定需要定制的主机名和域名,默认设置 localhost.localdomain 是一个很好的选择大多数用户。我们这里按照下表进行填写主机名和域名,详情请看"Hadoop集群_第2期_机器信息分布表"。

 

表2.8-1 机器信息分布

 

机器名称

IP地址

Master.Hadoop 

192.168.1.2 

Salve1.Hadoop 

192.168.1.3 

Salve2.Hadoop 

192.168.1.4 

Salve3.Hadoop 

192.168.1.5

 

 

  设置固定IP

  选择『配置网络』à『有线』à『eth0』à『编辑』,弹出编辑窗口上选择"IPv4设置",打开"方法"边上的下拉菜单,选择"手动"。单击 "添加"按钮,依次输入本机的IP子网掩码网关。在下面的"DNS服务器"处输入DNS地址。最后,点击"应用"按钮即可。

 

  按照前面的表2.8-1进行配置IP地址。

 

2.9 时区选择

  因为全世界分为24个时区,所以,要告知系统时区在哪里。如下图所示,你可以选择北京,或直接用鼠标在地图上选择。要特别注意UTC,它与"夏令时"有关,我们不需要选择这个选项,否则会造成时区混乱,导致系统显示的时间与本地时间不同。

 

 

2.10 设置管理员密码

  下面是最重要的"系统管理员的口令"设置,如下图所示。在Linux中,系统管理员的默认名称为root,请注意,这个口令很重要。至少6个字符以上,含有特殊符号,并要记好。

 

  备注:当你设置好进入下一步时,由于你的密码可能设置的过于简单,此时会弹出一个对话框,如果你要坚持你设置的密码,可以选择"无论如何都是用"。

 

2.11 磁盘分区配置

  为方便大家分区硬盘,CentOS预设给了我们分区模式,分别为:

 

  备注:我们实际是直接选的是"使用所有空间",当时就是图方便,但实际工作中却要按照实际的要求对硬盘进行分区,合理利用硬盘。而这里选择"创建自定义布局",按照1.4小节给出的服务器分区方案对硬盘重新分区。

 

  下面是对每个选项的详细介绍:

 

  • 使用所有空间(Use All Space ):

  选择此选项,删除您硬盘上的所有分区(这包括如Windows的NTFS分区VFAT或其他操作系统创建的分区)。

  • 替换现有的Linux系统(Replace Existing Linux System):

  选择此选项,以消除先前的Linux安装创建的分区。 这不会删除其他分区(如VFAT或FAT32分区),你可能对您的硬盘驱动器。

  • 缩小现有系统(Shrink Current System):

  选择此选项,调整当前的数据和分区安装在手动释放的空间是一个默认的红帽企业Linux布局。

  • 使用剩余空间(Use Free Space):

  选择此选项以保留您当前的数据和分区并安装在未使用的存储驱动器上的空间可用的Scientific。 确保有足够的存储驱动器上的可用空间,然后再选择此选项。

  • 创建自定义布局(Create Custom Layout):

  选择此选项,手动存储设备进行分区并创建自定义布局。

 

  下面是"创建自定义布局"的步骤:

 

  第一步:选择"创建自定义布局",按"下一步"按钮;如下图所示。

 

  第二步:创建"/boot"

  选择要分区的空闲空间,按下"创建"后,就会出现如下的画面。选择"标准分区"后,点击"生成"。

 

  挂载点:选择"/boot";文件系统类型:使用默认"Ext4 日志文件系统";大小:输入分配的大小100,以 MB 为单位;其它大小选项:选择"固定大小";点"确定"按钮。

 

  第三步:创建"/"

  继续选择空闲空间,按下"创建"后,就会出现如下的画面。选择"标准分区"后,点击"生成"。

  挂载点:选择"/";文件系统类型:使用默认"Ext4 日志文件系统";大小:输入分配的大小5000,以 MB 为单位;其它大小选项:选择"固定大小";点"确定"按钮。

 

  第四步:创建交换空间

  继续选择空闲空间,点"创建"后,就会出现如下的画面。选择"标准分区"后,点击"生成"。

  文件系统类型:选择"swap";大小:输入分配的大小1000,以 MB 为单位;其它大小选项:选择"固定大小"。点"确定"按钮。

 

  备注:"交换空间"用于实现虚拟内存,建议大小是物理内存的1~2倍。

  第五步:创建"/usr"

  继续选择空闲空间,按下"创建"后,就会出现如下的画面。选择"标准分区"后,点击"生成"。

  挂载点:选择"/usr";文件系统类型:使用默认"Ext4 日志文件系统";大小:输入分配的大小3000,以 MB 为单位;其它大小选项:选择"固定大小";点"确定"按钮。

 

  第六步:创建"/var"

  继续选择空闲空间,按下"创建"后,就会出现如下的画面。选择"标准分区"后,点击"生成"。

  挂载点:选择"/var";文件系统类型:使用默认"Ext4 日志文件系统";大小:输入分配的大小1000,以 MB 为单位;其它大小选项:选择"固定大小",点"确定"按钮。

  备注:图与第五步基本相同,故略。

  第七步:创建"/tmp"

  继续选择空闲空间,按下"创建"后,就会出现如下的画面。选择"标准分区"后,点击"生成"。

  挂载点:选择"/tmp";文件系统类型:使用默认"Ext4 日志文件系统";大小:输入分配的大小500,以 MB 为单位;其它大小选项:选择"固定大小",点"确定"按钮。

  备注:图与第五步基本相同,故略。

  第八步:"/home"分区

  继续选择空闲空间,点"创建"后,就会出现如下的画面。选择"标准分区"后,点击"生成"。

  挂载点:选择"/home";文件系统类型:使用默认"Ext4 日志文件系统";其它大小选项:选择"使用全部可用空间",点"确定"按钮。

 

 

  至此,分区已全部创建完毕,如果不满意,还可以点击"重设"按钮进行更改。如果确定,就点"下一步"按钮后,弹出"是否格式化以下已存在的硬盘",选择"格式化"。

 

 

  安装程序会提示您确认您所选的分区选项。单击"将修改写入磁盘",以允许安装程序在您的硬盘进行分区,并安装系统更改。

 

 

2.12引导装载程序设置

  如下图所示为GRUB引导安装窗口,可采用默认设置,直接单击"下一步"按钮。

 

  备注:设置引导装载程序密码的方法是,选择"使用引导装载程序口令",在弹出的窗口中输入密码。我们实际并没有设置此项

 

2.13选择安装的软件包

  可选的服务器类型更多,而且默认安装是一个非常小的甚至不完整的系统。选中"现在自定义",然后单击"下一步"按钮,如下图所示。

 

 

  可选的类型说明如下:

  • Desktop:基本的桌面系统,包括常用的桌面软件,如文档查看工具。
  • Minimal Desktop:基本的桌面系统,包含的软件更少。
  • Minimal:基本的系统,不含有任何可选的软件包。
  • Basic Server:安装的基本系统的平台支持,不包含桌面。
  • Database Server:基本系统平台,加上MySQL和PostgreSQL数据库,无桌面。
  • Web Server:基本系统平台,加上PHP,Web server,还有MySQL和PostgreSQL数据库的客户端,无桌面。
  • Virtual Host:基本系统加虚拟平台。
  • Software Development Workstation:包含软件包较多,基本系统,虚拟化平台,桌面环境,开发工具。

 

  备注:我们这里安装Linux是作为服务器使用,没有必要安装图形界面,故选择的是最小化安装(Minimal),如果想安装图形界面,可以选择桌面安装(Desktop)。

因为上一步我们选择的是最小化安装(Minimal),所以在以下软件包设置画面中,所有的软件包默认都是没有被选中的状态。这里我们只选择我们需要的软件包来安装。

 

  首先,选中「基本系统」中的「基本」。

 

 

  然后,选中「开发」中的「开发工具」。

 

 

  最后,在「语言支持」中选中「中文支持」。如果您有支持其他语言的需要,也可以在这里一并选择。最后点击"下一步",开始安装。

 

 

2.14开始安装Linux系统

  开始安装。在安装的画面中,会显示还需要多少时间,每个软件包的名称,以及该软件包的简单说明,如下图所示。

 

 

  等到安装完之后,一切就都完成了。出现最后这个画面时,请将光盘拿出来,并按下"重新引导"按钮去启动,如下图所示。

 

 

3、环境设置

  在CentOS 6刚刚安装好之后,默认的安全设置以及服务启动等等可能并不符合我们的需求,所以在搭建各种系统服务之前,我们先将系统环境设置为最为简洁的状态,在以后开通各种服务的时候,在按照服务的需求来进行具体的定制。

 

3.1 建立一般用户

  root 用户拥有控制整个系统最高权限,如果在一般的操作中我们用 root 来进行,很容易不小心对系统造成误删、误更改等操作,所以我们首先建立一般用户,用一般用户来应付日常的系统操作。

 

  

  在一般用户建立成功以后,我们就可以用一般用户来登录系统进行日常的系统管理。在一些需要 root 权限的情况,我们可以通过以下方式在一般用户的登录状态下直接登录为 root 用户来进行需要 root 权限的操作。

 

 

  如以上示范,我们可以在一般用户登录的状态下通过"su -"命令来直接登录为 root 用户。

 

3.2 关闭防火墙及SELinux

  本站文档是假定「服务器在防火墙以内」的前提下编纂的,在这个前提下,服务器本身在内部网络是不需要防火墙的(在需要抵御内部安全威胁的情况下,您也可以用您在 iptables 方面的相关知识,根据实情设置防火前,并保证服务器端防火墙启动的状态)。另外,在一般情况下,我们不需要 SELinux 复杂的保护,所以也将其设置为无效的状态。

 

  关闭防火墙

 

 

  关闭SELinux

   

  用下面命令执行,并修改内容如下所示:

 

vim /etc/sysconfig/selinux

 

SELINUX=enforcing

SELINUX=disabled

 

 

  接着在执行如下命令:

 

setenforce 0

getenforce

 

 

3.3 关闭不需要的服务

  使用下面命令进行关闭不需要的服务:

 

for SERVICES in abrtd acpid auditd avahi-daemon cpuspeed haldaemon mdmonitor messagebus udev-post; do chkconfig ${SERVICES} off; done

 

 

  备注:该文档由于是事后很久才编写,当时在安装Linux的时候,所以本节的3.2和3.3并没有进行,以至于后来遇到不少麻烦,所以在这里把这些遗漏的补上。另外本文档所有的截图并不是当时实际安装,而是为了编写此文档在虚拟机上安装时截的图,所以个别图上的信息反映的是虚拟机的硬件信息,在实际中则不一样,安装时请注意细微差别。

 

  文章下载地址:http://files.cnblogs.com/xia520pi/HadoopCluster_Vol.1.rar


Hadoop集群(第2期)_机器信息分布表

1、分布式环境搭建

  采用4台安装Linux环境的机器来构建一个小规模的分布式集群。

图1 集群的架构

 

  其中有一台机器是Master节点,即名称节点,另外三台是Slaver节点,即数据节点。这四台机器彼此间通过路由器相连,从而实验相互通信以及数据传输。它们都可以通过路由器访问Internet,实验网页文档的采集。

2、集群机器详细信息

2.1 Master服务器

名称

详细信息

机器名称

Master.Hadoop

机器IP地址

192.168.1.2

最高用户名称(Name

root 

最用用户密码(PWD

hadoop(全小写)

一般用户名称(Name

hadoop(全小写)

一般用户密码(PWD

hadoop(全小写)

2.2 Slave1服务器

名称

详细信息

机器名称

Slave1.Hadoop 

机器IP地址

192.168.1.3 

最高用户名称(Name

root 

最用用户密码(PWD

hadoop(全小写)

一般用户名称(Name

hadoop(全小写)

一般用户密码(PWD

hadoop(全小写)

 

2.3 Slave2服务器

名称

详细信息

机器名称

Slave2.Hadoop 

机器IP地址

192.168.1.4 

最高用户名称(Name

root 

最用用户密码(PWD

hadoop(全小写)

一般用户名称(Name

hadoop(全小写)

一般用户密码(PWD

hadoop(全小写)

 

2.4 Slave3服务器

名称

详细信息

机器名称

Slave3.Hadoop 

机器IP地址

192.168.1.5 

最高用户名称(Name

root 

最用用户密码(PWD

hadoop(全小写)

一般用户名称(Name

hadoop(全小写)

一般用户密码(PWD

hadoop(全小写)

 

  备注:

      添加新用户命令:useradd;修改新用户密码:passwd 用户名

      退出当前用户:exit;         登录root用户:su –

3、集群机器实际布局

  下面是几张Hadoop集群实际机器的部署情况,可以从图中看到那时的我们怎么实际配置Hadoop集群的。

 

3.1 Hadoop工作集群

  该Hadoop集群机器是学习和研究之用,上面运行着已经搭建好的的Hadoop平台以及运行着一些实际程序。

 

图3.1-1 Hadoop工作集群部署图(1)

 

图3.1-2 Hadoop工作集群部署图(2)

  上面从两个角度来分别观察Hadoop集群部署,作为Hadoop集群的Master节点,用的是机器较为不错的Dell,而三台Slave机器则是实验室淘汰的Lenovo,用一个小路由器他们组成了一个局域网。

 

图3.1-3 路由器特写

3.2 Hadoop实验集群

  为了方便新成员练习Hadoop技术,又防止在实际Hadoop集群上破坏已运行的程序,故另外弄了两台旧Lenovo电脑组成:一个"主——TMaster";一个"辅——TSlave"。

 

图3.2-1 Hadoop实验集群部署

 

  上面就是实验室Hadoop集群的样子,虽然很简陋,但足够学习用了。

 

  文章下载地址:http://files.cnblogs.com/xia520pi/HadoopCluster_Vol.2.rar

Hadoop集群(第3期)_VSFTP安装配置

  

1、VSFTP简介

  VSFTP是一个基于GPL发布的类Unix系统上使用的FTP服务器软件,它的全称是Very Secure FTP 从此名称可以看出来,编制者的初衷是代码的安全。

  安全性是编写VSFTP的初衷,除了这与生俱来的安全特性以外,高速与高稳定性也是VSFTP的两个重要特点。

  在速度方面,使用ASCII代码的模式下载数据时,VSFTP的速度是Wu-FTP的两倍,如果Linux主机使用2.4.*的内核,在千兆以太网上的下载速度可达86MB/S。

  在稳定方面,VSFTP就更加的出色,VSFTP在单机(非集群)上支持4000个以上的并发用户同时连接,根据Red Hat的Ftp服务器(ftp.redhat.com)的数据,VSFTP服务器可以支持15000个并发用户。

 

2、VSFTP安装及配置

    安装该软件需要使用最高用户(root)进行安装,否则不能进行。

 

 

2.1 安装

  首先用命令检查VSFTP是否已经安装。

 

chkconfig –list | grep vsftpd

    

  显示结果如下,没有任何反应,说明没有安装VSFTP。

 

 

  接着使用yum命令直接安装

 

yum –y install vsftpd

 

 

  上图中表示正在下载,需要耐心等一下,如果网络不畅通,也可能需要下载失败,不过不要担心,只要在重新提交一次命令就行。

 

 

  从上面的结果中看出,已经成功安装。然后为它创建日志文件:

 

touch /var/log/vsftpd.log

 

 

  这样简单的两个命令就完成了vsftp的安装,但是如果你现在想这样ftp://your_ip来访问的话,那还不行,还需要配置权限!

 

2.2 启动与配置自启动

  再次使用"chkconfig –list | grep vsfpd"来查看vsftpd服务启动项情况;

  如果看到的是如下显示的结果:

 

 

  服务全部都是关闭(off)的,注意这里的关闭(off)表示的是服务器启动的时候是否会自启动服务,我们使用如下命令来配置其自启动:

 

chkconfig vsftpd on

 

  或者

 

chkconfig –level 2345 vsftpd on

 

  执行结果如下:

 

 

  查看与管理ftp服务:

 

启动ftp服务:service vsftpd start

查看ftp服务状态:service vsftpd status

重启ftp服务:service vsftpd restart

关闭ftp服务:service vsftpd stop

 

2.3 配置vsfpd服务

  编辑/etc/vsftpd/vsftpd.conf文件,配置vsftp服务:

 

vim /etc/vsftpd/vsftpd.conf

 

 

  上图中先显示出"/etc/vsftpd"下面有哪些文件,然后在用"vim"编辑器进行修改。

 

   先按键盘上的"a"就可以进行编辑了,按照下面进行操作。

 

anonymous_enable=YES --> anonymous_enable=NO //不允许匿名用户访问,默认是允许

xferlog_file=/var/log/vsftpd.log #设定vsftpd的服务日志保存路径。注意,该文件默认不存在。必须要手动touch出来

#idle_session_timeout=600 --> idle_session_timeout=600 //会话超时,客户端连接到ftp但未操作,默认被注释掉,可根据个人情况修改

#async_abor_enable=YES --> async_abor_enable=YES //支持异步传输功能,默认是注释掉的,去掉注释

#ascii_upload_enable=YES --> ascii_upload_enable=YES //支持ASCII模式的下载功能,默认是注释掉的,去掉注释

#ascii_download_enable=YES --> ascii_download_enable=YES //支持ASCII模式的上传功能,默认是注释掉的,去掉注释

#ftpd_banner=Welcome to blah FTP service //FTP的登录欢迎语,本身是被注释掉的,去不去都行

#chroot_local_user=YES --> chroot_local_user=YES

//禁止本地用户登出自己的FTP主目录,本身被注释掉,去掉注释

 

  下面几个都是已经去掉的,只是写出来,明白其用意。

 

local_enable=YES //允许本地用户访问,默认就是YES,不用改

write_enable=YES //允许写入,默认是YES,不用改

local_umask=022 //上传后文件的权限掩码,不用改

dirmessage_enable=YES //开启目录标语,默认是YES,开不开无所谓,我是默认就行

xferlog_enable=YES //开启日志,默认是YES,不用改

connect_from_port_20=YES //设定连接端口20

xferlog_std_format=YES //设定vsftpd的服务日志保存路径,不用改

 

pam_service_name=vsftpd //设定pam服务下vsftpdd的验证配置文件名,不用改

userlist_enable=YES //拒绝登录用户名单,不用改

TCP_wrappers=YES //限制主机对VSFTP服务器的访问,不用改(通过/etc/hosts.deny和/etc/hosts.allow这两个文件来配置)

 

    按照上面修改完之后,按键盘"Esc"退出编辑,再按":",并在后面输入"wq",进行保存并退出。

 

2.4 配置iptables防火墙

  按照以上步骤还不能运行,用下面命令可以查看一下防火墙允许的端口号,我们知道ftp的端口号是"20、21",从结果中可以看出,并没有。

 

service ipstables status

 

 

   这时需要对"/etc/sysconfig/iptables"进行配置:

 

vim /etc/sysconfig/iptables

 

 

  上图中先列出以"iptables"模糊比配的文件,然后在用上面的命名对"iptables"文件进行添加下面的内容。

 

-A INPUT -m state --state NEW -m tcp -p tcp --dport 21 -j ACCEPT

-A INPUT -m state --state NEW -m tcp -p tcp --dport 20 -j ACCEPT

 

  具体的添加操作命令和前面对"/etc/vsftpd/vsftpd.conf"的操作一样。

 

 

2.5 设置selinux问题

  SELinux(Security-Enhanced Linux) 是美国国家安全局(NSA)对于强制访问控制的实现,是 Linux史上最杰出的新安全子系统。SELinux 是 2.6 版本的 Linux 内核中提供的强制访问控制 (MAC)系统。对于目前可用的 Linux 安全模块来说,SELinux 是功能最全面,而且测试最充分的,它是在 20 年的 MAC 研究基础上建立的。SELinux 在类型强制服务器中合并了多级安全性或一种可选的多类策略,并采用了基于角色的访问控制概念SELinux提供了比传统的UNIX权限更好的访问控制。

  正式如此,所以此时还不能访问FTP服务器。用下面命令查询:

 

getsebool -a | grep ftp

 

 

  可以通过下面命令把上图中红色圈住的两个的值设置为"on"。

 

setsebool -P ftp_home_dir 1

setsebool -P allow_ftpd_full_access 1

 

 

2.6 重启相关服务

  最后就是把"vsftpd"和"iptables"两个服务重新启动一下。

 

service vsftpd restart

 

 

  如果出现下图,说明刚才没有把vsftpd服务启动起来。

 

service vsftpd start

 

 

  最后把防火墙重新启动一下。

service iptables restart

 

 

  到此为止FTP服务器就搭建完毕了,下面可以通过"FlashFXP.exe"软件进行访问了。

 

3、FlashFXP使用

 

3.1 FlashFXP简介

 

  FlashFXP是一款功能强大的FXP/FTP软件,集成了其它优秀的FTP软件的优点,如CuteFTP的目录比较,支持彩色文字显示;如BpFTP支持多目录选择文件,暂存目录;又如LeapFTP的界面设计。支持目录(和子目录)的文件传输,删除;支持上传,下载,以及第三方文件续传;可以跳过指定的文件类型,只传送需要的本件;可自定义不同文件类型的显示颜色;暂存远程目录列表,支持FTP代理及Socks 3&4;有避免闲置断线功能,防止被FTP平台踢出;可显示或隐藏具有"隐藏"属性的文档和目录;支持每个平台使用被动模式等。

 

 

  上图为FlashFXP开启界面,左面是显示的"本地文件系统",后面是显示"服务器文件系统",点击右侧上面的闪电图标,按照下图选择"Quick Connect"。

 

  点击之后出现下图,并按照下图进行填写。例如:

 

 

 

3.2 示例上传

 

 

   链接成功后,会显示上面的内容,可以尝试把左边的一个文件拖到右边,就能上传了,我们现在试一下。

 

 

  最后登录一下看看是否在linux下面有这个文件。

 

 

   从上图中发现了我们刚才上传的文件"SqlHelper1.cs"文件,好了,到此为止,我们的Linux下的FTP服务器基本完成了, 但是离真正的FTP服务器还差的很远,因为我们只是用于"远程上传"点文件,所以基本能满足我们的需要。

 

4、用到的Linux命令

4.1 chkconfig命令详解

  chkconfig命令主要用来更新启动停止)和查询系统服务的运行级信息。谨记chkconfig不是立即自动禁止或激活一个服务,它只是简单的改变了符号连接。

 

  使用语法:

 

chkconfig [--add][--del][--list][系统服务] 或 chkconfig [--level <等级代号>][系统服务][on/off/reset]

 

  chkconfig在没有参数运行时,显示用法。如果加上服务名,那么就检查这个服务是否在当前运行级启动。如果是,返回true,否则返回false。如果在服务名后面指定了on,off或者reset,那么chkconfig会改变指定服务的启动信息。on和off分别指服务被启动和停止,reset指重置服务的启动信息,无论有问题的初始化脚本指定了什么。on和off开关,系统默认只对运行级3,4,5有效,但是reset可以对所有运行级有效。

 

  参数用法:

 

--add 增加所指定的系统服务,让chkconfig指令得以管理它,并同时在系统启动的叙述文件内增加相关数据。

--del 删除所指定的系统服务,不再由chkconfig指令管理,并同时在系统启动的叙述文件内删除相关数据。

--level<等级代号> 指定读系统服务要在哪一个执行等级中开启或关毕。

 

等级0表示:表示关机

等级1表示:单用户模式

等级2表示:无网络连接的多用户命令行模式

等级3表示:有网络连接的多用户命令行模式

等级4表示:不可用

等级5表示:带图形界面的多用户模式

等级6表示:重新启动

 

  需要说明的是,level选项可以指定要查看的运行级而不一定是当前运行级。对于每个运行级,只能有一个启动脚本或者停止脚本。当切换运行级时,init不会重新启动已经启动的服务,也不会再次去停止已经停止的服务。

 

chkconfig --list [name]:显示所有运行级系统服务的运行状态信息(on或off)。如果指定了name,那么只显示指定的服务在不同运行级的状态。

chkconfig --add name:增加一项新的服务。chkconfig确保每个运行级有一项启动(S)或者杀死(K)入口。如有缺少,则会从缺省的init脚本自动建立。

chkconfig --del name:删除服务,并把相关符号连接从/etc/rc[0-6].d删除。

chkconfig [--level levels] name:设置某一服务在指定的运行级是被启动,停止还是重置。

 

  运行级文件:

 

  每个被chkconfig管理的服务需要在对应的init.d下的脚本加上两行或者更多行的注释。第一行告诉chkconfig缺省启动的运行级以及启动和停止的优先级。如果某服务缺省不在任何运行级启动,那么使用 - 代替运行级。第二行对服务进行描述,可以用\ 跨行注释。

 

  例如,random.init包含三行:

# chkconfig: 2345 20 80

# description: Saves and restores system entropy pool for \

# higher quality random number generation.

 

  使用范例:

 

chkconfig --list #列出所有的系统服务

chkconfig --add httpd #增加httpd服务

chkconfig --del httpd #删除httpd服务

chkconfig --level httpd 2345 on #设置httpd在运行级别为2、3、4、5的情况下都是on(开启)的状态

chkconfig --list #列出系统所有的服务启动情况

chkconfig --list mysqld #列出mysqld服务设置情况

chkconfig --level 35 mysqld on #设定mysqld在等级3和5为开机运行服务,--level 35表示操作只在等级3和5执行,on表示启动,off表示关闭

chkconfig mysqld on #设定mysqld在各等级为on,"各等级"包括2、3、4、5等级

 

  如何增加一个服务:

 

1.服务脚本必须存放在/etc/ini.d/目录下;

2.chkconfig --add servicename

在chkconfig工具服务列表中增加此服务,此时服务会被在/etc/rc.d/rcN.d中赋予K/S入口了;

3.chkconfig --level 35 mysqld on

修改服务的默认启动等级。

 

4.2 yum命令详解

  yum(全称为 Yellow dog Updater, Modified)是一个在Fedora和RedHat以及SUSE中的Shell前端软件包管理器。基於RPM包管理,能够从指定的服务器自动下载RPM包并且安装,可以自动处理依赖性关系,并且一次安装所有依赖的软体包,无须繁琐地一次次下载、安装。yum提供了查找、安装、删除某一个、一组甚至全部软件包的命令,而且命令简洁而又好记。

 

  yum的命令形式一般是如下:yum [options] [command] [package ...]

 

  其中的[options]是可选的,选项包括-h(帮助),-y(当安装过程提示选择全部为"yes"),-q(不显示安装的过程)等等。[command]为所要进行的操作,[package ...]是操作的对象。

 

  概括了部分常用的命令包括:

 

  自动搜索最快镜像插件: yum install yum-fastestmirror

  安装yum图形窗口插件: yum install yumex

  查看可能批量安装的列表: yum grouplist

 

  1 安装

 

yum install 全部安装

yum install package1 安装指定的安装包package1

yum groupinsall group1 安装程序组group1

 

  2 更新和升级

 

yum update 全部更新

yum update package1 更新指定程序包package1

yum check-update 检查可更新的程序

yum upgrade package1 升级指定程序包package1

yum groupupdate group1 升级程序组group1

 

  3 查找和显示

 

yum info package1 显示安装包信息package1

yum list 显示所有已经安装和可以安装的程序包

yum list package1 显示指定程序包安装情况package1

yum groupinfo group1 显示程序组group1信息yum search string 根据关键字string查找安装包

 

  4 删除程序

 

yum remove package1 删除程序包package1

yum groupremove group1 删除程序组group1

yum deplist package1 查看程序package1依赖情况

 

  5 清除缓存

 

yum clean packages 清除缓存目录下的软件包

yum clean headers 清除缓存目录下的 headers

yum clean oldheaders 清除缓存目录下旧的 headers

yum clean, yum clean all (= yum clean packages; yum clean oldheaders) 清除缓存目录下的软件包及旧的headers

 

  比如,要安装游戏程序组,首先进行查找:

  #:yum grouplist

  可以发现,可安装的游戏程序包名字是"Games and Entertainment",这样就可以进行安装:

  #:yum groupinstall "Games and Entertainment"

  所 有的游戏程序包就自动安装了。在这里Games and Entertainment的名字必须用双引号选定,因为linux下面遇到空格会认为文件名结束了,因此必须告诉系统安装的程序包的名字是"Games and Entertainment"而不是"Games"。

 

  此外,还可以修改配置文件/etc/yum.conf选择安装源。可见yum进行配置程序有多方便了吧。更多详细的选项和命令,当然只要在命令提示行下面:man yum

 

4.3 SELinux两个命令

  getsebool与setsebool工具

 

  说明:SELinux规范了许多boolean数值清单档案,提供开启或关闭功能存取项目,而这些值都存放在/selinux/booleans/目录内相关档案,这些档案里的值只有两种:1(启用)或 0(关闭)

 

  1)getsebool

 

  说明:列出所有selinux bool数值清单表与内容

  使用方式:getsebool [ -a ]

  例如以下范例:

 

#getsebool ftpd_disable_trans

ftpd_disable_trans –> off

#getsebool -a

NetworkManager_disable_trans –> off

allow_cvs_read_shadow –> off

allow_daemons_dump_core –> on

allow_daemons_use_tty –> off

allow_execheap –> off

allow_execmem –> on

allow_execmod –> off

………

 

2)setsebool

 

  说明:设定selinux bool数值清单表与内容

  使用方式:setsebool [ -P ] boolean value | bool1=val1 bool2=val2 bool3=val3……

  参数配置: -P表示设定该项目永久套用

  使用范例:

setsebool ftpd_disable_trans=on ( on 或者 1 )

setsebool -P ftpd_disable_trans=off ( off 或者 0 )

 

 

4.4 vim命令详解

  vi编辑器是所有Unix及Linux系统下标准的编辑器,它的强大不逊色于任何最新的文本编辑器,这里只是简单地介绍一下它的用法和一小部分指令。由于对Unix及Linux系统的任何版本,vi编辑器是完全相同的,因此您可以在其他任何介绍vi的地方进一步了解它。Vi也是Linux中最基本的文本编辑器,学会它后,您将在Linux的世界里畅行无阻。

 

  1、vi的基本概念

 

  基本上vi可以分为三种状态,分别是命令模式(command mode)、插入模式(Insert mode)和底行模式(last line mode),各模式的功能区分如下:

  1) 命令行模式command mode)

  控制屏幕光标的移动,字符、字或行的删除,移动复制某区段及进入Insert mode下,或者到 last line mode。

 

  2) 插入模式(Insert mode)

  只有在Insert mode下,才可以做文字输入,按「ESC」键可回到命令行模式。

 

  3) 底行模式(last line mode)

  将文件保存或退出vi,也可以设置编辑环境,如寻找字符串、列出行号……等。

  不过一般我们在使用时把vi简化成两个模式,就是将底行模式(last line mode)也算入命令行模式command mode)。

 

2、vi的基本操作

 

  a) 进入vi

  在系统提示符号输入vi及文件名称后,就进入vi全屏幕编辑画面:

 

$ vi myfile

 

  不过有一点要特别注意,就是您进入vi之后,是处于「命令行模式(command mode)」,您要切换到「插入模式(Insert mode)」才能够输入文字。初次使用vi的人都会想先用上下左右键移动光标,结果电脑一直哔哔叫,把自己气个半死,所以进入vi后,先不要乱动,转换到「插入模式(Insert mode)」再说吧!

 

  b) 切换至插入模式(Insert mode)编辑文件

  在「命令行模式(command mode)」下按一下字母「i」就可以进入「插入模式(Insert mode)」,这时候你就可以开始输入文字了。

 

  c) Insert 的切换

  您目前处于「插入模式(Insert mode)」,您就只能一直输入文字,如果您发现输错了字!想用光标键往回移动,将该字删除,就要先按一下「ESC」键转到「命令行模式(command mode)」再删除文字。

 

  d) 退出vi及保存文件

  在「命令行模式(command mode)」下,按一下「:」冒号键进入「Last line mode」,例如:

 

: w filename (输入 「w filename」将文章以指定的文件名filename保存)

: wq (输入「wq」,存盘并退出vi)

: q! (输入q!, 不存盘强制退出vi)

 

3、命令行模式(command mode)功能键

 

  1)插入模式

  按「i」切换进入插入模式「insert mode」,按"i"进入插入模式后是从光标当前位置开始输入文件;

  按「a」进入插入模式后,是从目前光标所在位置的下一个位置开始输入文字;

  按「o」进入插入模式后,是插入新的一行,从行首开始输入文字。

 

  2)从插入模式切换为命令行模式

  按「ESC」键。

 

  3)移动光标

  vi可以直接用键盘上的光标来上下左右移动,但正规的vi是用小写英文字母「h」、「j」、「k」、「l」,分别控制光标左、下、上、右移一格。

 

  按「ctrl」+「b」:屏幕往"后"移动一页。

  按「ctrl」+「f」:屏幕往"前"移动一页。

  按「ctrl」+「u」:屏幕往"后"移动半页。

  按「ctrl」+「d」:屏幕往"前"移动半页。

  按数字「0」:移到文章的开头。

  按「G」:移动到文章的最后。

  按「$」:移动到光标所在行的"行尾"。

  按「^」:移动到光标所在行的"行首"

  按「w」:光标跳到下个字的开头

  按「e」:光标跳到下个字的字尾

  按「b」:光标回到上个字的开头

  按「#l」:光标移到该行的第#个位置,如:5l,56l。

 

  4)删除文字

  「x」:每按一次,删除光标所在位置的"后面"一个字符。

  「#x」:例如,「6x」表示删除光标所在位置的"后面"6个字符。

  「X」:大写的X,每按一次,删除光标所在位置的"前面"一个字符。

  「#X」:例如,「20X」表示删除光标所在位置的"前面"20个字符。

  「dd」:删除光标所在行。

  「#dd」:从光标所在行开始删除#行

 

  5)复制

  「yw」:将光标所在之处到字尾的字符复制到缓冲区中。

  「#yw」:复制#个字到缓冲区

  「yy」:复制光标所在行到缓冲区。

  「#yy」:例如,「6yy」表示拷贝从光标所在的该行"往下数"6行文字。

  「p」:将缓冲区内的字符贴到光标所在位置。注意:所有与"y"有关的复制命令都必须与"p"配合才能完成复制与粘贴功能。

 

  6)替换

  「r」:替换光标所在处的字符。

  「R」:替换光标所到之处的字符,直到按下「ESC」键为止。

 

  7)回复上一次操作

  「u」:如果您误执行一个命令,可以马上按下「u」,回到上一个操作。按多次"u"可以执行多次回复。

 

  8)更改

  「cw」:更改光标所在处的字到字尾处

  「c#w」:例如,「c3w」表示更改3个字

 

  9)跳至指定的行

  「ctrl」+「g」列出光标所在行的行号。

  「#G」:例如,「15G」,表示移动光标至文章的第15行行首。

 

  4、Last line mode下命令简介

 

  在使用「last line mode」之前,请记住先按「ESC」键确定您已经处于「command mode」下后,再按「:」冒号即可进入「last line mode」。

 

  A) 列出行号

  「set nu」:输入「set nu」后,会在文件中的每一行前面列出行号。

  B) 跳到文件中的某一行

  「#」:「#」号表示一个数字,在冒号后输入一个数字,再按回车键就会跳到该行了,如输入数字15,再回车,就会跳到文章的第15行。

  C) 查找字符

  「/关键字」:先按「/」键,再输入您想寻找的字符,如果第一次找的关键字不是您想要的,可以一直按「n」会往后寻找到您要的关键字为止。

  「?关键字」:先按「?」键,再输入您想寻找的字符,如果第一次找的关键字不是您想要的,可以一直按「n」会往前寻找到您要的关键字为止。

  D) 保存文件

  「w」:在冒号输入字母「w」就可以将文件保存起来。

  E) 离开vi

  「q」:按「q」就是退出,如果无法离开vi,可以在「q」后跟一个「!」强制离开vi。

  「qw」:一般建议离开时,搭配「w」一起使用,这样在退出的时候还可以保存文件。

 

  5、vi命令列表

 

  1、下表列出命令模式下的一些键的功能:

 

h         左移光标一个字符

l         右移光标一个字符

k         光标上移一行

j         光标下移一行

^         光标移动至行首

0         数字"0",光标移至文章的开头

G         光标移至文章的最后

$         光标移动至行尾

Ctrl+f     向前翻屏

Ctrl+b     向后翻屏

Ctrl+d     向前翻半屏

Ctrl+u     向后翻半屏

i         在光标位置前插入字符

a         在光标所在位置的后一个字符开始增加

o         插入新的一行,从行首开始输入

ESC     从输入状态退至命令状态

x         删除光标后面的字符

#x         删除光标后的#个字符

X         (大写X),删除光标前面的字符

#X         删除光标前面的#个字符

dd         删除光标所在的行

#dd     删除从光标所在行数的#行

yw         复制光标所在位置的一个字

#yw     复制光标所在位置的#个字

yy         复制光标所在位置的一行

#yy     复制从光标所在行数的#行

p         粘贴

u         取消操作

cw         更改光标所在位置的一个字

#cw     更改光标所在位置的#个字

 

  2、下表列出行命令模式下的一些指令

 

w filename     储存正在编辑的文件为filename

wq filename     储存正在编辑的文件为filename,并退出vi

q!             放弃所有修改,退出vi

set nu         显示行号

/或?         查找,在/后输入要查找的内容

n             与/或?一起使用,如果查找的内容不是想要找的关键字,按n或向后(与/联用)或向前(与?联用)继续查找,直到找到为止。

 

  对于第一次用vi,有几点注意要提醒一下:

 

  1、用vi打开文件后,是处于「命令行模式(command mode)」,您要切换到「插入模式(Insert mode)」才能够输入文字。切换方法:在「命令行模式(command mode)」下按一下字母「i」就可以进入「插入模式(Insert mode)」,这时候你就可以开始输入文字了。

  2、编辑好后,需从插入模式切换为命令行模式才能对文件进行保存,切换方法:按「ESC」键。

  3、保存并退出文件:在命令模式下输入:wq即可!(别忘了wq前面的:)

 

4.5 touch命令详解

  linux中新建文件命令touch用法详解:

 

  linux中的touch命令一般用来修改文件的时间,或者新建一个不存在的文件。

 

  语法如下:

 

touch [-acm][-r ref_file(参照文件)|-t time(时间值)] file(文件名)

 

  例子:

 

touch file1.txt 更新file1.txt的存取和修改时间

touch -c file1.txt 如果file1.txt不存在,不创建文件

touch -r ref_file file1.txt 更新file1.txt的时间戳和ref+file相同

touch -t 0911252234.40 file1.txt 设定文件的时间为09年11月25日22点34分40秒

 

  个参数说明如下:

 

-a          修改文件 file 的存取时间.

-c          不创建文件 file.

-m          修改文件 file 的修改时间

-r ref_file 将参照文件 ref_file 相应的时间戳记的数值作为指定文件 file 时间戳记的新值.

-t time      使用指定的时间值 time 作为指定文件 file 相应时间戳记的新值.此处的 time 规定为如下形式的十进制数∶ [[CC]YY]MMDDhhmm[.SS]

  这里,CC为年数中的前两位,即"世纪数";YY为年数的后两位,即某世纪中的年数.如果不给出CC的值,则touch 将把年数CCYY限定在1969--2068之内.MM为月数,DD为天将把年数CCYY限定在1969--2068之内.MM为月数,DD为天数,hh 为小时数(几点),mm为分钟数,SS为秒数.此处秒的设定范围是0--61,这样可以处理闰秒.这些数字组成的时间是环境变量TZ指定的时区中的一个时 间.由于系统的限制,早于1970年1月1日的时间是错误的。

 

  文章下载地址:http://files.cnblogs.com/xia520pi/HadoopCluster_Vol.3.rar

Hadoop集群(第4期)_SecureCRT使用

 

1、SecureCRT简介

 

  SecureCRT是一款支持SSH(SSH1和SSH2)的终端仿真程序,同时支持Telnet和rlogin协议。SecureCRT是一款用于连接运行包括Windows、UNIX和VMS的远程系统的理想工具。通过使用内含的VCP命令行程序可以进行加密文件的传输。有流行CRTTelnet客户机的所有特点,包括:自动注册、对不同主机保持不同的特性、打印功能、颜色设置、可变屏幕尺寸、用户定义的键位图和优良的VT100,VT102,VT220和ANSI竞争。能从命令行中运行或从浏览器中运行。其它特点包括文本手稿、易于使用的工具条、用户的键位图编辑器、可定制的ANSI颜色等.SecureCRT的SSH协议支持DES,3DES和RC4密码和密码与RSA鉴别。

 

2、SecureCRT安装

 

2.1 下载软件

      为了使实验室后续人员还能按照该文档设置,故把本次用的软件上传到CSDN上,即使你实验室找不到该软件的情况下还可以从下面地址中下载。

    地址http://download.csdn.net/detail/xia520pi/4088823

 

2.2 安装软件

  因为这包软件是绿色安装,所以只需要把压缩包解压到你想要的地方即可。例如:

 

 

  上图中是本人的解压之后的地方,我放在了E盘下,打开文件夹,找到SecureCRT.exe

 

 

  接着击"SecureCRT.exe",建立桌面快捷方式。

 

 

  当首次运行该软件时会弹出一个选项框,别管它,关掉即可,这是会出现如下界面。

 

 

  上图就是这个软件的启动界面,下面我们就将如何实现该软件实现远程登录我们的Linux系统。

 

3、用密码登录Linux

  这小节我们将采用"密码"的方式登录Linux,首先我们做的第一件事儿就是建立"远程会话"。有两种途径建立。

  第一种:"文件"à"连接"

  第二种:用菜单栏的快捷方式

 

 

  按照两种方式都可以,然后弹出下面选项框,照着图那样点击所示按钮。

 

 

  然后弹出如下界面。

 

 

  选择"SSH2",接着点击"下一步"。

 

 

  按照上图所示,填写要远程连接的"主机名"和"用户名",比如我要连接"主机名:192.168.1.10"且"用户名:hadoop"的机器,就如上所写。

 

 

  上面这个"会话名称"可以修改,也可以保持不变,点击"完成",就设置成功了。

 

 

  如果刚才改"会话名称",上图的就不是一个IP地址了,而是你自己设置的名字。还有记得勾上"在标签页中打开",如果你想在一个界面中打开多个终端,就有这个必要。这时选择你想远程连接,选择一个"会话名称",比如"192.168.1.10",点击"连接"。

 

 

  出现上面画面,默认"接受并保存",直接按回车,就可以了。

 

 

  然后就会让你输入密码,写上密码,在"保存密码"前打上对勾,接着"确认"。如果网络畅通,就可以进行远程访问了。

 

 

  出现上面的"绿色"对勾,就表示你已经连接上了, 我们现在试一个命令吧。

 

 

  我们发现了两个不如意的地方,第一个:出现了红色标记的乱码;第二个:黑白显示不是很舒服。当然这个我们是可以进行调节的。那我们还等什么呢?那就赶紧行动吧,使我们的界面变得更加人性化。

 

 

  按照上图中,右击"192.168.1.10"标签,会弹出一个菜单。点击"会话选项"。

 

 

  按照上图中三步进行设置,点击"仿真",在终端选择"Linux",接着,把"使用颜色方案"和"ANSI颜色"打上对勾

  然后还有一个问题没有解决,就是"中文乱码"问题,好,我们选择"外观",可以更改"当前颜色方案",默认的是"Monochrone",我比较喜欢"花白/暗青",接着设置字体,如果你感觉默认字体比较舒服,就按原来不动,如果感觉自己比较小,可以把字体设置大,我比较喜欢"楷体 14号",这样看显示结果比较舒服一点,你现在该问了,我们不是解决"中文乱码"问题吗?怎么现在竟弄了一下无关紧要的事儿,哈哈,下面就该设置编码了,细心的你会发现有一个"字符编码"的选项,默认是"Default",现在我们选择"UTF-8"。这样设置完,重新连接,就换成我们想要的界面了。下面是我们刚才设置之后的界面截图。

 

 

  下图是我们重新连接之后的界面,发现界面颜色变了,而且也没有乱码了。

 

 

  好了,到目前为止,我们用密码连接Linux已经结束了,现在就可以远程对Linux进行操作了。下一节我们将进行SSH密钥连接Linux,这样每次连接就不需要进行输入用户名和密码就可以直接进行连接,而且安全性是最高的。

 

4、用SSH登录Linux

 

4.1 SSH基础知识

  我想肯定有不少朋友在使用 SecureCRT 做为 SSH 的客户端软件,但都很少使用他的 RSA/DSA 加密功能吧,怎么你还不知道 RSA 是什么?

  SSH,特别是 OpenSSH,是一个类似于 telnet 或 rsh,ssh 客户程序也可以用于登录到远程机器。我们中有许多人把优秀的 OpenSSH用作古老的 telnet 和 rsh 命令的替代品,OpenSSH 不仅是安全的而且是加密的。OpenSSH 更加吸引人的特性之一是它能够使用基于一对互补数字式密钥RSADSA 认证协议来认证用户。RSA 和 DSA 认证承诺不必提供密码就能够同远程系统建立连接,这是它的主要魅力之一。虽然这非常吸引人,但是 OpenSSH 的新用户们常常以一种快速却不完善的方式配置 RSA/DSA,结果虽然实现了无密码登录,却也在此过程中开了一个很大的安全漏洞。

  什么是 RSA/DSA 认证?

  SSH,特别是 OpenSSH(完全免费的 SSH 的实现),是一个不可思议的工具。类似于 telnet 或 rsh,ssh 客户程序也可以用于登录到远程机器。所要求的只是该远程机器正在运行 sshd,即 ssh 服务器进程。但是,与 telnet 不同的是,ssh 协议非常安全。加密数据流,确保数据流的完整性,甚至安全可靠的进行认证它都使用了专门的算法。

  然而,虽然 ssh 的确很棒,但还是有一个 ssh 功能组件常常被忽略、被危险的误用或者简直就是被误解。这个组件就是 OpenSSH 的 RSA/DSA 密钥认证系统,它可以代替 OpenSSH 缺省使用的标准安全密码认证系统。

  OpenSSH 的 RSA 和 DSA 认证协议的基础是一对专门生成的密钥,分别叫做专用密钥公用密钥。使用这些基于密钥的认证系统的优势在于:在许多情况下,有可能不必手工输入密码就能建立起安全的连接。

  两项注意事项

  关于 RSA 和 DSA 认证有两项重要的注意事项。第一项是我们的确只需要生成一对密钥。然后我们可以把我们的公用密钥拷贝到想要访问的那些远程机器上,它们都会根据我们的那把专用密钥进行恰当的认证。换句话说,我们并不需要为想要访问的 每个系统都准备一对密钥。只要一对就足够了。

  另一项注意事项是专用密钥不应落入其它人手中。正是专用密钥授权我们访问远程系统,任何拥有我们的专用密钥的人都会被授予和我们完全相同的特权。如同我们不想让陌生人有我们的住处的钥匙一样,我们应该保护我们的专用密钥以防未授权的使用。在比特和字节的世界里,这意味着没有人是本来就应该能读取或是拷贝我们的专用密钥的。

 

4.2 创建公钥

  了解了SSH的基本知识后,我们开始进行本小节的SSH登录Linux,首先在SecureCRT软件的菜单栏中:工具à创建公钥

 

 

  点击"创建公钥"之后出现下图,并借着点击"下一步"即可。

 

 

  选择非对称加密方式RSA,然后点击"下一步"。

 

 

  通行短语是可选的,如果设置了,在连接上服务器的时候需要输入。例如:我这里设置了通行短语"myhadoop",并且下面的注释写为"虾皮工作室@Linux"。

 

 

  下一步,密钥长度,默认即可,2048也行,越长越安全。我们在这里就保持默认即可,如果选择的越大,生成密钥生成的时间也就越长。

 

 

  下一步,生成密钥,请耐心等待。

 

 

  完成后下一步,这里保存了私钥地址和文件名。建议将这个名字改一改,因为生成多个用户的密钥的时候方便识别,而且放在U盘里随身携带的时候也好认识,例如把原来的"Identity"改写成"PubKey_TMaster_Hadoop"。

 

 

  到此为止,使用客户端 SecureCRT 生成密钥已经完成。接下来,就是要将密钥文件上传至服务器端,并在服务器端导入密钥。

 

4.3 SSH服务器设置

  第一步:在需要配置的用户根目录下创建.ssh目录并设置一定的权限。本次在一般用户(hadoop)下面进行。

 

 

  如上图所示通过下面命令建立了.ssh目录,并且修改了其权限。

 

# mkdir /root/.ssh

# chmod 700 /root/.ssh

 

  第二步:采用Hadoop集群建立VSFTP服务器,通过FTP把我们的公钥上传上去,当然你可以通过U盘把公钥拷贝到服务器上。此次我们采用FTP的方式。

 

 

   然后通过SecureCRT查看文件是否已经上传。

 

 

  从上图中我们知道已经上传了。

  第三步:用上传的公钥生成authorized_keys文件并设置文件的权限,将SSH2兼容格式的公钥转换成为Openssh兼容格式。

 

 

  上图中遇到的命令如下所示:

 

# ssh-keygen -i -f /home/hadoop/.ssh/PubKey_TMaster_Hadoop.pub >>/home/hadoop/.ssh /authorized_keys

# chmod 600 /home/hadoop/.ssh/authorized_keys

    

  上面命令红色标注的部分是根据实际情况变化的,比如这是从网上找的一个例子,他是这样设置的。

 

# ssh-keygen -i -f Identity.pub >> /root/.ssh/authorized_keys

# chmod 600 /root/.ssh/authorized_keys

 

  上面蓝色标注的部分是两个命令的共同点。

  第四步:设置ssh配置文件,为了安全建议只使用密钥登录,去掉默认的密码登录。

  一般用户无法修改"/etc/ssh/sshd_config"文件,所以必须切换到root最高权限用户下进行修改。使用下面命令。

 

Protocol 2              # 使用SSH2协议

RSAAuthentication yes # 启用 RSA 认证

PubkeyAuthentication yes # 启用公钥私钥配对认证方式

AuthorizedKeysFile .ssh/authorized_keys # 公钥文件路径(和上面生成的文件同)

PasswordAuthentication no # 禁止密码验证登陆(根据需要,不过一般启用了密钥,就不用密码了)

 

  第五步:重启Linux服务器的ssh服务

 

# service sshd restart

 

4.4 SSH连接服务器

  在"用密码登陆Linux"小节中,我们讲到过用两种方式打开"连接"界面,然后选择"快速连接"。

 

 

  点击完"快速连接"会弹出下面界面:

 

 

  按照上面的步骤进行,步骤1是默认就选择SSH2协议的,步骤2步骤3是根据实际情况填写的。比如我上面要连接远程Linux的IP为"192.168.1.10"且一般账户为"hadoop"的机器。步骤4只需要勾上"公钥"和"键盘交互",然后中"公钥",点击其"属性"按钮,及图中标注的步骤5

 

 

  通过浏览选择我们刚才生成的证书,点击确定,回到"快速连接"界面点击"连接"按钮,然后弹出一个窗口,然填写我们的"通行短语"。把我们刚才设置的通行短语"myhadoop"输入进去,就可以了。点击确定,就可以实现SSH无密码连接了。

 

  

  文章下载地址:http://files.cnblogs.com/xia520pi/HadoopCluster_Vol.4.rar


Hadoop集群(第5期)_Hadoop安装配置

 

1、集群部署介绍

 

1.1 Hadoop简介

 

  Hadoop是Apache软件基金会旗下的一个开源分布式计算平台。以Hadoop分布式文件系统(HDFS,Hadoop Distributed Filesystem)和MapReduce(Google MapReduce的开源实现)为核心的Hadoop为用户提供了系统底层细节透明的分布式基础架构。

  对于Hadoop的集群来讲,可以分成两大类角色:Master和Salve。一个HDFS集群是由一个NameNode和若干个DataNode组成的。其中NameNode作为主服务器,管理文件系统的命名空间和客户端对文件系统的访问操作;集群中的DataNode管理存储的数据。MapReduce框架是由一个单独运行在主节点上的JobTracker和运行在每个集群从节点的TaskTracker共同组成的。主节点负责调度构成一个作业的所有任务,这些任务分布在不同的从节点上。主节点监控它们的执行情况,并且重新执行之前的失败任务;从节点仅负责由主节点指派的任务。当一个Job被提交时,JobTracker接收到提交作业和配置信息之后,就会将配置信息等分发给从节点,同时调度任务并监控TaskTracker的执行。

  从上面的介绍可以看出,HDFS和MapReduce共同组成了Hadoop分布式系统体系结构的核心。HDFS在集群上实现分布式文件系统MapReduce在集群上实现了分布式计算任务处理。HDFS在MapReduce任务处理过程中提供了文件操作和存储等支持,MapReduce在HDFS的基础上实现了任务的分发、跟踪、执行等工作,并收集结果,二者相互作用,完成了Hadoop分布式集群的主要任务。

 

1.2 环境说明

  集群中包括4个节点:1个Master,3个Salve,节点之间局域网连接,可以相互ping通,具体集群信息可以查看"Hadoop集群(第2期)"。节点IP地址分布如下:

 

机器名称

IP地址

Master.Hadoop

192.168.1.2

Salve1.Hadoop 

192.168.1.3 

Salve2.Hadoop 

192.168.1.4 

Salve3.Hadoop 

192.168.1.5 

 

  四个节点上均是CentOS6.0系统,并且有一个相同的用户hadoop。Master机器主要配置NameNode和JobTracker的角色,负责总管分布式数据和分解任务的执行;3个Salve机器配置DataNode和TaskTracker的角色,负责分布式数据存储以及任务的执行。其实应该还应该有1个Master机器,用来作为备用,以防止Master服务器宕机,还有一个备用马上启用。后续经验积累一定阶段后补上一台备用Master机器。

 

1.3 网络配置

  Hadoop集群要按照1.2小节表格所示进行配置,我们在"Hadoop集群(第1期)"的CentOS6.0安装过程就按照提前规划好的主机名进行安装和配置。如果实验室后来人在安装系统时,没有配置好,不要紧,没有必要重新安装,在安装完系统之后仍然可以根据后来的规划对机器的主机名进行修改。

  下面的例子我们将以Master机器为例,即主机名为"Master.Hadoop",IP为"192.168.1.2"进行一些主机名配置的相关操作。其他的Slave机器以此为依据进行修改。

 

  1)查看当前机器名称

  用下面命令进行显示机器名称,如果跟规划的不一致,要按照下面进行修改。

 

hostname

 

   

  上图中,用"hostname"查"Master"机器的名字为"Master.Hadoop",与我们预先规划的一致。

 

  2)修改当前机器名称

  假定我们发现我们的机器的主机名不是我们想要的,通过对"/etc/sysconfig/network"文件修改其中"HOSTNAME"后面的值,改成我们规划的名称。

  这个"/etc/sysconfig/network"文件是定义hostname和是否利用网络的不接触网络设备的对系统全体定义的文件。

  设定形式:设定值=值

  "/etc/sysconfig/network"的设定项目如下:

 

NETWORKING 是否利用网络

GATEWAY 默认网关

IPGATEWAYDEV 默认网关的接口名

HOSTNAME 主机名

DOMAIN 域名

 

  用下面命令进行修改当前机器的主机名(备注:修改系统文件一般用root用户)

 

vim /etc/sysconfig/network

 

 

  通过上面的命令我们从"/etc/sysconfig/network"中找到"HOSTNAME"进行修改,查看内容如下:

 

 

    3)修改当前机器IP

    假定我们的机器连IP在当时安装机器时都没有配置好,那此时我们需要对"ifcfg-eth0"文件进行配置,该文件位于"/etc/sysconfig/network-scripts"文件夹下。

  在这个目录下面,存放的是网络接口(网卡)的制御脚本文件(控制文件),ifcfg- eth0是默认的第一个网络接口,如果机器中有多个网络接口,那么名字就将依此类推ifcfg-eth1,ifcfg-eth2,ifcfg- eth3,……。

  这里面的文件是相当重要的,涉及到网络能否正常工作。

  设定形式:设定值=值

  设定项目项目如下:

 

DEVICE 接口名(设备,网卡)

BOOTPROTO IP的配置方法(static:固定IP, dhcpHCP, none:手动)

HWADDR MAC地址

ONBOOT 系统启动的时候网络接口是否有效(yes/no)

TYPE 网络类型(通常是Ethemet)

NETMASK 网络掩码

IPADDR IP地址

IPV6INIT IPV6是否有效(yes/no)

GATEWAY 默认网关IP地址

 

  查看"/etc/sysconfig/network-scripts/ifcfg-eth0"内容,如果IP不复核,就行修改。

 

 

  如果上图中IP与规划不相符,用下面命令进行修改:

 

vim /etc/sysconfig/network-scripts/ifcgf-eth0

 

  修改完之后可以用"ifconfig"进行查看。

 

   

  4)配置hosts文件(必须

  "/etc/hosts"这个文件是用来配置主机将用的DNS服务器信息,是记载LAN内接续的各主机的对应[HostName和IP]用的。当用户在进行网络连接时,首先查找该文件,寻找对应主机名(或域名)对应的IP地址。

  我们要测试两台机器之间知否连通,一般用"ping 机器的IP",如果想用"ping 机器的主机名"发现找不见该名称的机器,解决的办法就是修改"/etc/hosts"这个文件,通过把LAN内的各主机的IP地址和HostName的一一对应写入这个文件的时候,就可以解决问题。

  例如:机器为"Master.Hadoop:192.168.1.2"对机器为"Salve1.Hadoop:192.168.1.3"用命令"ping"记性连接测试。测试结果如下:

 

   

   从上图中的值,直接对IP地址进行测试,能够ping通,但是对主机名进行测试,发现没有ping通,提示"unknown host——未知主机",这时查看"Master.Hadoop"的"/etc/hosts"文件内容。

 

   

  发现里面没有"192.168.1.3 Slave1.Hadoop"内容,故而本机器是无法对机器的主机名为"Slave1.Hadoop" 解析。

  在进行Hadoop集群配置中,需要在"/etc/hosts"文件中添加集群中所有机器的IP与主机名,这样Master与所有的Slave机器之间不仅可以通过IP进行通信,而且还可以通过主机名进行通信。所以在所有的机器上的"/etc/hosts"文件末尾中都要添加如下内容:

 

192.168.1.2 Master.Hadoop

192.168.1.3 Slave1.Hadoop

192.168.1.4 Slave2.Hadoop

192.168.1.5 Slave3.Hadoop

 

  用以下命令进行添加:

 

vim /etc/hosts

 

 

  添加结果如下:

 

 

  现在我们在进行对机器为"Slave1.Hadoop"的主机名进行ping通测试,看是否能测试成功。

 

 

  从上图中我们已经能用主机名进行ping通了,说明我们刚才添加的内容,在局域网内能进行DNS解析了,那么现在剩下的事儿就是在其余的Slave机器上进行相同的配置。然后进行测试。(备注:当设置SSH无密码验证后,可以"scp"进行复制,然后把原来的"hosts"文件执行覆盖即可。)

 

1.4 所需软件

1)JDK软件

    下载地址:http://www.oracle.com/technetwork/java/javase/index.html

    JDK版本:jdk-6u31-linux-i586.bin

2)Hadoop软件

    下载地址:http://hadoop.apache.org/common/releases.html

    Hadoop版本:hadoop-1.0.0.tar.gz

 

1.5 VSFTP上传

  在"Hadoop集群(第3期)"讲了VSFTP的安装及配置,如果没有安装VSFTP可以按照该文档进行安装。如果安装好了,就可以通过FlashFXP.exe软件把我们下载的JDK6.0和Hadoop1.0软件上传到"Master.Hadoop:192.168.1.2"服务器上。

 

 

  刚才我们用一般用户(hadoop)通过FlashFXP软件把所需的两个软件上传了跟目下,我们通过命令查看下一下是否已经上传了。

 

 

    从图中,我们的所需软件已经准备好了。

 

2、SSH无密码验证配置

  Hadoop运行过程中需要管理远端Hadoop守护进程,在Hadoop启动以后,NameNode是通过SSH(Secure Shell)来启动和停止各个DataNode上的各种守护进程的。这就必须在节点之间执行指令的时候是不需要输入密码的形式,故我们需要配置SSH运用无密码公钥认证的形式,这样NameNode使用SSH无密码登录并启动DataName进程,同样原理,DataNode上也能使用SSH无密码登录到NameNode。

 

2.1 安装和启动SSH协议

  在"Hadoop集群(第1期)"安装CentOS6.0时,我们选择了一些基本安装包,所以我们需要两个服务:ssh和rsync已经安装了。可以通过下面命令查看结果显示如下:

 

rpm –qa | grep openssh

rpm –qa | grep rsync

 

 

  假设没有安装ssh和rsync,可以通过下面命令进行安装。

 

yum install ssh 安装SSH协议

yum install rsync (rsync是一个远程数据同步工具,可通过LAN/WAN快速同步多台主机间的文件)

service sshd restart 启动服务

 

  确保所有的服务器都安装,上面命令执行完毕,各台机器之间可以通过密码验证相互登。

 

2.2 配置Master无密码登录所有Salve

  1)SSH无密码原理

  Master(NameNode | JobTracker)作为客户端,要实现无密码公钥认证,连接到服务器Salve(DataNode | Tasktracker)上时,需要在Master上生成一个密钥对,包括一个公钥和一个私钥,而后将公钥复制到所有的Slave上。当Master通过SSH连接Salve时,Salve就会生成一个随机数并用Master的公钥对随机数进行加密,并发送给Master。Master收到加密数之后再用私钥解密,并将解密数回传给Slave,Slave确认解密数无误之后就允许Master进行连接了。这就是一个公钥认证过程,其间不需要用户手工输入密码。重要过程是将客户端Master复制到Slave上。

  2)Master机器上生成密码对

   在Master节点上执行以下命令:

 

ssh-keygen –t rsa –P ''

 

  这条命是生成其无密码密钥对,询问其保存路径时直接回车采用默认路径。生成的密钥对:id_rsa和id_rsa.pub,默认存储在"/home/hadoop/.ssh"目录下。

 

 

  查看"/home/hadoop/"下是否有".ssh"文件夹,且".ssh"文件下是否有两个刚生产的无密码密钥对。

 

 

  接着在Master节点上做如下配置,把id_rsa.pub追加到授权的key里面去。

 

cat ~/.ssh/id_rsa.pub >> ~/.ssh/authorized_keys

 

 

  在验证前,需要做两件事儿。第一件事儿是修改文件"authorized_keys"权限(权限的设置非常重要,因为不安全的设置安全设置,会让你不能使用RSA功能),另一件事儿是用root用户设置"/etc/ssh/sshd_config"的内容。使其无密码登录有效。

  1)修改文件"authorized_keys"

    

chmod 600 ~/.ssh/authorized_keys

 

   

   备注:如果不进行设置,在验证时,扔提示你输入密码,在这里花费了将近半天时间来查找原因。在网上查到了几篇不错的文章,把作为"Hadoop集群_第5期副刊_JDK和SSH无密码配置"来帮助额外学习之用。

 

    2)设置SSH配置

  用root用户登录服务器修改SSH配置文件"/etc/ssh/sshd_config"的下列内容。

 

 

RSAAuthentication yes # 启用 RSA 认证

PubkeyAuthentication yes # 启用公钥私钥配对认证方式

AuthorizedKeysFile .ssh/authorized_keys # 公钥文件路径(和上面生成的文件同)

 

  设置完之后记得重启SSH服务,才能使刚才设置有效。

 

service sshd restart

    

  退出root登录,使用hadoop普通用户验证是否成功。

 

ssh localhost

 

   

  从上图中得知无密码登录本级已经设置完毕,接下来的事儿是把公钥复制所有Slave机器上。使用下面的命令格式进行复制公钥:

 

scp ~/.ssh/id_rsa.pub 远程用户名@远程服务器IP:~/

 

  例如:

 

scp ~/.ssh/id_rsa.pub hadoop@192.168.1.3:~/

 

  上面的命令是复制文件"id_rsa.pub"到服务器IP为"192.168.1.3"的用户为"hadoop"的"/home/hadoop/"下面。

 

  下面就针对IP为"192.168.1.3"的Slave1.Hadoop的节点进行配置。

  1)把Master.Hadoop上的公钥复制到Slave1.Hadoop上

 

 

 

  从上图中我们得知,已经把文件"id_rsa.pub"传过去了,因为并没有建立起无密码连接,所以在连接时,仍然要提示输入输入Slave1.Hadoop服务器用户hadoop的密码。为了确保确实已经把文件传过去了,用SecureCRT登录Slave1.Hadoop:192.168.1.3服务器,查看"/home/hadoop/"下是否存在这个文件。

 

 

  从上面得知我们已经成功把公钥复制过去了。

  2)在"/home/hadoop/"下创建".ssh"文件夹

  这一步并不是必须的,如果在Slave1.Hadoop的"/home/hadoop"已经存在就不需要创建了,因为我们之前并没有对Slave机器做过无密码登录配置,所以该文件是不存在的。用下面命令进行创建。(备注:用hadoop登录系统,如果不涉及系统文件修改,一般情况下都是用我们之前建立的普通用户hadoop进行执行命令。)

 

mkdir ~/.ssh

 

  然后是修改文件夹".ssh"的用户权限,把他的权限修改为"700",用下面命令执行:

 

chmod 700 ~/.ssh

 

  备注:如果不进行,即使你按照前面的操作设置了"authorized_keys"权限,并配置了"/etc/ssh/sshd_config",还重启了sshd服务,在Master能用"ssh localhost"进行无密码登录,但是对Slave1.Hadoop进行登录仍然需要输入密码,就是因为".ssh"文件夹的权限设置不对。这个文件夹".ssh"在配置SSH无密码登录时系统自动生成时,权限自动为"700",如果是自己手动创建,它的组权限和其他权限都有,这样就会导致RSA无密码远程登录失败。

 

 

  对比上面两张图,发现文件夹".ssh"权限已经变了。

  3)追加到授权文件"authorized_keys"

  到目前为止Master.Hadoop的公钥也有了,文件夹".ssh"也有了,且权限也修改了。这一步就是把Master.Hadoop的公钥追加到Slave1.Hadoop的授权文件"authorized_keys"中去。使用下面命令进行追加并修改"authorized_keys"文件权限:

 

cat ~/id_rsa.pub >> ~/.ssh/authorized_keys

chmod 600 ~/.ssh/authorized_keys

 

  4)用root用户修改"/etc/ssh/sshd_config"

  具体步骤参考前面Master.Hadoop的"设置SSH配置",具体分为两步:第1是修改配置文件;第2是重启SSH服务。

  5)用Master.Hadoop使用SSH无密码登录Slave1.Hadoop

  当前面的步骤设置完毕,就可以使用下面命令格式进行SSH无密码登录了。

 

ssh 远程服务器IP

 

 

  从上图我们主要3个地方,第1个就是SSH无密码登录命令,第2、3个就是登录前后"@"后面的机器名变了,由"Master"变为了"Slave1",这就说明我们已经成功实现了SSH无密码登录了。

  最后记得把"/home/hadoop/"目录下的"id_rsa.pub"文件删除掉。

 

rm –r ~/id_rsa.pub

 

 

  到此为止,我们经过前5步已经实现了从"Master.Hadoop"到"Slave1.Hadoop"SSH无密码登录,下面就是重复上面的步骤把剩余的两台(Slave2.Hadoop和Slave3.Hadoop)Slave服务器进行配置。这样,我们就完成了"配置Master无密码登录所有的Slave服务器"。

 

2.3 配置所有Slave无密码登录Master

  和Master无密码登录所有Slave原理一样,就是把Slave的公钥追加到Master的".ssh"文件夹下的"authorized_keys"中,记得是追加(>>)

  为了说明情况,我们现在就以"Slave1.Hadoop"无密码登录"Master.Hadoop"为例,进行一遍操作,也算是巩固一下前面所学知识,剩余的"Slave2.Hadoop"和"Slave3.Hadoop"就按照这个示例进行就可以了。

  首先创建"Slave1.Hadoop"自己的公钥和私钥,并把自己的公钥追加到"authorized_keys"文件中。用到的命令如下:

 

ssh-keygen –t rsa –P ''

cat ~/.ssh/id_rsa.pub >> ~/.ssh/authorized_keys

 

 

  接着是用命令"scp"复制"Slave1.Hadoop"的公钥"id_rsa.pub"到"Master.Hadoop"的"/home/hadoop/"目录下,并追加到"Master.Hadoop"的"authorized_keys"中。

  1)在"Slave1.Hadoop"服务器的操作

  用到的命令如下:

 

scp ~/.ssh/id_rsa.pub     hadoop@192.168.1.2:~/

 

  

  2)在"Master.Hadoop"服务器的操作

  用到的命令如下:

 

cat ~/id_rsa.pub >> ~/.ssh/authorized_keys

 

 

  然后删除掉刚才复制过来的"id_rsa.pub"文件。

 

    

  最后是测试从"Slave1.Hadoop"到"Master.Hadoop"无密码登录。

 

 

  从上面结果中可以看到已经成功实现了,再试下从"Master.Hadoop"到"Slave1.Hadoop"无密码登录。

 

 

  至此"Master.Hadoop"与"Slave1.Hadoop"之间可以互相无密码登录了,剩下的就是按照上面的步骤把剩余的"Slave2.Hadoop"和"Slave3.Hadoop"与"Master.Hadoop"之间建立起无密码登录。这样,Master能无密码验证登录每个Slave,每个Slave也能无密码验证登录到Master。

 

3、Java环境安装

  所有的机器上都要安装JDK,现在就先在Master服务器安装,然后其他服务器按照步骤重复进行即可。安装JDK以及配置环境变量,需要以"root"的身份进行。

 

3.1 安装JDK

  首先用root身份登录"Master.Hadoop"后在"/usr"下创建"java"文件夹,再把用FTP上传到"/home/hadoop/"下的"jdk-6u31-linux-i586.bin"复制到"/usr/java"文件夹中。

 

mkdir /usr/java

cp /home/hadoop/ jdk-6u31-linux-i586.bin /usr/java

 

 

  接着进入"/usr/java"目录通过下面命令使其JDK获得可执行权限,并安装JDK。

 

chmod    +x     jdk-6u31-linux-i586.bin

./jdk-6u31-linux-i586.bin

 

 

  按照上面几步进行操作,最后点击"Enter"键开始安装,安装完会提示你按"Enter"键退出,然后查看"/usr/java"下面会发现多了一个名为"jdk1.6.0_31"文件夹,说明我们的JDK安装结束,删除"jdk-6u31-linux-i586.bin"文件,进入下一个"配置环境变量"环节。

 

 

3.2 配置环境变量

  编辑"/etc/profile"文件,在后面添加Java的"JAVA_HOME"、"CLASSPATH"以及"PATH"内容。

  1)编辑"/etc/profile"文件

 

vim     /etc/profile

 

 

  2)添加Java环境变量

  在"/etc/profile"文件的尾部添加以下内容:

 

# set java environment

export JAVA_HOME=/usr/java/jdk1.6.0_31/

export JRE_HOME=/usr/java/jdk1.6.0_31/jre

export CLASSPATH=.:$CLASSPATH:$JAVA_HOME/lib:$JRE_HOME/lib

export PATH=$PATH:$JAVA_HOME/bin:$JRE_HOME/bin

 

  或者

 

# set java environment

export JAVA_HOME=/usr/java/jdk1.6.0_31

export CLASSPATH=.:$CLASSPATH:$JAVA_HOME/lib:$JAVA_HOME/jre/lib

export PATH=$PATH:$JAVA_HOME/bin:$JAVA_HOME/jre/bin

 

   以上两种意思一样,那么我们就选择第2种来进行设置。

 

 

  3)使配置生效

  保存并退出,执行下面命令使其配置立即生效。

 

source /etc/profile

 

 

3.3 验证安装成功

  配置完毕并生效后,用下面命令判断是否成功。

 

java -version

 

 

  从上图中得知,我们以确定JDK已经安装成功。

 

3.4 安装剩余机器

  这时用普通用户hadoop通过下面命令格式把"Master.Hadoop"文件夹"/home/hadoop/"的JDK复制到其他Slave的"/home/hadoop/"下面,剩下的事儿就是在其余的Slave服务器上按照上图的步骤安装JDK。

 

scp /home/hadoop/jdk-6u31-linux-i586.bin 远程用户名@远程服务器IP:~/

 

  或者

 

scp ~/jdk-6u31-linux-i586.bin 远程用户名@远程服务器IP:~/

 

  备注:"~"代表当前用户的主目录,当前用户为hadoop,所以"~"代表"/home/hadoop"。

  例如:把JDK从"Master.Hadoop"复制到"Slave1.Hadoop"的命令如下。

 

scp ~/jdk-6u31-linux-i586 hadoop@192.168.1.3:~/

 

 

  然后查看"Slave1.Hadoop"的"/home/hadoop"查看是否已经复制成功了。

 

 

  从上图中得知,我们已经成功复制了,现在我们就用最高权限用户root进行安装了。其他的与这个一样。

 

4、Hadoop集群安装

  所有的机器上都要安装hadoop,现在就先在Master服务器安装,然后其他服务器按照步骤重复进行即可。安装和配置hadoop需要以"root"的身份进行。

 

4.1 安装hadoop

  首先用root用户登录"Master.Hadoop"机器,查看我们之前用FTP上传至"/home/Hadoop"上传的"hadoop-1.0.0.tar.gz"。

 

 

  接着把"hadoop-1.0.0.tar.gz"复制到"/usr"目录下面。

 

cp /home/hadoop/hadoop-1.0.0.tar.gz /usr

 

 

  下一步进入"/usr"目录下,用下面命令把"hadoop-1.0.0.tar.gz"进行解压,并将其命名为"hadoop",把该文件夹的读权限分配给普通用户hadoop,然后删除"hadoop-1.0.0.tar.gz"安装包。

 

cd /usr #进入"/usr"目录

tar –zxvf hadoop-1.0.0.tar.gz #解压"hadoop-1.0.0.tar.gz"安装包

mv hadoop-1.0.0 hadoop #将"hadoop-1.0.0"文件夹重命名"hadoop"

chown –R hadoop:hadoop hadoop #将文件夹"hadoop"读权限分配给hadoop用户

rm –rf hadoop-1.0.0.tar.gz                 #删除"hadoop-1.0.0.tar.gz"安装包

 

 

  解压后,并重命名。

 

 

  把"/usr/hadoop"读权限分配给hadoop用户(非常重要

 

 

  删除"hadoop-1.0.0.tar.gz"安装包

 

 

  最后在"/usr/hadoop"下面创建tmp文件夹,把Hadoop的安装路径添加到"/etc/profile"中,修改"/etc/profile"文件(配置java环境变量的文件),将以下语句添加到末尾,并使其有效:

 

# set hadoop path

export HADOOP_HOME=/usr/hadoop

export PATH=$PATH :$HADOOP_HOME/bin

 

    1)在"/usr/hadoop"创建"tmp"文件夹

 

mkdir /usr/hadoop/tmp

 

 

    2)配置"/etc/profile"

 

vim /etc/profile

 

 

  配置后的文件如下:

 

    

3)重启"/etc/profile"

 

source /etc/profile

 

 

4.2 配置hadoop

  1)配置hadoop-env.sh

  该"hadoop-env.sh"文件位于"/usr/hadoop/conf"目录下。

 

 

  在文件的末尾添加下面内容。

 

# set java environment

export JAVA_HOME=/usr/java/jdk1.6.0_31

 

 

  Hadoop配置文件在conf目录下,之前的版本的配置文件主要是Hadoop-default.xml和Hadoop-site.xml。由于Hadoop发展迅速,代码量急剧增加,代码开发分为了core,hdfs和map/reduce三部分,配置文件也被分成了三个core-site.xml、hdfs-site.xml、mapred-site.xml。core-site.xml和hdfs-site.xml是站在HDFS角度上配置文件;core-site.xml和mapred-site.xml是站在MapReduce角度上配置文件。

 

  2)配置core-site.xml文件

  修改Hadoop核心配置文件core-site.xml,这里配置的是HDFS的地址和端口号。

 

<configuration>

    <property>

        <name>hadoop.tmp.dir</name>

        <value>/usr/hadoop/tmp</value>

        (备注:请先在 /usr/hadoop 目录下建立 tmp 文件夹)

        <description>A base for other temporary directories.</description>

    </property>

<!-- file system properties -->

    <property>

        <name>fs.default.name</name>

        <value>hdfs://192.168.1.2:9000</value>

    </property>

</configuration>

 

  备注:如没有配置hadoop.tmp.dir参数,此时系统默认的临时目录为:/tmp/hadoo-hadoop。而这个目录在每次重启后都会被干掉,必须重新执行format才行,否则会出错。

    用下面命令进行编辑:

 

 

    编辑结果显示如下:

 

 

  3)配置hdfs-site.xml文件

  修改Hadoop中HDFS的配置,配置的备份方式默认为3。

 

<configuration>

    <property>

        <name>dfs.replication</name>

        <value>1</value>

        (备注:replication 是数据副本数量,默认为3,salve少于3台就会报错)

    </property>

<configuration>

 

  用下面命令进行编辑:

 

 

  编辑结果显示如下:

 

 

  4)配置mapred-site.xml文件

  修改Hadoop中MapReduce的配置文件,配置的是JobTracker的地址和端口。

 

<configuration>

    <property>

        <name>mapred.job.tracker</name>

        <value>http://192.168.1.2:9001</value>

    </property>

</configuration>

 

  用下面命令进行编辑:

 

 

  编辑结果显示如下:

 

 

  5)配置masters文件

  有两种方案:

    (1)第一种

        修改localhost为Master.Hadoop

    (2)第二种

        去掉"localhost",加入Master机器的IP:192.168.1.2

  为保险起见,启用第二种,因为万一忘记配置"/etc/hosts"局域网的DNS失效,这样就会出现意想不到的错误,但是一旦IP配对,网络畅通,就能通过IP找到相应主机。

  用下面命令进行修改:

 

 

  编辑结果显示如下:

 

 

    6)配置slaves文件(Master主机特有

    有两种方案:

    (1)第一种

去掉"localhost",每行只添加一个主机名,把剩余的Slave主机名都填上。

        例如:添加形式如下

 

Slave1.Hadoop

Slave2.Hadoop

Slave3.Hadoop

 

    (2)第二种

        去掉"localhost",加入集群中所有Slave机器的IP,也是每行一个。

        例如:添加形式如下

 

192.168.1.3

192.168.1.4

192.168.1.5

 

  原因和添加"masters"文件一样,选择第二种方式。

  用下面命令进行修改:

 

 

  编辑结果如下:

 

 

  现在在Master机器上的Hadoop配置就结束了,剩下的就是配置Slave机器上的Hadoop。

 

  一种方式是按照上面的步骤,把Hadoop的安装包在用普通用户hadoop通过"scp"复制到其他机器的"/home/hadoop"目录下,然后根据实际情况进行安装配置,除了第6步,那是Master特有的。用下面命令格式进行。(备注:此时切换到普通用户hadoop)

 

scp ~/hadoop-1.0.0.tar.gz hadoop@服务器IP:~/

 

    例如:从"Master.Hadoop"到"Slave1.Hadoop"复制Hadoop的安装包。

 

 

    另一种方式是将 Master上配置好的hadoop所在文件夹"/usr/hadoop"复制到所有的Slave的"/usr"目录下(实际上Slave机器上的slavers文件是不必要的, 复制了也没问题)。用下面命令格式进行。(备注:此时用户可以为hadoop也可以为root)

    

scp -r /usr/hadoop root@服务器IP:/usr/

 

    例如:从"Master.Hadoop"到"Slave1.Hadoop"复制配置Hadoop的文件。

 

 

    上图中以root用户进行复制,当然不管是用户root还是hadoop,虽然Master机器上的"/usr/hadoop"文件夹用户hadoop有权限,但是Slave1上的hadoop用户却没有"/usr"权限,所以没有创建文件夹的权限。所以无论是哪个用户进行拷贝,右面都是"root@机器IP"格式。因为我们只是建立起了hadoop用户的SSH无密码连接,所以用root进行"scp"时,扔提示让你输入"Slave1.Hadoop"服务器用户root的密码。

    查看"Slave1.Hadoop"服务器的"/usr"目录下是否已经存在"hadoop"文件夹,确认已经复制成功。查看结果如下:

 

 

  从上图中知道,hadoop文件夹确实已经复制了,但是我们发现hadoop权限是root,所以我们现在要给"Slave1.Hadoop"服务器上的用户hadoop添加对"/usr/hadoop"读权限。

  以root用户登录"Slave1.Hadoop",执行下面命令。

 

chown -R hadoop:hadoop(用户名:用户组) hadoop(文件夹

 

  

  接着在"Slave1 .Hadoop"上修改"/etc/profile"文件(配置 java 环境变量的文件),将以下语句添加到末尾,并使其有效(source /etc/profile):

 

# set hadoop environment

export HADOOP_HOME=/usr/hadoop

export PATH=$PATH :$HADOOP_HOME/bin

 

  如果不知道怎么设置,可以查看前面"Master.Hadoop"机器的"/etc/profile"文件的配置,到此为此在一台Slave机器上的Hadoop配置就结束了。剩下的事儿就是照葫芦画瓢把剩余的几台Slave机器按照《从"Master.Hadoop"到"Slave1.Hadoop"复制Hadoop的安装包。》这个例子进行部署Hadoop。

 

4.3 启动及验证

  1)格式化HDFS文件系统

  在"Master.Hadoop"上使用普通用户hadoop进行操作。(备注:只需一次,下次启动不再需要格式化,只需 start-all.sh)

 

hadoop namenode -format

 

  某些书上和网上的某些资料中用下面命令执行。

 

 

  我们在看好多文档包括有些书上,按照他们的hadoop环境变量进行配置后,并立即使其生效,但是执行发现没有找见"bin/hadoop"这个命令。

 

 

  其实我们会发现我们的环境变量配置的是"$HADOOP_HOME/bin",我们已经把bin包含进入了,所以执行时,加上"bin"反而找不到该命令,除非我们的hadoop坏境变量如下设置。

 

# set hadoop path

export HADOOP_HOME=/usr/hadoop

export PATH=$PATH : $HADOOP_HOME :$HADOOP_HOME/bin

 

  这样就能直接使用"bin/hadoop"也可以直接使用"hadoop",现在不管哪种情况,hadoop命令都能找见了。我们也没有必要重新在设置hadoop环境变量了,只需要记住执行Hadoop命令时不需要在前面加"bin"就可以了。

 

 

    

  从上图中知道我们已经成功格式话了,但是美中不足就是出现了一个警告,从网上的得知这个警告并不影响hadoop执行,但是也有办法解决,详情看后面的"常见问题FAQ"。

    

  2)启动hadoop

  在启动前关闭集群中所有机器的防火墙,不然会出现datanode开后又自动关闭。

 

service iptables stop

 

  使用下面命令启动。

 

start-all.sh

 

 

  执行结果如下:

 

 

  可以通过以下启动日志看出,首先启动namenode 接着启动datanode1,datanode2,…,然后启动secondarynamenode。再启动jobtracker,然后启动tasktracker1,tasktracker2,…。

  启动 hadoop成功后,在 Master 中的 tmp 文件夹中生成了 dfs 文件夹,在Slave 中的 tmp 文件夹中均生成了 dfs 文件夹和 mapred 文件夹。

  查看Master中"/usr/hadoop/tmp"文件夹内容

 

 

  查看Slave1中"/usr/hadoop/tmp"文件夹内容。

 

 

  3)验证hadoop

  (1)验证方法一:用"jps"命令

  在Master上用 java自带的小工具jps查看进程。

 

 

  在Slave1上用jps查看进程。

 

 

  如果在查看Slave机器中发现"DataNode"和"TaskTracker"没有起来时,先查看一下日志的,如果是"namespaceID"不一致问题,采用"常见问题FAQ6.2"进行解决,如果是"No route to host"问题,采用"常见问题FAQ6.3"进行解决。

 

  (2)验证方式二:用"hadoop dfsadmin -report"

  用这个命令可以查看Hadoop集群的状态。

  Master服务器的状态:

 

 

  Slave服务器的状态

 

 

4.4 网页查看集群

  1)访问"http:192.168.1.2:50030"

 

 

  2)访问"http:192.168.1.2:50070"

 

 

5、常见问题FAQ

 

5.1 关于 Warning: $HADOOP_HOME is deprecated.

  hadoop 1.0.0版本,安装完之后敲入hadoop命令时,是提示这个警告:

 

Warning: $HADOOP_HOME is deprecated.

 

  经查hadoop-1.0.0/bin/hadoop脚本和"hadoop-config.sh"脚本,发现脚本中对HADOOP_HOME的环境变量设置做了判断,笔者的环境根本不需要设置HADOOP_HOME环境变量。

  解决方案一:编辑"/etc/profile"文件,去掉HADOOP_HOME的变量设定,重新输入hadoop fs命令,警告消失。

  解决方案二:编辑"/etc/profile"文件,添加一个环境变量,之后警告消失:

 

export HADOOP_HOME_WARN_SUPPRESS=1

 

  解决方案三:编辑"hadoop-config.sh"文件,把下面的"if - fi"功能注释掉。

 

 

  我们这里本着不动Hadoop原配置文件的前提下,采用"方案二",在"/etc/profile"文件添加上面内容,并用命令"source /etc/profile"使之有效。

  1)切换至root用户

 

 

  2)添加内容

 

 

  3)重新生效

 

 

5.2 解决"no datanode to stop"问题

  当我停止Hadoop时发现如下信息:

 

 

  原因:每次namenode format会重新创建一个namenodeId,而tmp/dfs/data下包含了上次format下的id,namenode format清空了namenode下的数据,但是没有清空datanode下的数据,导致启动时失败,所要做的就是每次fotmat前,清空tmp一下的所有目录。

  第一种解决方案如下:

  1)先删除"/usr/hadoop/tmp"

 

rm -rf /usr/hadoop/tmp

 

  2)创建"/usr/hadoop/tmp"文件夹

 

mkdir /usr/hadoop/tmp

 

  3)删除"/tmp"下以"hadoop"开头文件

 

rm -rf /tmp/hadoop*

 

  4)重新格式化hadoop

 

hadoop namenode -format

 

  5)启动hadoop

 

start-all.sh

 

  使用第一种方案,有种不好处就是原来集群上的重要数据全没有了。假如说Hadoop集群已经运行了一段时间。建议采用第二种。

  第二种方案如下:

  1)修改每个Slave的namespaceID使其与Master的namespaceID一致。

  或者

  2)修改Master的namespaceID使其与Slave的namespaceID一致。

 

  该"namespaceID"位于"/usr/hadoop/tmp/dfs/data/current/VERSION"文件中,前面蓝色的可能根据实际情况变化,但后面红色是不变的。

  例如:查看"Master"下的"VERSION"文件

 

 

  本人建议采用第二种,这样方便快捷,而且还能防止误删。

 

5.3 Slave服务器中datanode启动后又自动关闭

  查看日志发下如下错误。

  ERROR org.apache.hadoop.hdfs.server.datanode.DataNode: java.io.IOException: Call to ... failed on local exception: java.net.NoRouteToHostException: No route to host

  解决方案是:关闭防火墙

 

service iptables stop

 

5.4 从本地往hdfs文件系统上传文件

  出现如下错误:

  INFO hdfs.DFSClient: Exception in createBlockOutputStream java.io.IOException: Bad connect ack with firstBadLink

  INFO hdfs.DFSClient: Abandoning block blk_-1300529705803292651_37023

  WARN hdfs.DFSClient: DataStreamer Exception: java.io.IOException: Unable to create new block.

  解决方案是:

  1)关闭防火墙

 

service iptables stop

 

  2)禁用selinux

    编辑 "/etc/selinux/config"文件,设置"SELINUX=disabled"

 

5.5 安全模式导致的错误

  出现如下错误:

  org.apache.hadoop.dfs.SafeModeException: Cannot delete ..., Name node is in safe mode

  在分布式文件系统启动的时候,开始的时候会有安全模式,当分布式文件系统处于安全模式的情况下,文件系统中的内容不允许修改也不允许删除,直到安全模式结束。安全模式主要是为了系统启动的时候检查各个DataNode上数据块的有效性,同时根据策略必要的复制或者删除部分数据块。运行期通过命令也可以进入安全模式。在实践过程中,系统启动的时候去修改和删除文件也会有安全模式不允许修改的出错提示,只需要等待一会儿即可。

  解决方案是:关闭安全模式

 

hadoop dfsadmin -safemode leave

 

5.6 解决Exceeded MAX_FAILED_UNIQUE_FETCHES

  出现错误如下:

  Shuffle Error: Exceeded MAX_FAILED_UNIQUE_FETCHES; bailing-out

  程序里面需要打开多个文件,进行分析,系统一般默认数量是1024,(用ulimit -a可以看到)对于正常使用是够了,但是对于程序来讲,就太少了。

  解决方案是:修改2个文件。

  1)"/etc/security/limits.conf"

 

vim /etc/security/limits.conf

 

  加上:

 

soft nofile 102400

hard nofile 409600

 

  2)"/etc/pam.d/login"

 

vim /etc/pam.d/login

 

  添加:

 

session required /lib/security/pam_limits.so

 

  针对第一个问题我纠正下答案:

  这是reduce预处理阶段shuffle时获取已完成的map的输出失败次数超过上限造成的,上限默认为5。引起此问题的方式可能会有很多种,比如网络连接不正常,连接超时,带宽较差以及端口阻塞等。通常框架内网络情况较好是不会出现此错误的。

 

5.7 解决"Too many fetch-failures"

  出现这个问题主要是结点间的连通不够全面。

  解决方案是:

  1)检查"/etc/hosts"

  要求本机ip 对应 服务器名

  要求要包含所有的服务器ip +服务器名

  2)检查".ssh/authorized_keys"

  要求包含所有服务器(包括其自身)的public key

 

5.8 处理速度特别的慢

  出现map,但是reduce,而且反复出现"reduce=0%"。

  解决方案如下:

  结合解决方案5.7,然后修改"conf/hadoop-env.sh"中的"export HADOOP_HEAPSIZE=4000"

 

5.9解决hadoop OutOfMemoryError问题

  出现这种异常,明显是jvm内存不够得原因。

  解决方案如下:要修改所有的datanode的jvm内存大小。

 

Java –Xms 1024m -Xmx 4096m

 

  一般jvm的最大内存使用应该为总内存大小的一半,我们使用的8G内存,所以设置为4096m,这一值可能依旧不是最优的值。

 

5.10 Namenode in safe mode

  解决方案如下:

 

bin/hadoop dfsadmin -safemode leave

 

5.11 IO写操作出现问题

  0-1246359584298, infoPort=50075, ipcPort=50020):Got exception while serving blk_-5911099437886836280_1292 to /172.16.100.165:

  java.net.SocketTimeoutException: 480000 millis timeout while waiting for channel to be ready for write. ch : java.nio.channels.SocketChannel[connected local=/

  172.16.100.165:50010 remote=/172.16.100.165:50930]

  at org.apache.hadoop.net.SocketIOWithTimeout.waitForIO(SocketIOWithTimeout.java:185)

  at org.apache.hadoop.net.SocketOutputStream.waitForWritable(SocketOutputStream.java:159)

  ……

  It seems there are many reasons that it can timeout, the example given in HADOOP-3831 is a slow reading client.

    解决方案如下:

  在hadoop-site.xml中设置dfs.datanode.socket.write.timeout=0

 

5.12 status of 255 error

  错误类型:

  java.io.IOException: Task process exit with nonzero status of 255.

  at org.apache.hadoop.mapred.TaskRunner.run(TaskRunner.java:424)

 

  错误原因:

  Set mapred.jobtracker.retirejob.interval and mapred.userlog.retain.hours to higher value. By default, their values are 24 hours. These might be the reason for failure, though I'm not sure restart.

  解决方案如下:单个datanode

  如果一个datanode 出现问题,解决之后需要重新加入cluster而不重启cluster,方法如下:

 

bin/hadoop-daemon.sh start datanode

bin/hadoop-daemon.sh start jobtracker

 

 

6、用到的Linux命令

 

6.1 chmod命令详解

  使用权限:所有使用者

  使用方式:chmod [-cfvR] [--help] [--version] mode file...

  说明:

  Linux/Unix 的档案存取权限分为三级 : 档案拥有者、群组、其他。利用 chmod 可以藉以控制档案如何被他人所存取。

  mode :权限设定字串,格式如下 :[ugoa...][[+-=][rwxX]...][,...],其中u 表示该档案的拥有者,g 表示与该档案的拥有者属于同一个群体(group)者,o 表示其他以外的人,a 表示这三者皆是。

 

+ 表示增加权限、- 表示取消权限、= 表示唯一设定权限。

r 表示可读取,w 表示可写入,x 表示可执行,X 表示只有当该档案是个子目录或者该档案已经被设定过为可执行。

 

-c : 若该档案权限确实已经更改,才显示其更改动作

-f : 若该档案权限无法被更改也不要显示错误讯息

-v : 显示权限变更的详细资料

-R : 对目前目录下的所有档案与子目录进行相同的权限变更(即以递回的方式逐个变更)

--help : 显示辅助说明

--version : 显示版本

 

  范例:

  将档案 file1.txt 设为所有人皆可读取

 

chmod ugo+r file1.txt

 

  将档案 file1.txt 设为所有人皆可读取

 

chmod a+r file1.txt

 

  将档案 file1.txt 与 file2.txt 设为该档案拥有者,与其所属同一个群体者可写入,但其他以外的人则不可写入

 

chmod ug+w,o-w file1.txt file2.txt

 

  将 ex1.py 设定为只有该档案拥有者可以执行

 

chmod u+x ex1.py

 

  将目前目录下的所有档案与子目录皆设为任何人可读取

 

chmod -R a+r *

 

  此外chmod也可以用数字来表示权限如 chmod 777 file

  语法为:chmod abc file

  其中a,b,c各为一个数字,分别表示User、Group、及Other的权限。

  r=4,w=2,x=1

  若要rwx属性则4+2+1=7;

  若要rw-属性则4+2=6;

  若要r-x属性则4+1=7。

  范例:

  chmod a=rwx file 和 chmod 777 file 效果相同

  chmod ug=rwx,o=x file 和 chmod 771 file 效果相同

  若用chmod 4755 filename可使此程式具有root的权限

 

6.2 chown命令详解

  使用权限:root

  使用方式:chown [-cfhvR] [--help] [--version] user[:group] file...

  说明:

  Linux/Unix 是多人多工作业系统,所有的档案皆有拥有者。利用 chown 可以将档案的拥有者加以改变。一般来说,这个指令只有是由系统管理者(root)所使用,一般使用者没有权限可以改变别人的档案拥有者,也没有权限可以自己的档案拥有者改设为别人。只有系统管理者(root)才有这样的权限。

 

user : 新的档案拥有者的使用者

IDgroup : 新的档案拥有者的使用者群体(group)

-c : 若该档案拥有者确实已经更改,才显示其更改动作

-f : 若该档案拥有者无法被更改也不要显示错误讯息

-h : 只对于连结(link)进行变更,而非该 link 真正指向的档案

-v : 显示拥有者变更的详细资料

-R : 对目前目录下的所有档案与子目录进行相同的拥有者变更(即以递回的方式逐个变更)

--help : 显示辅助说明

--version : 显示版本

 

  范例:

  将档案 file1.txt 的拥有者设为 users 群体的使用者 jessie

 

chown jessie:users file1.txt

 

  将目前目录下的所有档案与子目录的拥有者皆设为 users 群体的使用者 lamport

 

chown -R lamport:users *

 

-rw------- (600) -- 只有属主有读写权限。

-rw-r--r-- (644) -- 只有属主有读写权限;而属组用户和其他用户只有读权限。

-rwx------ (700) -- 只有属主有读、写、执行权限。

-rwxr-xr-x (755) -- 属主有读、写、执行权限;而属组用户和其他用户只有读、执行权限。

-rwx--x--x (711) -- 属主有读、写、执行权限;而属组用户和其他用户只有执行权限。

-rw-rw-rw- (666) -- 所有用户都有文件读、写权限。这种做法不可取。

-rwxrwxrwx (777) -- 所有用户都有读、写、执行权限。更不可取的做法。

 

  以下是对目录的两个普通设定:

 

drwx------ (700) - 只有属主可在目录中读、写。

drwxr-xr-x (755) - 所有用户可读该目录,但只有属主才能改变目录中的内容

 

  suid的代表数字是4,比如4755的结果是-rwsr-xr-x

  sgid的代表数字是2,比如6755的结果是-rwsr-sr-x

  sticky位代表数字是1,比如7755的结果是-rwsr-sr-t

 

6.3 scp命令详解

  scp是 secure copy的缩写,scp是linux系统下基于ssh登陆进行安全的远程文件拷贝命令。linux的scp命令可以在linux服务器之间复制文件和目录。

  scp命令的用处:

  scp在网络上不同的主机之间复制文件,它使用ssh安全协议传输数据,具有和ssh一样的验证机制,从而安全的远程拷贝文件。

  scp命令基本格式:

 

scp [-1246BCpqrv] [-c cipher] [-F ssh_config] [-i identity_file]

[-l limit] [-o ssh_option] [-P port] [-S program]

[[user@]host1:]file1 [...] [[user@]host2:]file2

 

  scp命令的参数说明:

 

-1    强制scp命令使用协议ssh1

-2    强制scp命令使用协议ssh2

-4    强制scp命令只使用IPv4寻址

-6    强制scp命令只使用IPv6寻址

-B    使用批处理模式(传输过程中不询问传输口令或短语)

-C    允许压缩。(将-C标志传递给ssh,从而打开压缩功能)

-p 保留原文件的修改时间,访问时间和访问权限。

-q    不显示传输进度条。

-r    递归复制整个目录。

-v 详细方式显示输出。scp和ssh(1)会显示出整个过程的调试信息。这些信息用于调试连接,验证和配置问题。

-c cipher    以cipher将数据传输进行加密,这个选项将直接传递给ssh。

-F ssh_config    指定一个替代的ssh配置文件,此参数直接传递给ssh。

-i identity_file        从指定文件中读取传输时使用的密钥文件,此参数直接传递给ssh。

-l limit    限定用户所能使用的带宽,以Kbit/s为单位。

-o ssh_option    如果习惯于使用ssh_config(5)中的参数传递方式,

-P port 注意是大写的P, port是指定数据传输用到的端口号

-S program    指定加密传输时所使用的程序。此程序必须能够理解ssh(1)的选项。

 

  scp命令的实际应用

  1)从本地服务器复制到远程服务器    

  (1) 复制文件:

  命令格式:

 

scp local_file remote_username@remote_ip:remote_folder

  或者

scp local_file remote_username@remote_ip:remote_file

  或者

scp local_file remote_ip:remote_folder

  或者

scp local_file remote_ip:remote_file

 

  第1,2个指定了用户名,命令执行后需要输入用户密码,第1个仅指定了远程的目录,文件名字不变,第2个指定了文件名

  第3,4个没有指定用户名,命令执行后需要输入用户名和密码,第3个仅指定了远程的目录,文件名字不变,第4个指定了文件名

  实例:

 

scp /home/linux/soft/scp.zip root@www.mydomain.com:/home/linux/others/soft

scp /home/linux/soft/scp.zip root@www.mydomain.com:/home/linux/others/soft/scp2.zip

scp /home/linux/soft/scp.zip www.mydomain.com:/home/linux/others/soft

scp /home/linux/soft/scp.zip www.mydomain.com:/home/linux/others/soft/scp2.zip

 

  (2) 复制目录:

  命令格式:

 

scp -r local_folder remote_username@remote_ip:remote_folder

  或者

scp -r local_folder remote_ip:remote_folder

 

  第1个指定了用户名,命令执行后需要输入用户密码;

  第2个没有指定用户名,命令执行后需要输入用户名和密码;

 

  例子:

 

scp -r /home/linux/soft/ root@www.mydomain.com:/home/linux/others/

scp -r /home/linux/soft/ www.mydomain.com:/home/linux/others/

 

  上面 命令 将 本地 soft 目录 复制 到 远程 others 目录下,即复制后远程服务器上会有/home/linux/others/soft/ 目录。

 

  2)从远程服务器复制到本地服务器

  从远程复制到本地的scp命令与上面的命令雷同,只要将从本地复制到远程的命令后面2个参数互换顺序就行了。

  例如:

 

scp root@www.mydomain.com:/home/linux/soft/scp.zip /home/linux/others/scp.zip

scp www.mydomain.com:/home/linux/soft/ -r /home/linux/others/

 

  linux系统下scp命令中很多参数都和ssh1有关,还需要看到更原汁原味的参数信息,可以运行man scp 看到更细致的英文说明。

 

  文章下载地址:http://files.cnblogs.com/xia520pi/HadoopCluster_Vol.5.rar

Hadoop集群(第5期副刊)_JDK和SSH无密码配置

 

1、Linux配置java环境变量

 

1.1 原文出处

    地址:http://blog.csdn.net/jiedushi/article/details/6672894

 

1.2 解压安装jdk

  在shell终端下进入jdk-6u14-linux-i586.bin文件所在目录,执行命令 ./jdk-6u14-linux-i586.bin 这时会出现一段协议,连继敲回车,当询问是否同意的时候,输入yes,回车。之后会在当前目录下生成一个jdk1.6.0_14目录,你可以将它复制到 任何一个目录下。

 

1.3 需要配置的环境变量

  1)PATH环境变量

  作用是指定命令搜索路径,在shell下面执行命令时,它会到PATH变量所指定的路径中查找看是否能找到相应的命令程序。我们需要把 jdk安装目录下的bin目录增加到现有的PATH变量中,bin目录中包含经常要用到的可执行文件如javac/java/javadoc等待,设置好 PATH变量后,就可以在任何目录下执行javac/java等工具了。

  2)CLASSPATH环境变量

  作用是指定类搜索路径,要使用已经编写好的类,前提当然是能够找到它们了,JVM就是通过CLASSPTH来寻找类的。我们 需要把jdk安装目录下的lib子目录中的dt.jar和tools.jar设置到CLASSPATH中,当然,当前目录"."也必须加入到该变量中。

  3)JAVA_HOME环境变量

  它指向jdk的安装目录,Eclipse/NetBeans/Tomcat等软件就是通过搜索JAVA_HOME变量来找到并使用安装好的jdk。

 

1.4 三种配置环境变量的方法

  1)修改/etc/profile文件

  如果你的计算机仅仅作为开发使用时推荐使用这种方法,因为所有用户的shell都有权使用这些环境变量,可能会给系统带来安全性问题。

  • 用文本编辑器打开/etc/profile
  • 在profile文件末尾加入:

 

export JAVA_HOME=/usr/share/jdk1.6.0_14

export PATH=$JAVA_HOME/bin:$PATH

export CLASSPATH=.:$JAVA_HOME/lib/dt.jar:$JAVA_HOME/lib/tools.jar

 

  • 重新登录
  • 注解

 

a. 你要将 /usr/share/jdk1.6.0_14改为你的jdk安装目录

b. linux下用冒号":"来分隔路径

c. $PATH / $CLASSPATH / $JAVA_HOME 是用来引用原来的环境变量的值

在设置环境变量时特别要注意不能把原来的值给覆盖掉了,这是一种

常见的错误。

d. CLASSPATH中当前目录"."不能丢,把当前目录丢掉也是常见的错误。

e. export是把这三个变量导出为全局变量。

f. 大小写必须严格区分。

 

  2)修改.bash_profile文件

  这种方法更为安全,它可以把使用这些环境变量的权限控制到用户级别,如果你需要给某个用户权限使用这些环境变量,你只需要修改其个人用户主目录下的.bash_profile文件就可以了。

  • 用文本编辑器打开用户目录下的.bash_profile文件
  • 在.bash_profile文件末尾加入:

 

export JAVA_HOME=/usr/share/jdk1.6.0_14

export PATH=$JAVA_HOME/bin:$PATH

export CLASSPATH=.:$JAVA_HOME/lib/dt.jar:$JAVA_HOME/lib/tools.jar

 

  • 重新登录

 

  3)直接在shell下设置变量

  不赞成使用这种方法,因为换个shell,你的设置就无效了,因此这种方法仅仅是临时使用,以后要使用的时候又要重新设置,比较麻烦。

  只需在shell终端执行下列命令:

 

export JAVA_HOME=/usr/share/jdk1.6.0_14

export PATH=$JAVA_HOME/bin:$PATH

export CLASSPATH=.:$JAVA_HOME/lib/dt.jar:$JAVA_HOME/lib/tools.jar

 

 

  1.5 测试jdk

  1)用文本编辑器新建一个Test.java文件,在其中输入以下代码并保存:

 

public class test {

    public static void main(String args[]) {

        System.out.println("A new jdk test !");

    }

}

 

  2)编译:

  在shell终端执行命令 javac Test.java

  3)运行:

  在shell终端执行命令 java Test

   当shell下出现"A new jdk test !"字样则jdk运行正常。

 

1.6 卸载jdk

  • 找到jdk安装目录的_uninst子目录
  • 在shell终端执行命令./uninstall.sh即可卸载jdk。

 

2、配置OpenSSH无密码登陆

 

2.1 原文出处

    地址:http://www.iteye.com/topic/421608

 

2.2 文章序言

  最近在搭建Hadoop环境需要设置无密码登陆,所谓无密码登陆其实是指通过证书认证的方式登陆,使用一种被称为"公私钥"认证的方式来进行ssh登录。

   在linux系统中,ssh是远程登录的默认工具,因为该工具的协议使用了RSA/DSA的加密算法.该工具做linux系统的远程管理是非常安全的。telnet,因为其不安全性,在linux系统中被搁置使用了。

  " 公私钥"认证方式简单的解释:首先在客户端上创建一对公私钥 (公钥文件:~/.ssh/id_rsa.pub; 私钥文件:~/.ssh/id_rsa)。然后把公钥放到服务器上(~/.ssh/authorized_keys), 自己保留好私钥.在使用ssh登录时,ssh程序会发送私钥去和服务器上的公钥做匹配.如果匹配成功就可以登录了。

  在Ubuntu和Cygwin 配置都很顺利,而在Centos系统中配置时遇到了很多问题。故此文以Centos(Centos5 ) 为例详细讲解如何配置证书验证登陆,具体操作步骤如下:

 

2.3 确认系统已经安装好OpenSSH的server 和client

  安装步骤这里不再讲述,不是本文的重点。

 

2.4 确认本机sshd的配置文件(root)

 

$ vi /etc/ssh/sshd_config

 

  找到以下内容,并去掉注释符"#"

 

RSAAuthentication yes

PubkeyAuthentication yes

AuthorizedKeysFile .ssh/authorized_keys

 

 

2.5 如果修改了配置文件需要重启sshd服务(root)

 

$ vi /sbin/service sshd restart

 

 

2.6 ssh登陆系统 后执行测试命令

 

$ ssh localhost

 

  回车会提示你输入密码,因为此时我们还没有生成证书。

 

2.7 生成证书公私钥的步骤

 

$ ssh-keygen -t dsa -P '' -f ~/.ssh/id_dsa

$ cat ~/.ssh/id_dsa.pub >> ~/.ssh/authorized_keys

 

2.8 测试登陆 ssh localhost

 

$ ssh localhost

 

  正常情况下会登陆成功,显示一些成功登陆信息,如果失败请看下面的"一般调试步骤"。

 

2.9 一般调试步骤

  本人在配置时就失败了,按照以上步骤依旧提示要输入密码。于是用ssh -v 显示详细的登陆信息查找原因:

 

$ ssh -v localhost

 

  回车显示了详细的登陆信息如下:

 

。。。。。。省略

debug1: Authentications that can continue: publickey,gssapi-with-mic,password

debug1: Next authentication method: gssapi-with-mic

debug1: Unspecified GSS failure. Minor code may provide more information

Unknown code krb5 195

debug1: Unspecified GSS failure. Minor code may provide more information

Unknown code krb5 195

debug1: Unspecified GSS failure. Minor code may provide more information

Unknown code krb5 195

debug1: Next authentication method: publickey

debug1: Trying private key: /home/huaxia/.ssh/identity

debug1: Trying private key: /home/huaxia/.ssh/id_rsa

debug1: Offering public key: /home/huaxia/.ssh/id_dsa

debug1: Authentications that can continue: publickey,gssapi-with-mic,password

debug1: Next authentication method: password

huaxia@localhost's password:

 

  同时用root用户登陆查看系统的日志文件:

 

$tail /var/log/secure -n 20

 

。。。。。。省略

Jul 13 11:21:05 shnap sshd[3955]: Accepted password for huaxia from 192.168.8.253 port 51837 ssh2

Jul 13 11:21:05 shnap sshd[3955]: pam_unix(sshd:session): session opened for user huaxia by (uid=0)

Jul 13 11:21:47 shnap sshd[4024]: Connection closed by 127.0.0.1

Jul 13 11:25:28 shnap sshd[4150]: Authentication refused: bad ownership or modes for file /home/huaxia/.ssh/authorized_keys

Jul 13 11:25:28 shnap sshd[4150]: Authentication refused: bad ownership or modes for file /home/huaxia/.ssh/authorized_keys

Jul 13 11:26:30 shnap sshd[4151]: Connection closed by 127.0.0.1

。。。。。。省略

 

  从上面的日志信息中可知文件/home/huaxia/.ssh/authorized_keys 的权限有问题。

  查看/home/huaxia/.ssh/ 下文件的详细信息如下:

 

$ ls -lh ~/.ssh/

总计 16K

-rw-rw-r-- 1 huaxia huaxia 602 07-13 11:22 authorized_keys

-rw------- 1 huaxia huaxia 672 07-13 11:22 id_dsa

-rw-r--r-- 1 huaxia huaxia 602 07-13 11:22 id_dsa.pub

-rw-r--r-- 1 huaxia huaxia 391 07-13 11:21 known_hosts

 

  修改文件authorized_keys的权限(权限的设置非常重要,因为不安全的设置安全设置,会让你不能使用RSA功能 ):

 

$ chmod 600 ~/.ssh/authorized_keys

 

  再次测试登陆如下:

 

$ ssh localhost

Last login: Wed Jul 13 14:04:06 2011 from 192.168.8.253

 

  看到这样的信息表示已经成功实现了本机的无密码登陆。

 

2.10 认证登陆远程服务器

  备注:远程服务器OpenSSH的服务当然要启动。

  拷贝本地生产的key到远程服务器端(两种方法)

  1)方法一:

 

$cat ~/.ssh/id_rsa.pub | ssh 远程用户名@远程服务器ip 'cat - >> ~/.ssh/authorized_keys'

 

  2)方法二:

  在本机上执行:

 

$ scp ~/.ssh/id_dsa.pub michael@192.168.8.148:/home/michael/

 

  登陆远程服务器michael@192.168.8.148 后执行:

 

$ cat id_dsa.pub >> ~/.ssh/authorized_keys

 

  本机远程登陆192.168.8.148的测试:

 

$ssh michael@192.168.8.148

Linux michael-VirtualBox 2.6.35-22-generic #33-Ubuntu SMP Sun Sep 19 20:34:50 UTC 2010 i686 GNU/Linux

Ubuntu 10.10

 

Welcome to Ubuntu!

* Documentation: https://help.ubuntu.com/

 

216 packages can be updated.

71 updates are security updates.

 

New release 'natty' available.

Run 'do-release-upgrade' to upgrade to it.

 

Last login: Wed Jul 13 14:46:37 2011 from michael-virtualbox

michael@michael-VirtualBox:~$

 

  可见已经成功登陆。

  如果登陆测试不成功,需要修改远程服务器192.168.8.148上的文件authorized_keys的权限(权限的设置非常重要,因为不安全的设置安全设置,会让你不能使用RSA功能

 

chmod 600 ~/.ssh/authorized_keys

 

   文章下载地址:http://files.cnblogs.com/xia520pi/HadoopCluster_Vol.5S.rar

Hadoop集群(第6期)_WordCount运行详解

 

1、MapReduce理论简介

 

1.1 MapReduce编程模型

  MapReduce采用"分而治之"的思想,把对大规模数据集的操作,分发给一个主节点管理下的各个分节点共同完成,然后通过整合各个节点的中间结果,得到最终结果。简单地说,MapReduce就是"任务的分解与结果的汇总"。

  在Hadoop中,用于执行MapReduce任务的机器角色有两个:一个是JobTracker;另一个是TaskTracker,JobTracker是用于调度工作的,TaskTracker是用于执行工作的。一个Hadoop集群中只有一台JobTracker。

  在分布式计算中,MapReduce框架负责处理了并行编程中分布式存储、工作调度、负载均衡、容错均衡、容错处理以及网络通信等复杂问题,把处理过程高度抽象为两个函数:map和reduce,map负责把任务分解成多个任务,reduce负责把分解后多任务处理的结果汇总起来。

  需要注意的是,用MapReduce来处理的数据集(或任务)必须具备这样的特点:待处理的数据集可以分解成许多小的数据集,而且每一个小数据集都可以完全并行地进行处理。

 

1.2 MapReduce处理过程

  在Hadoop中,每个MapReduce任务都被初始化为一个Job,每个Job又可以分为两种阶段:map阶段和reduce阶段。这两个阶段分别用两个函数表示,即map函数和reduce函数。map函数接收一个<key,value>形式的输入,然后同样产生一个<key,value>形式的中间输出,Hadoop函数接收一个如<key,(list of values)>形式的输入,然后对这个value集合进行处理,每个reduce产生0或1个输出,reduce的输出也是<key,value>形式的。

 

 image

MapReduce处理大数据集的过程

 

 

2、运行WordCount程序

  单词计数是最简单也是最能体现MapReduce思想的程序之一,可以称为MapReduce版"Hello World",该程序的完整代码可以在Hadoop安装包的"src/examples"目录下找到。单词计数主要完成功能是:统计一系列文本文件中每个单词出现的次数,如下图所示。

 

 image

 

2.1 准备工作

  现在以"hadoop"普通用户登录"Master.Hadoop"服务器。

  1)创建本地示例文件

  首先在"/home/hadoop"目录下创建文件夹"file"。

 

 image

 

  接着创建两个文本文件file1.txt和file2.txt,使file1.txt内容为"Hello World",而file2.txt的内容为"Hello Hadoop"。

 

image

 

  2)在HDFS上创建输入文件夹

 

image

 

  3)上传本地file中文件到集群的input目录下

 

image

 

2.2 运行例子

  1)在集群上运行WordCount程序

  备注:以input作为输入目录,output目录作为输出目录。

  已经编译好的WordCount的Jar在"/usr/hadoop"下面,就是"hadoop-examples-1.0.0.jar",所以在下面执行命令时记得把路径写全了,不然会提示找不到该Jar包。

 

 image

 

  2)MapReduce执行过程显示信息

 

image

 

  Hadoop命令会启动一个JVM来运行这个MapReduce程序,并自动获得Hadoop的配置,同时把类的路径(及其依赖关系)加入到Hadoop的库中。以上就是Hadoop Job的运行记录,从这里可以看到,这个Job被赋予了一个ID号:job_201202292213_0002,而且得知输入文件有两个(Total input paths to process : 2),同时还可以了解map的输入输出记录(record数及字节数),以及reduce输入输出记录。比如说,在本例中,map的task数量是2个,reduce的task数量是一个。map的输入record数是2个,输出record数是4个等信息。

 

2.3 查看结果

  1)查看HDFS上output目录内容

 

image

 

  从上图中知道生成了三个文件,我们的结果在"part-r-00000"中。

  2)查看结果输出文件内容

 

image

 

3、WordCount源码分析

 

3.1 特别数据类型介绍

  Hadoop提供了如下内容的数据类型,这些数据类型都实现了WritableComparable接口,以便用这些类型定义的数据可以被序列化进行网络传输和文件存储,以及进行大小比较。

 

    BooleanWritable:标准布尔型数值

    ByteWritable:单字节数值

    DoubleWritable:双字节数

    FloatWritable:浮点数

    IntWritable:整型数

    LongWritable:长整型数

    Text:使用UTF8格式存储的文本

    NullWritable:当<key,value>中的key或value为空时使用

 

3.2 旧的WordCount分析

  1)源代码程序

 

package org.apache.hadoop.examples;

import java.io.IOException;
import java.util.Iterator;
import java.util.StringTokenizer;

import org.apache.hadoop.fs.Path;
import org.apache.hadoop.io.IntWritable;
import org.apache.hadoop.io.LongWritable;
import org.apache.hadoop.io.Text;
import org.apache.hadoop.mapred.FileInputFormat;
import org.apache.hadoop.mapred.FileOutputFormat;
import org.apache.hadoop.mapred.JobClient;
import org.apache.hadoop.mapred.JobConf;
import org.apache.hadoop.mapred.MapReduceBase;
import org.apache.hadoop.mapred.Mapper;
import org.apache.hadoop.mapred.OutputCollector;
import org.apache.hadoop.mapred.Reducer;
import org.apache.hadoop.mapred.Reporter;
import org.apache.hadoop.mapred.TextInputFormat;
import org.apache.hadoop.mapred.TextOutputFormat;

public class WordCount {

    public static class Map extends MapReduceBase implements
            Mapper<LongWritable, Text, Text, IntWritable> {
        private final static IntWritable one = new IntWritable(1);
        private Text word = new Text();

        public void map(LongWritable key, Text value,
                OutputCollector<Text, IntWritable> output, Reporter reporter)
                throws IOException {
            String line = value.toString();
            StringTokenizer tokenizer = new StringTokenizer(line);
            while (tokenizer.hasMoreTokens()) {
                word.set(tokenizer.nextToken());
                output.collect(word, one);
            }
        }
    }

    public static class Reduce extends MapReduceBase implements
            Reducer<Text, IntWritable, Text, IntWritable> {
        public void reduce(Text key, Iterator<IntWritable> values,
                OutputCollector<Text, IntWritable> output, Reporter reporter)
                throws IOException {
            int sum = 0;
            while (values.hasNext()) {
                sum += values.next().get();
            }
            output.collect(key, new IntWritable(sum));
        }
    }

    public static void main(String[] args) throws Exception {
        JobConf conf = new JobConf(WordCount.class);
        conf.setJobName("wordcount");

        conf.setOutputKeyClass(Text.class);
        conf.setOutputValueClass(IntWritable.class);

        conf.setMapperClass(Map.class);
        conf.setCombinerClass(Reduce.class);
        conf.setReducerClass(Reduce.class);

        conf.setInputFormat(TextInputFormat.class);
        conf.setOutputFormat(TextOutputFormat.class);

        FileInputFormat.setInputPaths(conf, new Path(args[0]));
        FileOutputFormat.setOutputPath(conf, new Path(args[1]));

        JobClient.runJob(conf);
    }
}

 

  3)主方法Main分析

 

public static void main(String[] args) throws Exception {
    JobConf conf = new JobConf(WordCount.class);
    conf.setJobName("wordcount");

    conf.setOutputKeyClass(Text.class);
    conf.setOutputValueClass(IntWritable.class);

    conf.setMapperClass(Map.class);
    conf.setCombinerClass(Reduce.class);
    conf.setReducerClass(Reduce.class);

    conf.setInputFormat(TextInputFormat.class);
    conf.setOutputFormat(TextOutputFormat.class);

    FileInputFormat.setInputPaths(conf, new Path(args[0]));
    FileOutputFormat.setOutputPath(conf, new Path(args[1]));

    JobClient.runJob(conf);
}

 

  首先讲解一下Job初始化过程main函数调用Jobconf类来对MapReduce Job进行初始化,然后调用setJobName()方法命名这个Job。对Job进行合理的命名有助于更快地找到Job,以便在JobTracker和Tasktracker的页面中对其进行监视

 

JobConf conf = new JobConf(WordCount. class ); conf.setJobName("wordcount" );

 

  接着设置Job输出结果<key,value>的中key和value数据类型,因为结果是<单词,个数>,所以key设置为"Text"类型,相当于Java中String类型。Value设置为"IntWritable",相当于Java中的int类型。

 

conf.setOutputKeyClass(Text.class );

conf.setOutputValueClass(IntWritable.class );

 

  然后设置Job处理的Map(拆分)、Combiner(中间结果合并)以及Reduce(合并)的相关处理类。这里用Reduce类来进行Map产生的中间结果合并,避免给网络数据传输产生压力。

 

conf.setMapperClass(Map.class );

conf.setCombinerClass(Reduce.class );

conf.setReducerClass(Reduce.class );

 

  接着就是调用setInputPath()和setOutputPath()设置输入输出路径。

 

conf.setInputFormat(TextInputFormat.class );

conf.setOutputFormat(TextOutputFormat.class );

 

  (1)InputFormat和InputSplit

  InputSplit是Hadoop定义的用来传送给每个单独map数据,InputSplit存储的并数据本身而是一个分片长度和一个记录数据位置数组生成InputSplit的方法可以通过InputFormat()设置

  当数据传送给map时,map会将输入分片传送到InputFormat,InputFormat则调用方法getRecordReader()生成RecordReaderRecordReader再通过creatKey()creatValue()方法创建可供map处理的<key,value>对。简而言之,InputFormat()方法是用来生成可供map处理的<key,value>对的。

  Hadoop预定义了多种方法将不同类型的输入数据转化为map能够处理的<key,value>对,它们都继承自InputFormat,分别是:

 

    InputFormat

        |

        |---BaileyBorweinPlouffe.BbpInputFormat

        |---ComposableInputFormat

        |---CompositeInputFormat

        |---DBInputFormat

        |---DistSum.Machine.AbstractInputFormat

        |---FileInputFormat

            |---CombineFileInputFormat

            |---KeyValueTextInputFormat

            |---NLineInputFormat

            |---SequenceFileInputFormat

            |---TeraInputFormat

            |---TextInputFormat

 

  其中TextInputFormat是Hadoop默认的输入方法,在TextInputFormat中,每个文件(或其一部分)都会单独地作为map的输入,而这个是继承自FileInputFormat的。之后,每行数据都会生成一条记录,每条记录则表示成<key,value>形式:

  • key值是每个数据的记录在 数据分片字节偏移量,数据类型是 LongWritable;  

value值是每行的内容,数据类型是Text

  (2)OutputFormat

  每一种格式都有一种格式与其对应。默认的输出格式是TextOutputFormat,这种输出方式与输入类似,会将每条记录以一行的形式存入文本文件。不过,它的键和值可以是任意形式的,因为程序内容会调用toString()方法将键和值转换为String类型再输出。

 

  3)Map类中map方法分析

 

public static class Map extends MapReduceBase implements
        Mapper<LongWritable, Text, Text, IntWritable> {
    private final static IntWritable one = new IntWritable(1);
    private Text word = new Text();

    public void map(LongWritable key, Text value,
            OutputCollector<Text, IntWritable> output, Reporter reporter)
            throws IOException {
        String line = value.toString();
        StringTokenizer tokenizer = new StringTokenizer(line);
        while (tokenizer.hasMoreTokens()) {
            word.set(tokenizer.nextToken());
            output.collect(word, one);
        }
    }
}

 

  Map类继承自MapReduceBase,并且它实现了Mapper接口,此接口是一个规范类型,它有4种形式的参数,分别用来指定map的输入key值类型、输入value值类型、输出key值类型和输出value值类型。在本例中,因为使用的是TextInputFormat,它的输出key值是LongWritable类型,输出value值是Text类型,所以map的输入类型为<LongWritable,Text>。在本例中需要输出<word,1>这样的形式,因此输出的key值类型是Text,输出的value值类型是IntWritable。

  实现此接口类还需要实现map方法,map方法会具体负责对输入进行操作,在本例中,map方法对输入的行以空格为单位进行切分,然后使用OutputCollect收集输出的<word,1>。

 

  4)Reduce类中reduce方法分析

 

public static class Reduce extends MapReduceBase implements
        Reducer<Text, IntWritable, Text, IntWritable> {
    public void reduce(Text key, Iterator<IntWritable> values,
            OutputCollector<Text, IntWritable> output, Reporter reporter)
            throws IOException {
        int sum = 0;
        while (values.hasNext()) {
            sum += values.next().get();
        }
        output.collect(key, new IntWritable(sum));
    }
}

 

  Reduce类也是继承自MapReduceBase的,需要实现Reducer接口。Reduce类以map的输出作为输入,因此Reduce的输入类型是<Text,Intwritable>。而Reduce的输出是单词它的数目,因此,它的输出类型是<Text,IntWritable>。Reduce类也要实现reduce方法,在此方法中,reduce函数将输入的key值作为输出的key值,然后将获得多个value值加起来,作为输出的值。

 

3.3 新的WordCount分析

  1)源代码程序

 

package org.apache.hadoop.examples;

import java.io.IOException;

import java.util.StringTokenizer;

import org.apache.hadoop.conf.Configuration;

import org.apache.hadoop.fs.Path;

import org.apache.hadoop.io.IntWritable;

import org.apache.hadoop.io.Text;

import org.apache.hadoop.mapreduce.Job;

import org.apache.hadoop.mapreduce.Mapper;

import org.apache.hadoop.mapreduce.Reducer;

import org.apache.hadoop.mapreduce.lib.input.FileInputFormat;

import org.apache.hadoop.mapreduce.lib.output.FileOutputFormat;

import org.apache.hadoop.util.GenericOptionsParser;

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> <out>");

      System.exit(2);

    }

    Job job = new Job(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);

    FileInputFormat.addInputPath(job, new Path(otherArgs[0]));

    FileOutputFormat.setOutputPath(job, new Path(otherArgs[1]));

    System.exit(job.waitForCompletion(true) ? 0 : 1);

}

}

 

   1)Map过程

 

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);

  }

}

 

  Map过程需要继承org.apache.hadoop.mapreduce包中Mapper类,并重写其map方法。通过在map方法中添加两句把key值和value值输出到控制台的代码,可以发现map方法中value值存储的是文本文件中的一行(以回车符为行结束标记),而key值为该行的首字母相对于文本文件的首地址的偏移量。然后StringTokenizer类将每一行拆分成为一个个的单词,并将<word,1>作为map方法的结果输出,其余的工作都交有MapReduce框架处理。

 

  2)Reduce过程

 

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);

  }

}

 

  Reduce过程需要继承org.apache.hadoop.mapreduce包中Reducer类,并重写其reduce方法。Map过程输出<key,values>中key为单个单词,而values是对应单词的计数值所组成的列表,Map的输出就是Reduce的输入,所以reduce方法只要遍历values并求和,即可得到某个单词的总次数。

 

    3)执行MapReduce任务

 

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> <out>");

    System.exit(2);

  }

  Job job = new Job(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);

  FileInputFormat.addInputPath(job, new Path(otherArgs[0]));

  FileOutputFormat.setOutputPath(job, new Path(otherArgs[1]));

  System.exit(job.waitForCompletion(true) ? 0 : 1);

}

 

  在MapReduce中,由Job对象负责管理和运行一个计算任务,并通过Job的一些方法对任务的参数进行相关的设置。此处设置了使用TokenizerMapper完成Map过程中的处理和使用IntSumReducer完成Combine和Reduce过程中的处理。还设置了Map过程和Reduce过程的输出类型:key的类型为Text,value的类型为IntWritable。任务的输出和输入路径则由命令行参数指定,并由FileInputFormat和FileOutputFormat分别设定。完成相应任务的参数设定后,即可调用job.waitForCompletion()方法执行任务。

 

4、WordCount处理过程

  本节将对WordCount进行更详细的讲解。详细执行步骤如下:

 

  1)将文件拆分成splits,由于测试用的文件较小,所以每个文件为一个split,并将文件按行分割形成<key,value>对,如图4-1所示。这一步由MapReduce框架自动完成,其中偏移量(即key值)包括了回车所占的字符数(Windows和Linux环境会不同)。

 

 image

图4-1 分割过程

 

  2)将分割好的<key,value>对交给用户定义的map方法进行处理,生成新的<key,value>对,如图4-2所示。

 

 image

图4-2 执行map方法

 

  3)得到map方法输出的<key,value>对后,Mapper会将它们按照key值进行排序,并执行Combine过程,将key至相同value值累加,得到Mapper的最终输出结果。如图4-3所示。

 

 image

图4-3 Map端排序及Combine过程

 

  4)Reducer先对从Mapper接收的数据进行排序,再交由用户自定义的reduce方法进行处理,得到新的<key,value>对,并作为WordCount的输出结果,如图4-4所示。

 

 image

图4-4 Reduce端排序及输出结果

 

5、MapReduce新旧改变

  Hadoop最新版本的MapReduce Release 0.20.0的API包括了一个全新的Mapreduce JAVA API,有时候也称为上下文对象。

  新的API类型上不兼容以前的API,所以,以前的应用程序需要重写才能使新的API发挥其作用 。

  新的API和旧的API之间有下面几个明显的区别。

  • 新的API倾向于使用抽象类,而不是接口,因为这更容易扩展。例如,你可以添加一个方法(用默认的实现)到一个抽象类而不需修改类之前的实现方法。在新的API中,Mapper和Reducer是抽象类。
  • 新的API是在org.apache.hadoop.mapreduce包(和子包)中的。之前版本的API则是放在org.apache.hadoop.mapred中的。
  • 新的API广泛使用context object(上下文对象),并允许用户代码与MapReduce系统进行通信。例如,MapContext基本上充当着JobConf的OutputCollector和Reporter的角色。
  • 新的API同时支持"推"和"拉"式的迭代。在这两个新老API中,键/值记录对被推mapper中,但除此之外,新的API允许把记录从map()方法中拉出,这也适用于reducer。"拉"式的一个有用的例子是分批处理记录,而不是一个接一个。
  • 新的API统一了配置。旧的API有一个特殊的JobConf对象用于作业配置,这是一个对于Hadoop通常的Configuration对象的扩展。在新的API中,这种区别没有了,所以作业配置通过Configuration来完成。作业控制的执行由Job类来负责,而不是JobClient,它在新的API中已经荡然无存。

     

      文章下载地址:http://files.cnblogs.com/xia520pi/HadoopCluster_Vol.6.rar

Hadoop集群(第7期)_Eclipse开发环境设置

1、Hadoop开发环境简介

1.1 Hadoop集群简介

  Java版本:jdk-6u31-linux-i586.bin

  Linux系统:CentOS6.0

  Hadoop版本:hadoop-1.0.0.tar.gz

1.2 Windows开发简介

  Java版本:jdk-6u31-windows-i586.exe

  Win系统:Windows 7 旗舰版

  Eclipse软件:eclipse-jee-indigo-SR1-win32.zip | eclipse-jee-helios-SR2-win32.zip

  Hadoop软件:hadoop-1.0.0.tar.gz

  Hadoop Eclipse 插件:hadoop-eclipse-plugin-1.0.0.jar

  下载地址:http://download.csdn.net/detail/xia520pi/4113746

  备注:下面是网上收集的收集的"hadoop-eclipse-plugin-1.0.0.jar",除"版本2.0"是根据"V1.0"按照"常见问题FAQ_1"改的之外,剩余的"V3.0"、"V4.0"和"V5.0"和"V2.0"一样是别人已经弄好的,而且我已经都测试过,没有任何问题,可以放心使用。我们这里选择第"V5.0"使用。记得在使用时重新命名为"hadoop-eclipse-plugin-1.0.0.jar"。

 

2、Hadoop Eclipse简介和使用

2.1 Eclipse插件介绍

  Hadoop是一个强大的并行框架,它允许任务在其分布式集群上并行处理。但是编写、调试Hadoop程序都有很大难度。正因为如此,Hadoop的开发者开发出了Hadoop Eclipse插件,它在Hadoop的开发环境中嵌入了Eclipse,从而实现了开发环境的图形化,降低了编程难度。在安装插件,配置Hadoop的相关信息之后,如果用户创建Hadoop程序,插件会自动导入Hadoop编程接口的JAR文件,这样用户就可以在Eclipse的图形化界面中编写、调试、运行Hadoop程序(包括单机程序和分布式程序),也可以在其中查看自己程序的实时状态、错误信息和运行结果,还可以查看、管理HDFS以及文件。总地来说,Hadoop Eclipse插件安装简单,使用方便,功能强大,尤其是在Hadoop编程方面,是Hadoop入门和Hadoop编程必不可少的工具。

2.2 Hadoop工作目录简介

  为了以后方便开发,我们按照下面把开发中用到的软件安装在此目录中,JDK安装除外,我这里把JDK安装在C盘的默认安装路径下,下面是我的工作目录:

 

    系统磁盘(E:)

        |---HadoopWorkPlat

            |--- eclipse

            |--- hadoop-1.0.0

            |--- workplace

            |---……

 

  按照上面目录把Eclipse和Hadoop解压到"E:\HadoopWorkPlat"下面,并创建"workplace"作为Eclipse的工作空间。

 

 

  备注:大家可以按照自己的情况,不一定按照我的结构来设计。

2.3 修改系统管理员名字

  经过两天多次探索,为了使Eclipse能正常对Hadoop集群的HDFS上的文件能进行修改和删除,所以修改你工作时所用的Win7系统管理员名字,默认一般为"Administrator",把它修改为"hadoop",此用户名与Hadoop集群普通用户一致,大家应该记得我们Hadoop集群中所有的机器都有一个普通用户——hadoop,而且Hadoop运行也是用这个用户进行的。为了不至于为权限苦恼,我们可以修改Win7上系统管理员的姓名,这样就避免出现该用户在Hadoop集群上没有权限等都疼问题,会导致在Eclipse中对Hadoop集群的HDFS创建和删除文件受影响。

  你可以做一下实验,查看Master.Hadoop机器上"/usr/hadoop/logs"下面的日志。发现权限不够,不能进行"Write"操作,网上有几种解决方案,但是对Hadoop1.0不起作用,详情见"常见问题FAQ_2"。下面我们进行修改管理员名字。

  首先"右击"桌面上图标"我的电脑",选择"管理",弹出界面如下:

 

 

  接着选择"本地用户和组",展开"用户",找到系统管理员"Administrator",修改其为"hadoop",操作结果如下图:

 

 

  最后,把电脑进行"注销"或者"重启电脑",这样才能使管理员才能用这个名字。

2.4 Eclipse插件开发配置

  第一步:把我们的"hadoop-eclipse-plugin-1.0.0.jar"放到Eclipse的目录的"plugins"中,然后重新Eclipse即可生效。

 

    系统磁盘(E:)

        |---HadoopWorkPlat

            |--- eclipse

                |--- plugins

                    |--- hadoop-eclipse-plugin-1.0.0.jar

 

  上面是我的"hadoop-eclipse-plugin"插件放置的地方。重启Eclipse如下图:

 

 

  细心的你从上图中左侧"Project Explorer"下面发现"DFS Locations",说明Eclipse已经识别刚才放入的Hadoop Eclipse插件了。

 

  第二步:选择"Window"菜单下的"Preference",然后弹出一个窗体,在窗体的左侧,有一列选项,里面会多出"Hadoop Map/Reduce"选项,点击此选项,选择Hadoop的安装目录(如我的Hadoop目录:E:\HadoopWorkPlat\hadoop-1.0.0)。结果如下图:

 

 

  第三步:切换"Map/Reduce"工作目录,有两种方法:

  1)选择"Window"菜单下选择"Open Perspective",弹出一个窗体,从中选择"Map/Reduce"选项即可进行切换。

 

 

  2)在Eclipse软件的右上角,点击图标""中的"",点击"Other"选项,也可以弹出上图,从中选择"Map/Reduce",然后点击"OK"即可确定。

  切换到"Map/Reduce"工作目录下的界面如下图所示。

 

 

  第四步:建立与Hadoop集群的连接,在Eclipse软件下面的"Map/Reduce Locations"进行右击,弹出一个选项,选择"New Hadoop Location",然后弹出一个窗体。

 

 

 

  注意上图中的红色标注的地方,是需要我们关注的地方。

  • Location Name:可以任意其,标识一个"Map/Reduce Location"
  • Map/Reduce Master
    Host:192.168.1.2( Master.Hadoop的IP地址)
    Port:9001
  • DFS Master
    Use M/R Master host:前面的 勾上。(因为我们的NameNode和JobTracker都在一个机器上。)
    Port:9000
  • User name:hadoop(默认为Win系统管理员名字,因为我们之前改了所以这里就变成了hadoop。)

 

 

  备注:这里面的Host、Port分别为你在mapred-site.xml、core-site.xml中配置的地址及端口。不清楚的可以参考"Hadoop集群_第5期_Hadoop安装配置_V1.0"进行查看。

   接着点击"Advanced parameters"从中找见"hadoop.tmp.dir",修改成为我们Hadoop集群中设置的地址,我们的Hadoop集群是"/usr/hadoop/tmp",这个参数在"core-site.xml"进行了配置。

 

 

  点击"finish"之后,会发现Eclipse软件下面的"Map/Reduce Locations"出现一条信息,就是我们刚才建立的"Map/Reduce Location"。

 

    

  第五步:查看HDFS文件系统,并尝试建立文件夹和上传文件。点击Eclipse软件左侧的"DFS Locations"下面的"Win7ToHadoop",就会展示出HDFS上的文件结构。

 

    

  右击"Win7ToHadoopàuseràhadoop"可以尝试建立一个"文件夹--xiapi",然后右击刷新就能查看我们刚才建立的文件夹。

 

 

  创建完之后,并刷新,显示结果如下:

 

 

  用SecureCRT远程登录"Master.Hadoop"服务器,用下面命令查看是否已经建立一个"xiapi"的文件夹。

 

hadoop fs -ls

 

    

  到此为止,我们的Hadoop Eclipse开发环境已经配置完毕,不尽兴的同学可以上传点本地文件到HDFS分布式文件上,可以互相对比意见文件是否已经上传成功。

3、Eclipse运行WordCount程序

3.1 配置Eclipse的JDK

  如果电脑上不仅仅安装的JDK6.0,那么要确定一下Eclipse的平台的默认JDK是否6.0。从"Window"菜单下选择"Preference",弹出一个窗体,从窗体的左侧找见"Java",选择"Installed JREs",然后添加JDK6.0。下面是我的默认选择JRE。

 

 

  下面是没有添加之前的设置如下:

 

 

  下面是添加完JDK6.0之后结果如下:

 

 

  接着设置Complier。

 

 

3.2 设置Eclipse的编码为UTF-8

 

 

3.3 创建MapReduce项目

   从"File"菜单,选择"Other",找到"Map/Reduce Project",然后选择它。

 

    

  接着,填写MapReduce工程的名字为"WordCountProject",点击"finish"完成。

 

 

  目前为止我们已经成功创建了MapReduce项目,我们发现在Eclipse软件的左侧多了我们的刚才建立的项目。

 

 

3.4 创建WordCount类

  选择"WordCountProject"工程,右击弹出菜单,然后选择"New",接着选择"Class",然后填写如下信息:

 

 

  因为我们直接用Hadoop1.0.0自带的WordCount程序,所以报名需要和代码中的一致为"org.apache.hadoop.examples",类名也必须一致为"WordCount"。这个代码放在如下的结构中。

 

    hadoop-1.0.0

        |---src

            |---examples

                |---org

                    |---apache

                        |---hadoop

                            |---examples

 

  从上面目录中找见"WordCount.java"文件,用记事本打开,然后把代码复制到刚才建立的java文件中。当然源码有些变动,变动的红色已经标记出。

 

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
package org.apache.hadoop.examples;
 
import java.io.IOException;
import java.util.StringTokenizer;
 
import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.fs.Path;
import org.apache.hadoop.io.IntWritable;
import org.apache.hadoop.io.Text;
import org.apache.hadoop.mapreduce.Job;
import org.apache.hadoop.mapreduce.Mapper;
import org.apache.hadoop.mapreduce.Reducer;
import org.apache.hadoop.mapreduce.lib.input.FileInputFormat;
import org.apache.hadoop.mapreduce.lib.output.FileOutputFormat;
import org.apache.hadoop.util.GenericOptionsParser;
 
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 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();
     conf.set( "mapred.job.tracker" , "192.168.1.2:9001" );
     String[] ars= new String[]{ "input" , "newout" };
     String[] otherArgs = new GenericOptionsParser(conf, ars).getRemainingArgs();
     if (otherArgs.length != 2 ) {
       System.err.println( "Usage: wordcount  " );
       System.exit( 2 );
     }
     Job job = new Job(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 );
     FileInputFormat.addInputPath(job, new Path(otherArgs[ 0 ]));
     FileOutputFormat.setOutputPath(job, new Path(otherArgs[ 1 ]));
     System.exit(job.waitForCompletion( true ) ? 0 : 1 );
   }
}

 

  备注:如果不加"conf.set("mapred.job.tracker", "192.168.1.2:9001");",将提示你的权限不够,其实照成这样的原因是刚才设置的"Map/Reduce Location"其中的配置不是完全起作用,而是在本地的磁盘上建立了文件,并尝试运行,显然是不行的。我们要让Eclipse提交作业到Hadoop集群上,所以我们这里手动添加Job运行地址。详细参考"常见问题FAQ_3"。

3.5 运行WordCount程序

  选择"Wordcount.java"程序,右击一次按照"Run ASàRun on Hadoop"运行。然后会弹出如下图,按照下图进行操作。

 

 

  运行结果如下:

 

 

  从上图中我们得知我们的程序已经运行成功了。

3.6 查看WordCount运行结果

  查看Eclipse软件左侧,右击"DFS LocationsàWin7ToHadoopàuseràhadoop",点击刷新按钮"Refresh",我们刚才出现的文件夹"newoutput"会出现。记得"newoutput"文件夹是运行程序时自动创建的,如果已经存在相同的的文件夹,要么程序换个新的输出文件夹,要么删除HDFS上的那个重名文件夹,不然会出错。

 

 

  打开"newoutput"文件夹,打开"part-r-00000"文件,可以看见执行后的结果。

 

 

  到此为止,Eclipse开发环境设置已经完毕,并且成功运行Wordcount程序,下一步我们真正开始Hadoop之旅。

4、常见问题FAQ

4.1 "error: failure to login"问题

  下面以网上找的"hadoop-0.20.203.0"为例,我在使用"V1.0"时也出现这样的情况,原因就是那个"hadoop-eclipse-plugin-1.0.0_V1.0.jar",是直接把源码编译而成,故而缺少相应的Jar包。具体情况如下

  详细地址:http://blog.csdn.net/chengfei112233/article/details/7252404

  在我实践尝试中,发现hadoop-0.20.203.0版本的该包如果直接复制到eclipse的插件目录中,在连接DFS时会出现错误,提示信息为: "error: failure to login"。

  弹出的错误提示框内容为"An internal error occurred during: "Connecting to DFS hadoop".org/apache/commons/configuration/Configuration". 经过察看Eclipse的log,发现是缺少jar包导致的。进一步查找资料后,发现直接复制hadoop-eclipse-plugin-0.20.203.0.jar,该包中lib目录下缺少了jar包。

  经过网上资料搜集,此处给出正确的安装方法:

  首先要对hadoop-eclipse-plugin-0.20.203.0.jar进行修改。用归档管理器打开该包,发现只有commons-cli-1.2.jar 和hadoop-core.jar两个包。将hadoop/lib目录下的:

  • commons-configuration-1.6.jar ,
  • commons-httpclient-3.0.1.jar ,
  • commons-lang-2.4.jar ,
  • jackson-core-asl-1.0.1.jar
  • jackson-mapper-asl-1.0.1.jar

一共5个包复制到hadoop-eclipse-plugin-0.20.203.0.jar的lib目录下,如下图:

 

 

  然后,修改该包META-INF目录下的MANIFEST.MF,将classpath修改为一下内容:

 

Bundle-ClassPath:classes/,lib/hadoop-core.jar,lib/commons-cli-1.2.jar,lib/commons-httpclient-3.0.1.jar,lib/jackson-core-asl-1.0.1.jar,lib/jackson-mapper-asl-1.0.1.jar,lib/commons-configuration-1.6.jar,lib/commons-lang-2.4.jar

 

 

  这样就完成了对hadoop-eclipse-plugin-0.20.203.0.jar的修改。

  最后,将hadoop-eclipse-plugin-0.20.203.0.jar复制到Eclipse的plugins目录下。

  备注:上面的操作对"hadoop-1.0.0"一样适用。

4.2 "Permission denied"问题

  网上试了很多,有提到"hadoop fs -chmod 777 /user/hadoop ",有提到"dfs.permissions 的配置项,将value值改为 false",有提到"hadoop.job.ugi",但是通通没有效果。

  参考文献:

        地址1:http://www.cnblogs.com/acmy/archive/2011/10/28/2227901.html

        地址2:http://sunjun041640.blog.163.com/blog/static/25626832201061751825292/

    错误类型:org.apache.hadoop.security.AccessControlException: org.apache.hadoop.security .AccessControlException: Permission denied: user=*********, access=WRITE, inode="hadoop": hadoop:supergroup:rwxr-xr-x

    解决方案:

    我的解决方案直接把系统管理员的名字改成你的Hadoop集群运行hadoop的那个用户。

4.3 "Failed to set permissions of path"问题

   参考文献:https://issues.apache.org/jira/browse/HADOOP-8089

   错误信息如下:

    ERROR security.UserGroupInformation: PriviledgedActionException as: hadoop cause:java.io.IOException Failed to set permissions of path:\usr\hadoop\tmp\mapred\staging\hadoop753422487\.staging to 0700 Exception in thread "main" java.io.IOException: Failed to set permissions of path: \usr\hadoop\tmp \mapred\staging\hadoop753422487\.staging to 0700

   解决方法:

 

Configuration conf = new Configuration();

conf.set("mapred.job.tracker", "[server]:9001");

 

   "[server]:9001"中的"[server]"为Hadoop集群Master的IP地址。

4.4 "hadoop mapred执行目录文件权"限问题

   参考文献:http://blog.csdn.net/azhao_dn/article/details/6921398

   错误信息如下:

   job Submission failed with exception 'java.io.IOException(The ownership/permissions on the staging directory /tmp/hadoop-hadoop-user1/mapred/staging/hadoop-user1/.staging is not as expected. It is owned by hadoop-user1 and permissions are rwxrwxrwx. The directory must be owned by the submitter hadoop-user1 or by hadoop-user1 and permissions must be rwx------)

   修改权限:

 

 

  这样就能解决问题。

 

  文章下载地址:http://files.cnblogs.com/xia520pi/HadoopCluster_Vol.7.rar




Hadoop集群(第8期)_HDFS初探之旅

1、HDFS简介

  HDFS(Hadoop Distributed File System)是Hadoop项目的核心子项目,是分布式计算中数据存储管理的基础,是基于流数据模式访问和处理超大文件的需求而开发的,可以运行于廉价的商用服务器上。它所具有的高容错、高可靠性、高可扩展性、高获得性、高吞吐率等特征为海量数据提供了不怕故障的存储,为超大数据集(Large Data Set)的应用处理带来了很多便利。

  Hadoop整合了众多文件系统,在其中有一个综合性的文件系统抽象,它提供了文件系统实现的各类接口,HDFS只是这个抽象文件系统的一个实例。提供了一个高层的文件系统抽象类org.apache.hadoop.fs.FileSystem,这个抽象类展示了一个分布式文件系统,并有几个具体实现,如下表1-1所示。

表1-1 Hadoop的文件系统

文件系统

URI方案

Java实现

org.apache.hadoop

定义

Local

file

fs.LocalFileSystem

支持有客户端校验和本地文件系统。带有校验和的本地系统文件在fs.RawLocalFileSystem中实现。

HDFS

hdfs

hdfs.DistributionFileSystem

Hadoop的分布式文件系统。

HFTP

hftp

hdfs.HftpFileSystem

支持通过HTTP方式以只读的方式访问HDFSdistcp经常用在不同HDFS集群间复制数据。

HSFTP

hsftp

hdfs.HsftpFileSystem

支持通过HTTPS方式以只读的方式访问HDFS

HAR

har

fs.HarFileSystem

构建在Hadoop文件系统之上,对文件进行归档。Hadoop归档文件主要用来减少NameNode内存使用

KFS

kfs

fs.kfs.KosmosFileSystem

Cloudstore(其前身是Kosmos文件系统)文件系统是类似于HDFSGoogleGFS文件系统,使用C++编写。

FTP

ftp

fs.ftp.FtpFileSystem

FTP服务器支持的文件系统。

S3(本地)

s3n

fs.s3native.NativeS3FileSystem

基于Amazon S3的文件系统。

S3(基于块)

s3 

fs.s3.NativeS3FileSystem

基于Amazon S3的文件系统,以块格式存储解决了S35GB文件大小的限制。

  Hadoop提供了许多文件系统的接口,用户可以使用URI方案选取合适的文件系统来实现交互。

2、HDFS基础概念

2.1 数据块(block)

  • HDFS(Hadoop Distributed File System)默认的最基本的存储单位是64M的数据块。
  • 和普通文件系统相同的是,HDFS中的文件是被分成64M一块的数据块存储的。
  • 不同于普通文件系统的是,HDFS中,如果一个文件小于一个数据块的大小,并不占用整个数据块存储空间。

2.2 NameNode和DataNode

  HDFS体系结构中有两类节点,一类是NameNode,又叫"元数据节点";另一类是DataNode,又叫"数据节点"。这两类节点分别承担Master和Worker具体任务的执行节点。

  1)元数据节点用来管理文件系统的命名空间

  • 其将所有的文件和文件夹的元数据保存在一个文件系统树中。
  • 这些信息也会在硬盘上保存成以下文件:命名空间镜像(namespace image)及修改日志(edit log)
  • 其还保存了一个文件包括哪些数据块,分布在哪些数据节点上。然而这些信息并不存储在硬盘上,而是在系统启动的时候从数据节点收集而成的。

  2)数据节点是文件系统中真正存储数据的地方。

  • 客户端(client)或者元数据信息(namenode)可以向数据节点请求写入或者读出数据块。
  • 其周期性的向元数据节点回报其存储的数据块信息。

  3)从元数据节点(secondary namenode)

  • 从元数据节点并不是元数据节点出现问题时候的备用节点,它和元数据节点负责不同的事情。
  • 其主要功能就是周期性将元数据节点的命名空间镜像文件和修改日志合并,以防日志文件过大。这点在下面会相信叙述。
  • 合并过后的命名空间镜像文件也在从元数据节点保存了一份,以防元数据节点失败的时候,可以恢复。

2.3 元数据节点目录结构

 

  

 

  VERSION文件是java properties文件,保存了HDFS的版本号。

  • layoutVersion是一个负整数,保存了HDFS的持续化在硬盘上的数据结构的格式版本号。
  • namespaceID是文件系统的唯一标识符,是在文件系统初次格式化时生成的。
  • cTime此处为0
  • storageType表示此文件夹中保存的是元数据节点的数据结构。

 

namespaceID=1232737062

cTime=0

storageType=NAME_NODE

layoutVersion=-18

 

2.4 数据节点的目录结构

 

  

  • 数据节点的VERSION文件格式如下:

 

namespaceID=1232737062

storageID=DS-1640411682-127.0.1.1-50010-1254997319480

cTime=0

storageType=DATA_NODE

layoutVersion=-18

 

  • blk_<id>保存的是HDFS的数据块,其中保存了具体的二进制数据。
  • blk_<id>.meta保存的是数据块的属性信息:版本信息,类型信息,和checksum
  • 当一个目录中的数据块到达一定数量的时候,则创建子文件夹来保存数据块及数据块属性信息。

2.5 文件系统命名空间映像文件及修改日志

  • 当文件系统客户端(client)进行写操作时,首先把它记录在修改日志中(edit log)
  • 元数据节点在内存中保存了文件系统的元数据信息。在记录了修改日志后,元数据节点则修改内存中的数据结构。
  • 每次的写操作成功之前,修改日志都会同步(sync)到文件系统。
  • fsimage文件,也即命名空间映像文件,是内存中的元数据在硬盘上的checkpoint,它是一种序列化的格式,并不能够在硬盘上直接修改。
  • 同数据的机制相似,当元数据节点失败时,则最新checkpoint的元数据信息从fsimage加载到内存中,然后逐一重新执行修改日志中的操作。
  • 从元数据节点就是用来帮助元数据节点将内存中的元数据信息checkpoint到硬盘上的
  • checkpoint的过程如下:
    • 从元数据节点通知元数据节点生成新的日志文件,以后的日志都写到新的日志文件中。
    • 从元数据节点用http get从元数据节点获得fsimage文件及旧的日志文件。
    • 从元数据节点将fsimage文件加载到内存中,并执行日志文件中的操作,然后生成新的fsimage文件。
    • 从元数据节点奖新的fsimage文件用http post传回元数据节点
    • 元数据节点可以将旧的fsimage文件及旧的日志文件,换为新的fsimage文件和新的日志文件(第一步生成的),然后更新fstime文件,写入此次checkpoint的时间。
    • 这样元数据节点中的fsimage文件保存了最新的checkpoint的元数据信息,日志文件也重新开始,不会变的很大了。

 

 

3、HDFS体系结构

  HDFS是一个主/从(Mater/Slave)体系结构,从最终用户的角度来看,它就像传统的文件系统一样,可以通过目录路径对文件执行CRUD(Create、Read、Update和Delete)操作。但由于分布式存储的性质,HDFS集群拥有一个NameNode和一些DataNode。NameNode管理文件系统的元数据,DataNode存储实际的数据。客户端通过同NameNode和DataNodes的交互访问文件系统。客户端联系NameNode以获取文件的元数据,而真正的文件I/O操作是直接和DataNode进行交互的。

 

图3.1 HDFS总体结构示意图

 

  1)NameNode、DataNode和Client

  • NameNode可以看作是分布式文件系统中的管理者,主要负责管理文件系统的命名空间、集群配置信息和存储块的复制等。NameNode会将文件系统的Meta-data存储在内存中,这些信息主要包括了文件信息、每一个文件对应的文件块的信息和每一个文件块在DataNode的信息等。
  • DataNode是文件存储的基本单元,它将Block存储在本地文件系统中,保存了Block的Meta-data,同时周期性地将所有存在的Block信息发送给NameNode。
  • Client就是需要获取分布式文件系统文件的应用程序。

  2)文件写入

  • Client向NameNode发起文件写入的请求。
  • NameNode根据文件大小和文件块配置情况,返回给Client它所管理部分DataNode的信息。
  • Client将文件划分为多个Block,根据DataNode的地址信息,按顺序写入到每一个DataNode块中。

  3)文件读取

  • Client向NameNode发起文件读取的请求。
  • NameNode返回文件存储的DataNode的信息。
  • Client读取文件信息。

 

  HDFS典型的部署是在一个专门的机器上运行NameNode,集群中的其他机器各运行一个DataNode;也可以在运行NameNode的机器上同时运行DataNode,或者一台机器上运行多个DataNode。一个集群只有一个NameNode的设计大大简化了系统架构。

4、HDFS的优缺点

4.1 HDFS的优点

  1)处理超大文件

  这里的超大文件通常是指百MB、设置数百TB大小的文件。目前在实际应用中,HDFS已经能用来存储管理PB级的数据了。

  2)流式的访问数据

  HDFS的设计建立在更多地响应"一次写入、多次读写"任务的基础上。这意味着一个数据集一旦由数据源生成,就会被复制分发到不同的存储节点中,然后响应各种各样的数据分析任务请求。在多数情况下,分析任务都会涉及数据集中的大部分数据,也就是说,对HDFS来说,请求读取整个数据集要比读取一条记录更加高效

  3)运行于廉价的商用机器集群上

  Hadoop设计对硬件需求比较,只须运行在低廉的商用硬件集群上,而无需昂贵的高可用性机器上。廉价的商用机也就意味着大型集群中出现节点故障情况的概率非常高。这就要求设计HDFS时要充分考虑数据的可靠性,安全性及高可用性。

4.2 HDFS的缺点

  1)不适合低延迟数据访问

  如果要处理一些用户要求时间比较短的低延迟应用请求,则HDFS不适合。HDFS是为了处理大型数据集分析任务的,主要是为达到数据吞吐量而设计的,这就可能要求以高延迟作为代价。

  改进策略:对于那些有低延时要求的应用程序,HBase是一个更好的选择。通过上层数据管理项目来尽可能地弥补这个不足。在性能上有了很大的提升,它的口号就是goes real time。使用缓存或多master设计可以降低client的数据请求压力,以减少延时。还有就是对HDFS系统内部的修改,这就得权衡大吞吐量与低延时了,HDFS不是万能的银弹。

  2)无法高效存储大量小文件

  因为Namenode把文件系统的元数据放置在内存中,所以文件系统所能容纳的文件数目是由Namenode的内存大小来决定。一般来说,每一个文件文件夹Block需要占据150字节左右的空间,所以,如果你有100万个文件,每一个占据一个Block,你就至少需要300MB内存。当前来说,数百万的文件还是可行的,当扩展到数十亿时,对于当前的硬件水平来说就没法实现了。还有一个问题就是,因为Map task的数量是由splits来决定的,所以用MR处理大量的小文件时,就会产生过多的Maptask,线程管理开销将会增加作业时间。举个例子,处理10000M的文件,若每个split为1M,那就会有10000个Maptasks,会有很大的线程开销;若每个split为100M,则只有100个Maptasks,每个Maptask将会有更多的事情做,而线程的管理开销也将减小很多。

  改进策略:要想让HDFS能处理好小文件,有不少方法。

  • 利用SequenceFile、MapFile、Har等方式归档小文件,这个方法的原理就是把小文件归档起来管理,HBase就是基于此的。对于这种方法,如果想找回原来的小文件内容,那就必须得知道与归档文件的映射关系。
  • 横向扩展,一个Hadoop集群能管理的小文件有限,那就把几个Hadoop集群拖在一个虚拟服务器后面,形成一个大的Hadoop集群。google也是这么干过的。
  • 多Master设计,这个作用显而易见了。正在研发中的GFS II也要改为分布式多Master设计,还支持Master的Failover,而且Block大小改为1M,有意要调优处理小文件啊。
  • 附带个Alibaba DFS的设计,也是多Master设计,它把Metadata的映射存储和管理分开了,由多个Metadata存储节点和一个查询Master节点组成。

  3)不支持多用户写入及任意修改文件

  在HDFS的一个文件中只有一个写入者,而且写操作只能在文件末尾完成,即只能执行追加操作。目前HDFS还不支持多个用户同一文件操作,以及在文件任意位置进行修改。

5、HDFS常用操作

  先说一下"hadoop fshadoop dfs的区别",看两本Hadoop书上各有用到,但效果一样,求证与网络发现下面一解释比较中肯。

  粗略的讲,fs是个比较抽象的层面,在分布式环境中,fs就是dfs,但在本地环境中,fs是local file system,这个时候dfs就不能用。

5.1 文件操作

  1)列出HDFS文件

  此处为你展示如何通过"-ls"命令列出HDFS下的文件:

 

hadoop fs -ls

 

  执行结果如图5-1-1所示。在这里需要注意:在HDFS中未带参数的"-ls"命名没有返回任何值,它默认返回HDFS的"home"目录下的内容。在HDFS中,当前目录这样一个概念,也cd这个命令。

 

  

图5-1-1 列出HDFS文件

  2)列出HDFS目录下某个文档中的文件

  此处为你展示如何通过"-ls 文件名"命令浏览HDFS下名为"input"的文档中文件:

 

hadoop fs –ls input

 

  执行结果如图5-1-2所示。

 

  

图5-1-2 列出HDFS下名为input的文档下的文件

  3)上传文件到HDFS

  此处为你展示如何通过"-put 文件1 文件2"命令将"Master.Hadoop"机器下的"/home/hadoop"目录下的file文件上传到HDFS上并重命名test

 

hadoop fs –put ~/file test

 

  执行结果如图5-1-3所示。在执行"-put"时两种可能,即是执行成功执行失败。在上传文件时,文件首先复制到DataNode上。只有所有的DataNode都成功接收完数据,文件上传才是成功的。其他情况(如文件上传终端等)对HDFS来说都是做了无用功。

 

  

图5-1-3 成功上传file到HDFS

  4)将HDFS中文件复制本地系统

  此处为你展示如何通过"-get 文件1 文件2"命令将HDFS中的"output"文件复制到本地系统并命名为"getout"。

 

hadoop fs –get output getout

 

  执行结果如图5-1-4所示。

 

  

图5-1-4 成功将HDFS中output文件复制到本地系统

  备注:与"-put"命令一样,"-get"操作既可以操作文件,也可以操作目录

  5)删除HDFS下的文档

  此处为你展示如何通过"-rmr 文件"命令删除HDFS下名为"newoutput"的文档:

 

hadoop fs –rmr newoutput

 

  执行结果如图5-1-5所示。

 

  

图5-1-5 成功删除HDFS下的newoutput文档

  6)查看HDFS下某个文件

  此处为你展示如何通过"-cat 文件"命令查看HDFS下input文件中内容:

 

hadoop fs -cat input/*

 

  执行结果如图5-1-6所示。

 

  

图5-1-6 HDFS下input文件的内容

  "hadoop fs"的命令远不止这些,本小节介绍的命令已可以在HDFS上完成大多数常规操作。对于其他操作,可以通过"-help commandName"命令所列出的清单来进一步学习与探索。

5.2 管理与更新

  1)报告HDFS的基本统计情况

  此处为你展示通过"-report"命令如何查看HDFS的基本统计信息:

 

hadoop dfsadmin -report

 

  执行结果如图5-2-1所示。

 

  

图5-2-1 HDFS基本统计信息

  2)退出安全模式

  NameNode在启动时自动进入安全模式。安全模式是NameNode的一种状态,在这个阶段,文件系统不允许有任何修改。安全模式的目的是在系统启动时检查各个DataNode上数据块的有效性,同时根据策略对数据块进行必要的复制删除,当数据块最小百分比数满足的最小副本数条件时,会自动退出安全模式。

  系统显示"Name node is in safe mode",说明系统正处于安全模式,这时只需要等待17秒即可,也可以通过下面的命令退出安全模式:

 

hadoop dfsadmin –safemode enter

 

  成功退出安全模式结果如图5-2-2所示。

 

  

图5-2-2 成功退出安全模式

  3)进入安全模式

  在必要情况下,可以通过以下命令把HDFS置于安全模式:

 

hadoop dfsadmin –safemode enter

 

  执行结果如图5-2-3所示。

 

  

图5-2-3 进入HDFS安全模式

  4)添加节点

  可扩展性是HDFS的一个重要特性,向HDFS集群中添加节点是很容易实现的。添加一个新的DataNode节点,首先在新加节点上安装好Hadoop,要和NameNode使用相同的配置(可以直接从NameNode复制),修改"/usr/hadoop/conf/master"文件,加入NameNode主机名。然后在NameNode节点上修改"/usr/hadoop/conf/slaves"文件,加入新节点主机名,再建立到新加点无密码的SSH连接,运行启动命令:

 

start-all.sh

 

  5)负载均衡

  HDFS的数据在各个DataNode中的分布肯能很不均匀,尤其是在DataNode节点出现故障新增DataNode节点时。新增数据块时NameNode对DataNode节点的选择策略也有可能导致数据块分布的不均匀。用户可以使用命令重新平衡DataNode上的数据块的分布:

 

start-balancer.sh

 

  执行命令前,DataNode节点上数据分布情况如图5-2-4所示。

 

  

 

  负载均衡完毕后,DataNode节点上数据的分布情况如图5-2-5所示。

 

  

 

  执行负载均衡命令如图5-2-6所示。

 

  

 

6、HDFS API详解

  Hadoop中关于文件操作类基本上全部是在"org.apache.hadoop.fs"包中,这些API能够支持的操作包含:打开文件,读写文件,删除文件等。

Hadoop类库中最终面向用户提供的接口类FileSystem,该类是个抽象类,只能通过来类的get方法得到具体类。get方法存在几个重载版本,常用的是这个:

 

static FileSystem get(Configuration conf);

 

  该类封装了几乎所有的文件操作,例如mkdir,delete等。综上基本上可以得出操作文件的程序库框架:

 

operator()

{

    得到Configuration对象

    得到FileSystem对象

    进行文件操作

}

 

6.1 上传本地文件

  通过"FileSystem.copyFromLocalFile(Path src,Patch dst)"可将本地文件上传HDFS的制定位置上,其中src和dst均为文件的完整路径。具体事例如下:

 

package com.hebut.file;

 

import org.apache.hadoop.conf.Configuration;

import org.apache.hadoop.fs.FileStatus;

import org.apache.hadoop.fs.FileSystem;

import org.apache.hadoop.fs.Path;

 

public class CopyFile {

    public static void main(String[] args) throws Exception {

        Configuration conf=new Configuration();

        FileSystem hdfs=FileSystem.get(conf);

       

        //本地文件

        Path src =new Path("D:\\HebutWinOS");

        //HDFS为止

        Path dst =new Path("/");

       

        hdfs.copyFromLocalFile(src, dst);

        System.out.println("Upload to"+conf.get("fs.default.name"));

       

        FileStatus files[]=hdfs.listStatus(dst);

        for(FileStatus file:files){

            System.out.println(file.getPath());

        }

    }

}

 

  运行结果可以通过控制台、项目浏览器和SecureCRT查看,如图6-1-1、图6-1-2、图6-1-3所示。

  1)控制台结果

 

  

图6-1-1 运行结果(1)

  2)项目浏览器

 

  

图6-1-2 运行结果(2)

  3)SecureCRT结果

 

  

图6-1-3 运行结果(3)

6.2 创建HDFS文件

  通过"FileSystem.create(Path f)"可在HDFS上创建文件,其中f为文件的完整路径。具体实现如下:

 

package com.hebut.file;

 

import org.apache.hadoop.conf.Configuration;

import org.apache.hadoop.fs.FSDataOutputStream;

import org.apache.hadoop.fs.FileSystem;

import org.apache.hadoop.fs.Path;

 

public class CreateFile {

 

    public static void main(String[] args) throws Exception {

        Configuration conf=new Configuration();

        FileSystem hdfs=FileSystem.get(conf);

       

        byte[] buff="hello hadoop world!\n".getBytes();

       

        Path dfs=new Path("/test");

       

        FSDataOutputStream outputStream=hdfs.create(dfs);

        outputStream.write(buff,0,buff.length);

       

    }

}

 

  运行结果如图6-2-1和图6-2-2所示。

  1)项目浏览器

 

  

图6-2-1 运行结果(1)

  2)SecureCRT结果

 

  

图6-2-2 运行结果(2)

6.3 创建HDFS目录

  通过"FileSystem.mkdirs(Path f)"可在HDFS上创建文件夹,其中f为文件夹的完整路径。具体实现如下:

 

package com.hebut.dir;

 

import org.apache.hadoop.conf.Configuration;

import org.apache.hadoop.fs.FileSystem;

import org.apache.hadoop.fs.Path;

 

public class CreateDir {

 

    public static void main(String[] args) throws Exception{

        Configuration conf=new Configuration();

        FileSystem hdfs=FileSystem.get(conf);

       

        Path dfs=new Path("/TestDir");

       

        hdfs.mkdirs(dfs);

 

    }

}

 

  运行结果如图6-3-1和图6-3-2所示。

  1)项目浏览器

 

  

图6-3-1 运行结果(1)

  2)SecureCRT结果

 

  

图6-3-2 运行结果(2)

6.4 重命名HDFS文件

  通过"FileSystem.rename(Path src,Path dst)"可为指定的HDFS文件重命名,其中src和dst均为文件的完整路径。具体实现如下:

 

package com.hebut.file;

 

import org.apache.hadoop.conf.Configuration;

import org.apache.hadoop.fs.FileSystem;

import org.apache.hadoop.fs.Path;

 

public class Rename{

    public static void main(String[] args) throws Exception {

        Configuration conf=new Configuration();

        FileSystem hdfs=FileSystem.get(conf);

     

        Path frpaht=new Path("/test");    //旧的文件名

        Path topath=new Path("/test1");    //新的文件名

       

        boolean isRename=hdfs.rename(frpaht, topath);

       

        String result=isRename?"成功":"失败";

        System.out.println("文件重命名结果为:"+result);

       

    }

}

 

  运行结果如图6-4-1和图6-4-2所示。

  1)项目浏览器

 

  

图6-4-1 运行结果(1)

    2)SecureCRT结果

 

  

图6-4-2 运行结果(2)

6.5 删除HDFS上的文件

  通过"FileSystem.delete(Path f,Boolean recursive)"可删除指定的HDFS文件,其中f为需要删除文件的完整路径,recuresive用来确定是否进行递归删除。具体实现如下:

 

package com.hebut.file;

 

import org.apache.hadoop.conf.Configuration;

import org.apache.hadoop.fs.FileSystem;

import org.apache.hadoop.fs.Path;

 

public class DeleteFile {

 

    public static void main(String[] args) throws Exception {

        Configuration conf=new Configuration();

        FileSystem hdfs=FileSystem.get(conf);

       

        Path delef=new Path("/test1");

       

        boolean isDeleted=hdfs.delete(delef,false);

        //递归删除

        //boolean isDeleted=hdfs.delete(delef,true);

        System.out.println("Delete?"+isDeleted);

    }

}

 

  运行结果如图6-5-1和图6-5-2所示。

  1)控制台结果

 

  

图6-5-1 运行结果(1)

    2)项目浏览器

  

图6-5-2 运行结果(2)

6.6 删除HDFS上的目录

  同删除文件代码一样,只是换成删除目录路径即可,如果目录下有文件,要进行递归删除。

6.7 查看某个HDFS文件是否存在

  通过"FileSystem.exists(Path f)"可查看指定HDFS文件是否存在,其中f为文件的完整路径。具体实现如下:

 

package com.hebut.file;

 

import org.apache.hadoop.conf.Configuration;

import org.apache.hadoop.fs.FileSystem;

import org.apache.hadoop.fs.Path;

 

public class CheckFile {

    public static void main(String[] args) throws Exception {

        Configuration conf=new Configuration();

        FileSystem hdfs=FileSystem.get(conf);

        Path findf=new Path("/test1");

        boolean isExists=hdfs.exists(findf);

        System.out.println("Exist?"+isExists);

    }

}

 

  运行结果如图6-7-1和图6-7-2所示。

  1)控制台结果

 

  

图6-7-1 运行结果(1)

  2)项目浏览器

 

  

图6-7-2 运行结果(2)

6.8 查看HDFS文件的最后修改时间

  通过"FileSystem.getModificationTime()"可查看指定HDFS文件的修改时间。具体实现如下:

 

package com.hebut.file;

 

import org.apache.hadoop.conf.Configuration;

import org.apache.hadoop.fs.FileStatus;

import org.apache.hadoop.fs.FileSystem;

import org.apache.hadoop.fs.Path;

 

public class GetLTime {

 

    public static void main(String[] args) throws Exception {

        Configuration conf=new Configuration();

        FileSystem hdfs=FileSystem.get(conf);

       

        Path fpath =new Path("/user/hadoop/test/file1.txt");

       

        FileStatus fileStatus=hdfs.getFileStatus(fpath);

        long modiTime=fileStatus.getModificationTime();

       

        System.out.println("file1.txt的修改时间是"+modiTime);

    }

}

 

  运行结果如图6-8-1所示。

 

  

图6-8-1 控制台结果

6.9 读取HDFS某个目录下的所有文件

  通过"FileStatus.getPath()"可查看指定HDFS中某个目录下所有文件。具体实现如下:

 

package com.hebut.file;

 

import org.apache.hadoop.conf.Configuration;

import org.apache.hadoop.fs.FileStatus;

import org.apache.hadoop.fs.FileSystem;

import org.apache.hadoop.fs.Path;

 

public class ListAllFile {

    public static void main(String[] args) throws Exception {

        Configuration conf=new Configuration();

        FileSystem hdfs=FileSystem.get(conf);

       

        Path listf =new Path("/user/hadoop/test");

       

        FileStatus stats[]=hdfs.listStatus(listf);

        for(int i = 0; i < stats.length; ++i)

     {

       System.out.println(stats[i].getPath().toString());

     }

        hdfs.close();

    }

}

 

  运行结果如图6-9-1和图6-9-2所示。

  1)控制台结果

 

  

图6-9-1 运行结果(1)

 

  2)项目浏览器

 

  

图6-9-2 运行结果(2)

6.10 查找某个文件在HDFS集群的位置

  通过"FileSystem.getFileBlockLocation(FileStatus file,long start,long len)"可查找指定文件在HDFS集群上的位置,其中file为文件的完整路径,start和len来标识查找文件的路径。具体实现如下:

 

package com.hebut.file;

 

import org.apache.hadoop.conf.Configuration;

import org.apache.hadoop.fs.BlockLocation;

import org.apache.hadoop.fs.FileStatus;

import org.apache.hadoop.fs.FileSystem;

import org.apache.hadoop.fs.Path;

 

public class FileLoc {

    public static void main(String[] args) throws Exception {

        Configuration conf=new Configuration();

        FileSystem hdfs=FileSystem.get(conf);

        Path fpath=new Path("/user/hadoop/cygwin");

       

        FileStatus filestatus = hdfs.getFileStatus(fpath);

        BlockLocation[] blkLocations = hdfs.getFileBlockLocations(filestatus, 0, filestatus.getLen());

 

        int blockLen = blkLocations.length;

        for(int i=0;i<blockLen;i++){

            String[] hosts = blkLocations[i].getHosts();

            System.out.println("block_"+i+"_location:"+hosts[0]);

        }

    }

}

 

  运行结果如图6-10-1和6.10.2所示。

  1)控制台结果

 

  

图6-10-1 运行结果(1)

  2)项目浏览器

 

  

图6-10-2 运行结果(2)

6.11 获取HDFS集群上所有节点名称信息

  通过"DatanodeInfo.getHostName()"可获取HDFS集群上的所有节点名称。具体实现如下:

 

package com.hebut.file;

 

import org.apache.hadoop.conf.Configuration;

import org.apache.hadoop.fs.FileSystem;

import org.apache.hadoop.hdfs.DistributedFileSystem;

import org.apache.hadoop.hdfs.protocol.DatanodeInfo;

 

public class GetList {

 

    public static void main(String[] args) throws Exception {

        Configuration conf=new Configuration();

        FileSystem fs=FileSystem.get(conf);

       

        DistributedFileSystem hdfs = (DistributedFileSystem)fs;

        DatanodeInfo[] dataNodeStats = hdfs.getDataNodeStats();

       

        for(int i=0;i<dataNodeStats.length;i++){

            System.out.println("DataNode_"+i+"_Name:"+dataNodeStats[i].getHostName());

        }

    }

}

 

  运行结果如图6-11-1所示。

 

  

图6-11-1 控制台结果

7、HDFS的读写数据流

7.1 文件的读取剖析

  

 

  文件读取的过程如下:

  1)解释一

  • 客户端(client)用FileSystem的open()函数打开文件。
  • DistributedFileSystem用RPC调用元数据节点,得到文件的数据块信息。
  • 对于每一个数据块,元数据节点返回保存数据块的数据节点的地址。
  • DistributedFileSystem返回FSDataInputStream给客户端,用来读取数据。
  • 客户端调用stream的read()函数开始读取数据。
  • DFSInputStream连接保存此文件第一个数据块的最近的数据节点。
  • Data从数据节点读到客户端(client)。
  • 当此数据块读取完毕时,DFSInputStream关闭和此数据节点的连接,然后连接此文件下一个数据块的最近的数据节点。
  • 当客户端读取完毕数据的时候,调用FSDataInputStream的close函数。
  • 在读取数据的过程中,如果客户端在与数据节点通信出现错误,则尝试连接包含此数据块的下一个数据节点。
  • 失败的数据节点将被记录,以后不再连接。

  2)解释二

  • 使用HDFS提供的客户端开发库,向远程的Namenode发起RPC请求;
  • Namenode会视情况返回文件的部分或者全部block列表,对于每个block,Namenode都会返回有该block拷贝的datanode地址;
  • 客户端开发库会选取离客户端最接近的datanode来读取block;
  • 读取完当前block的数据后,关闭与当前的datanode连接,并为读取下一个block寻找最佳的datanode;
  • 当读完列表的block后,且文件读取还没有结束,客户端开发库会继续向Namenode获取下一批的block列表。
  • 读取完一个block都会进行checksum验证,如果读取datanode时出现错误,客户端会通知Namenode,然后再从下一个拥有该block拷贝的datanode继续读。

7.2 文件的写入剖析

  

 

  写入文件的过程比读取较为复杂:

  1)解释一

  • 客户端调用create()来创建文件
  • DistributedFileSystem用RPC调用元数据节点,在文件系统的命名空间中创建一个新的文件。
  • 元数据节点首先确定文件原来不存在,并且客户端有创建文件的权限,然后创建新文件。
  • DistributedFileSystem返回DFSOutputStream,客户端用于写数据。
  • 客户端开始写入数据,DFSOutputStream将数据分成块,写入data queue。
  • Data queue由Data Streamer读取,并通知元数据节点分配数据节点,用来存储数据块(每块默认复制3块)。分配的数据节点放在一个pipeline里。
  • Data Streamer将数据块写入pipeline中的第一个数据节点。第一个数据节点将数据块发送给第二个数据节点。第二个数据节点将数据发送给第三个数据节点。
  • DFSOutputStream为发出去的数据块保存了ack queue,等待pipeline中的数据节点告知数据已经写入成功。
  • 如果数据节点在写入的过程中失败:
    • 关闭pipeline,将ack queue中的数据块放入data queue的开始。
    • 当前的数据块在已经写入的数据节点中被元数据节点赋予新的标示,则错误节点重启后能够察觉其数据块是过时的,会被删除。
    • 失败的数据节点从pipeline中移除,另外的数据块则写入pipeline中的另外两个数据节点。
    • 元数据节点则被通知此数据块是复制块数不足,将来会再创建第三份备份。
  • 当客户端结束写入数据,则调用stream的close函数。此操作将所有的数据块写入pipeline中的数据节点,并等待ack queue返回成功。最后通知元数据节点写入完毕。

  2)解释二

  • 使用HDFS提供的客户端开发库,向远程的Namenode发起RPC请求;
  • Namenode会检查要创建的文件是否已经存在,创建者是否有权限进行操作,成功则会为文件创建一个记录,否则会让客户端抛出异常;
  • 当客户端开始写入文件的时候,开发库会将文件切分成多个packets,并在内部以"data queue"的形式管理这些packets,并向Namenode申请新的blocks,获取用来存储replicas的合适的datanodes列表,列表的大小根据在Namenode中对replication的设置而定。
  • 开始以pipeline(管道)的形式将packet写入所有的replicas中。开发库把packet以流的方式写入第一个datanode,该datanode把该packet存储之后,再将其传递给在此pipeline中的下一个datanode,直到最后一个datanode,这种写数据的方式呈流水线的形式。
  • 最后一个datanode成功存储之后会返回一个ack packet,在pipeline里传递至客户端,在客户端的开发库内部维护着"ack queue",成功收到datanode返回的ack packet后会从"ack queue"移除相应的packet。
  • 如果传输过程中,有某个datanode出现了故障,那么当前的pipeline会被关闭,出现故障的datanode会从当前的pipeline中移除,剩余的block会继续剩下的datanode中继续以pipeline的形式传输,同时Namenode会分配一个新的datanode,保持replicas设定的数量。

 

  文章下载地址:http://files.cnblogs.com/xia520pi/HadoopCluster_Vol.8.rar


Hadoop集群(第9期)_MapReduce初级案例

1、数据去重

   "数据去重"主要是为了掌握和利用并行化思想来对数据进行有意义筛选统计大数据集上的数据种类个数从网站日志中计算访问地等这些看似庞杂的任务都会涉及数据去重。下面就进入这个实例的MapReduce程序设计。

1.1 实例描述

  对数据文件中的数据进行去重。数据文件中的每行都是一个数据。

  样例输入如下所示:

     1)file1:

 

2012-3-1 a

2012-3-2 b

2012-3-3 c

2012-3-4 d

2012-3-5 a

2012-3-6 b

2012-3-7 c

2012-3-3 c

 

     2)file2:

 

2012-3-1 b

2012-3-2 a

2012-3-3 b

2012-3-4 d

2012-3-5 a

2012-3-6 c

2012-3-7 d

2012-3-3 c

 

     样例输出如下所示:

 

2012-3-1 a

2012-3-1 b

2012-3-2 a

2012-3-2 b

2012-3-3 b

2012-3-3 c

2012-3-4 d

2012-3-5 a

2012-3-6 b

2012-3-6 c

2012-3-7 c

2012-3-7 d

 

1.2 设计思路

  数据去重最终目标是让原始数据出现次数超过一次数据输出文件出现一次我们自然而然会想到将同一个数据的所有记录都交给一台reduce机器,无论这个数据出现多少次,只要在最终结果中输出一次就可以了。具体就是reduce的输入应该以数据作为key,而对value-list则没有要求。当reduce接收到一个<key,value-list>时就直接将key复制到输出的key中,并将value设置成空值

  在MapReduce流程中,map的输出<key,value>经过shuffle过程聚集成<key,value-list>后会交给reduce。所以从设计好的reduce输入可以反推出map的输出key应为数据,value任意。继续反推,map输出数据的key为数据,而在这个实例中每个数据代表输入文件中的一行内容,所以map阶段要完成的任务就是在采用Hadoop默认的作业输入方式之后,将value设置为key,并直接输出(输出中的value任意)。map中的结果经过shuffle过程之后交给reduce。reduce阶段不会管每个key有多少个value,它直接将输入的key复制为输出的key,并输出就可以了(输出中的value被设置成空了)。

1.3 程序代码

     程序代码如下所示:

 

package com.hebut.mr;

 

import java.io.IOException;

 

import org.apache.hadoop.conf.Configuration;

import org.apache.hadoop.fs.Path;

import org.apache.hadoop.io.IntWritable;

import org.apache.hadoop.io.Text;

import org.apache.hadoop.mapreduce.Job;

import org.apache.hadoop.mapreduce.Mapper;

import org.apache.hadoop.mapreduce.Reducer;

import org.apache.hadoop.mapreduce.lib.input.FileInputFormat;

import org.apache.hadoop.mapreduce.lib.output.FileOutputFormat;

import org.apache.hadoop.util.GenericOptionsParser;

 

public class Dedup {

 

    //map将输入中的value复制到输出数据的key上,并直接输出

    public static class Map extends Mapper<Object,Text,Text,Text>{

        private static Text line=new Text();//每行数据

       

        //实现map函数

        public void map(Object key,Text value,Context context)

                throws IOException,InterruptedException{

            line=value;

            context.write(line, new Text(""));

        }

       

    }

   

    //reduce将输入中的key复制到输出数据的key上,并直接输出

    public static class Reduce extends Reducer<Text,Text,Text,Text>{

        //实现reduce函数

        public void reduce(Text key,Iterable<Text> values,Context context)

                throws IOException,InterruptedException{

            context.write(key, new Text(""));

        }

       

    }

   

    public static void main(String[] args) throws Exception{

        Configuration conf = new Configuration();

        //这句话很关键

        conf.set("mapred.job.tracker", "192.168.1.2:9001");

       

        String[] ioArgs=new String[]{"dedup_in","dedup_out"};

     String[] otherArgs = new GenericOptionsParser(conf, ioArgs).getRemainingArgs();

     if (otherArgs.length != 2) {

     System.err.println("Usage: Data Deduplication <in> <out>");

     System.exit(2);

     }

     

     Job job = new Job(conf, "Data Deduplication");

     job.setJarByClass(Dedup.class);

     

     //设置MapCombineReduce处理类

     job.setMapperClass(Map.class);

     job.setCombinerClass(Reduce.class);

     job.setReducerClass(Reduce.class);

     

     //设置输出类型

     job.setOutputKeyClass(Text.class);

     job.setOutputValueClass(Text.class);

     

     //设置输入和输出目录

     FileInputFormat.addInputPath(job, new Path(otherArgs[0]));

     FileOutputFormat.setOutputPath(job, new Path(otherArgs[1]));

     System.exit(job.waitForCompletion(true) ? 0 : 1);

     }

}

 

1.4 代码结果

     1)准备测试数据

     通过Eclipse下面的"DFS Locations"在"/user/hadoop"目录下创建输入文件"dedup_in"文件夹(备注:"dedup_out"不需要创建。)如图1.4-1所示,已经成功创建。

        

图1.4-1 创建"dedup_in"                                   图1.4.2 上传"file*.txt"

 

     然后在本地建立两个txt文件,通过Eclipse上传到"/user/hadoop/dedup_in"文件夹中,两个txt文件的内容如"实例描述"那两个文件一样。如图1.4-2所示,成功上传之后。

     从SecureCRT远处查看"Master.Hadoop"的也能证实我们上传的两个文件。

 

 

    查看两个文件的内容如图1.4-3所示:

 

图1.4-3 文件"file*.txt"内容

2)查看运行结果

     这时我们右击Eclipse的"DFS Locations"中"/user/hadoop"文件夹进行刷新,这时会发现多出一个"dedup_out"文件夹,且里面有3个文件,然后打开双其"part-r-00000"文件,会在Eclipse中间把内容显示出来。如图1.4-4所示。

 

图1.4-4 运行结果

 

    此时,你可以对比一下和我们之前预期的结果是否一致。

2、数据排序

  "数据排序"是许多实际任务执行时要完成的第一项工作,比如学生成绩评比数据建立索引等。这个实例和数据去重类似,都是原始数据进行初步处理,为进一步的数据操作打好基础。下面进入这个示例。

2.1 实例描述

    对输入文件中数据进行排序。输入文件中的每行内容均为一个数字即一个数据。要求在输出中每行有两个间隔的数字,其中,第一个代表原始数据在原始数据集中的位次第二个代表原始数据

    样例输入

    1)file1:

 

2

32

654

32

15

756

65223

 

    2)file2:

 

5956

22

650

92

 

    3)file3:

 

26

54

6

 

    样例输出

 

1    2

2    6

3    15

4    22

5    26

6    32

7    32

8    54

9    92

10    650

11    654

12    756

13    5956

14    65223

 

2.2 设计思路

  这个实例仅仅要求对输入数据进行排序,熟悉MapReduce过程的读者会很快想到在MapReduce过程中就有排序,是否可以利用这个默认的排序,而不需要自己再实现具体的排序呢?答案是肯定的。

  但是在使用之前首先需要了解它的默认排序规则。它是按照key值进行排序的,如果key为封装int的IntWritable类型,那么MapReduce按照数字大小对key排序,如果key为封装为String的Text类型,那么MapReduce按照字典顺序对字符串排序。

  了解了这个细节,我们就知道应该使用封装int的IntWritable型数据结构了。也就是在map中将读入的数据转化成IntWritable型,然后作为key值输出(value任意)。reduce拿到<key,value-list>之后,将输入的key作为value输出,并根据value-list元素个数决定输出的次数。输出的key(即代码中的linenum)是一个全局变量,它统计当前key的位次。需要注意的是这个程序中没有配置Combiner,也就是在MapReduce过程中不使用Combiner。这主要是因为使用map和reduce就已经能够完成任务了。

2.3 程序代码

    程序代码如下所示:

 

package com.hebut.mr;

 

import java.io.IOException;

 

import org.apache.hadoop.conf.Configuration;

import org.apache.hadoop.fs.Path;

import org.apache.hadoop.io.IntWritable;

import org.apache.hadoop.io.Text;

import org.apache.hadoop.mapreduce.Job;

import org.apache.hadoop.mapreduce.Mapper;

import org.apache.hadoop.mapreduce.Reducer;

import org.apache.hadoop.mapreduce.lib.input.FileInputFormat;

import org.apache.hadoop.mapreduce.lib.output.FileOutputFormat;

import org.apache.hadoop.util.GenericOptionsParser;

 

public class Sort {

 

    //map将输入中的value化成IntWritable类型,作为输出的key

    public static class Map extends

        Mapper<Object,Text,IntWritable,IntWritable>{

        private static IntWritable data=new IntWritable();

       

        //实现map函数

        public void map(Object key,Text value,Context context)

                throws IOException,InterruptedException{

            String line=value.toString();

            data.set(Integer.parseInt(line));

            context.write(data, new IntWritable(1));

        }

       

    }

   

    //reduce将输入中的key复制到输出数据的key上,

    //然后根据输入的value-list中元素的个数决定key的输出次数

    //用全局linenum来代表key的位次

    public static class Reduce extends

            Reducer<IntWritable,IntWritable,IntWritable,IntWritable>{

       

        private static IntWritable linenum = new IntWritable(1);

       

        //实现reduce函数

        public void reduce(IntWritable key,Iterable<IntWritable> values,Context context)

                throws IOException,InterruptedException{

            for(IntWritable val:values){

                context.write(linenum, key);

                linenum = new IntWritable(linenum.get()+1);

            }

           

        }

 

    }

   

    public static void main(String[] args) throws Exception{

        Configuration conf = new Configuration();

        //这句话很关键

        conf.set("mapred.job.tracker", "192.168.1.2:9001");

       

        String[] ioArgs=new String[]{"sort_in","sort_out"};

     String[] otherArgs = new GenericOptionsParser(conf, ioArgs).getRemainingArgs();

     if (otherArgs.length != 2) {

     System.err.println("Usage: Data Sort <in> <out>");

         System.exit(2);

     }

     

     Job job = new Job(conf, "Data Sort");

     job.setJarByClass(Sort.class);

     

     //设置MapReduce处理类

     job.setMapperClass(Map.class);

     job.setReducerClass(Reduce.class);

     

     //设置输出类型

     job.setOutputKeyClass(IntWritable.class);

     job.setOutputValueClass(IntWritable.class);

     

     //设置输入和输出目录

     FileInputFormat.addInputPath(job, new Path(otherArgs[0]));

     FileOutputFormat.setOutputPath(job, new Path(otherArgs[1]));

     System.exit(job.waitForCompletion(true) ? 0 : 1);

     }

}

 

2.4 代码结果

1)准备测试数据

    通过Eclipse下面的"DFS Locations"在"/user/hadoop"目录下创建输入文件"sort_in"文件夹(备注:"sort_out"不需要创建。)如图2.4-1所示,已经成功创建。

              

图2.4-1 创建"sort_in"                                                  图2.4.2 上传"file*.txt"

 

    然后在本地建立三个txt文件,通过Eclipse上传到"/user/hadoop/sort_in"文件夹中,三个txt文件的内容如"实例描述"那三个文件一样。如图2.4-2所示,成功上传之后。

    从SecureCRT远处查看"Master.Hadoop"的也能证实我们上传的三个文件。

 

 

查看两个文件的内容如图2.4-3所示:

 

图2.4-3 文件"file*.txt"内容

2)查看运行结果

    这时我们右击Eclipse的"DFS Locations"中"/user/hadoop"文件夹进行刷新,这时会发现多出一个"sort_out"文件夹,且里面有3个文件,然后打开双其"part-r-00000"文件,会在Eclipse中间把内容显示出来。如图2.4-4所示。

 

图2.4-4 运行结果

3、平均成绩

    "平均成绩"主要目的还是在重温经典"WordCount"例子,可以说是在基础上的微变化版,该实例主要就是实现一个计算学生平均成绩的例子。

3.1 实例描述

  对输入文件中数据进行就算学生平均成绩。输入文件中的每行内容均为一个学生姓名和他相应的成绩,如果有多门学科,则每门学科为一个文件。要求在输出中每行有两个间隔的数据,其中,第一个代表学生的姓名第二个代表其平均成绩

    样本输入

    1)math:

 

张三    88

李四    99

王五    66

赵六    77

 

    2)china:

 

张三    78

李四    89

王五    96

赵六    67

 

    3)english:

 

张三    80

李四    82

王五    84

赵六    86

 

    样本输出

 

张三    82

李四    90

王五    82

赵六    76

 

3.2 设计思路

    计算学生平均成绩是一个仿"WordCount"例子,用来重温一下开发MapReduce程序的流程。程序包括两部分的内容:Map部分和Reduce部分,分别实现了map和reduce的功能。

    Map处理的是一个纯文本文件,文件中存放的数据时每一行表示一个学生的姓名和他相应一科成绩。Mapper处理的数据是由InputFormat分解过的数据集,其中InputFormat的作用是将数据集切割成小数据集InputSplit,每一个InputSlit将由一个Mapper负责处理。此外,InputFormat中还提供了一个RecordReader的实现,并将一个InputSplit解析成<key,value>对提供给了map函数。InputFormat的默认值是TextInputFormat,它针对文本文件,按行将文本切割成InputSlit,并用LineRecordReader将InputSplit解析成<key,value>对,key是行在文本中的位置,value是文件中的一行。

    Map的结果会通过partion分发到Reducer,Reducer做完Reduce操作后,将通过以格式OutputFormat输出。

    Mapper最终处理的结果对<key,value>,会送到Reducer中进行合并,合并的时候,有相同key的键/值对则送到同一个Reducer上。Reducer是所有用户定制Reducer类地基础,它的输入是key和这个key对应的所有value的一个迭代器,同时还有Reducer的上下文。Reduce的结果由Reducer.Context的write方法输出到文件中。

3.3 程序代码

    程序代码如下所示:

 

package com.hebut.mr;

 

import java.io.IOException;

import java.util.Iterator;

import java.util.StringTokenizer;

 

import org.apache.hadoop.conf.Configuration;

import org.apache.hadoop.fs.Path;

import org.apache.hadoop.io.IntWritable;

import org.apache.hadoop.io.LongWritable;

import org.apache.hadoop.io.Text;

import org.apache.hadoop.mapreduce.Job;

import org.apache.hadoop.mapreduce.Mapper;

import org.apache.hadoop.mapreduce.Reducer;

import org.apache.hadoop.mapreduce.lib.input.FileInputFormat;

import org.apache.hadoop.mapreduce.lib.input.TextInputFormat;

import org.apache.hadoop.mapreduce.lib.output.FileOutputFormat;

import org.apache.hadoop.mapreduce.lib.output.TextOutputFormat;

import org.apache.hadoop.util.GenericOptionsParser;

 

public class Score {

 

    public static class Map extends

            Mapper<LongWritable, Text, Text, IntWritable> {

 

        // 实现map函数

        public void map(LongWritable key, Text value, Context context)

                throws IOException, InterruptedException {

            // 将输入的纯文本文件的数据转化成String

            String line = value.toString();

 

            // 将输入的数据首先按行进行分割

            StringTokenizer tokenizerArticle = new StringTokenizer(line, "\n");

 

            // 分别对每一行进行处理

            while (tokenizerArticle.hasMoreElements()) {

                // 每行按空格划分

                StringTokenizer tokenizerLine = new StringTokenizer(tokenizerArticle.nextToken());

 

                String strName = tokenizerLine.nextToken();// 学生姓名部分

                String strScore = tokenizerLine.nextToken();// 成绩部分

 

                Text name = new Text(strName);

                int scoreInt = Integer.parseInt(strScore);

                // 输出姓名和成绩

                context.write(name, new IntWritable(scoreInt));

            }

        }

 

    }

 

    public static class Reduce extends

            Reducer<Text, IntWritable, Text, IntWritable> {

        // 实现reduce函数

        public void reduce(Text key, Iterable<IntWritable> values,

                Context context) throws IOException, InterruptedException {

 

            int sum = 0;

            int count = 0;

 

            Iterator<IntWritable> iterator = values.iterator();

            while (iterator.hasNext()) {

                sum += iterator.next().get();// 计算总分

                count++;// 统计总的科目数

            }

 

            int average = (int) sum / count;// 计算平均成绩

            context.write(key, new IntWritable(average));

        }

 

    }

 

    public static void main(String[] args) throws Exception {

        Configuration conf = new Configuration();

        // 这句话很关键

        conf.set("mapred.job.tracker", "192.168.1.2:9001");

 

        String[] ioArgs = new String[] { "score_in", "score_out" };

        String[] otherArgs = new GenericOptionsParser(conf, ioArgs).getRemainingArgs();

        if (otherArgs.length != 2) {

            System.err.println("Usage: Score Average <in> <out>");

            System.exit(2);

        }

 

        Job job = new Job(conf, "Score Average");

        job.setJarByClass(Score.class);

 

        // 设置MapCombineReduce处理类

        job.setMapperClass(Map.class);

        job.setCombinerClass(Reduce.class);

        job.setReducerClass(Reduce.class);

 

        // 设置输出类型

        job.setOutputKeyClass(Text.class);

        job.setOutputValueClass(IntWritable.class);

 

        // 将输入的数据集分割成小数据块splites,提供一个RecordReder的实现

        job.setInputFormatClass(TextInputFormat.class);

        // 提供一个RecordWriter的实现,负责数据输出

        job.setOutputFormatClass(TextOutputFormat.class);

 

        // 设置输入和输出目录

        FileInputFormat.addInputPath(job, new Path(otherArgs[0]));

        FileOutputFormat.setOutputPath(job, new Path(otherArgs[1]));

        System.exit(job.waitForCompletion(true) ? 0 : 1);

    }

}

 

3.4 代码结果

1)准备测试数据

    通过Eclipse下面的"DFS Locations"在"/user/hadoop"目录下创建输入文件"score_in"文件夹(备注:"score_out"不需要创建。)如图3.4-1所示,已经成功创建。

 

            

图3.4-1 创建"score_in"                                                       图3.4.2 上传三门分数

 

    然后在本地建立三个txt文件,通过Eclipse上传到"/user/hadoop/score_in"文件夹中,三个txt文件的内容如"实例描述"那三个文件一样。如图3.4-2所示,成功上传之后。

    备注:文本文件的编码为"UTF-8",默认为"ANSI",可以另存为时选择,不然中文会出现乱码

    从SecureCRT远处查看"Master.Hadoop"的也能证实我们上传的三个文件。

 

 

查看三个文件的内容如图3.4-3所示:

 

图3.4.3 三门成绩的内容

2)查看运行结果

    这时我们右击Eclipse的"DFS Locations"中"/user/hadoop"文件夹进行刷新,这时会发现多出一个"score_out"文件夹,且里面有3个文件,然后打开双其"part-r-00000"文件,会在Eclipse中间把内容显示出来。如图3.4-4所示。

 

图3.4-4 运行结果

4、单表关联

    前面的实例都是在数据上进行一些简单的处理,为进一步的操作打基础。"单表关联"这个实例要求给出的数据寻找关心的数据,它是对原始数据所包含信息的挖掘。下面进入这个实例。

4.1 实例描述

    实例中给出child-parent(孩子——父母)表,要求输出grandchild-grandparent(孙子——爷奶)表。

    样例输入如下所示。

    file:

 

child        parent

Tom        Lucy

Tom        Jack

Jone        Lucy

Jone        Jack

Lucy        Mary

Lucy        Ben

Jack        Alice

Jack        Jesse

Terry        Alice

Terry        Jesse

Philip        Terry

Philip        Alma

Mark        Terry

Mark        Alma

 

    家族树状关系谱:

 

 image

图4.2-1 家族谱

    样例输出如下所示。

    file:

 

grandchild        grandparent

Tom              Alice

Tom              Jesse

Jone              Alice

Jone              Jesse

Tom              Mary

Tom              Ben

Jone              Mary

Jone              Ben

Philip              Alice

Philip              Jesse

Mark              Alice

Mark              Jesse

 

4.2 设计思路

       分析这个实例,显然需要进行单表连接,连接的是左表parent列和右表child列,且左表右表同一个表

  连接结果除去连接的两列就是所需要的结果——"grandchild--grandparent"表。要用MapReduce解决这个实例,首先应该考虑如何实现自连接其次就是连接列设置最后结果整理

      考虑到MapReduce的shuffle过程会将相同的key会连接在一起,所以可以将map结果的key设置成待连接,然后列中相同的值就自然会连接在一起了。再与最开始的分析联系起来:

  要连接的是左表的parent列和右表的child列,且左表和右表是同一个表,所以在map阶段读入数据分割childparent之后,会将parent设置成keychild设置成value进行输出,并作为左表再将同一对childparent中的child设置成keyparent设置成value进行输出,作为右表。为了区分输出中的左右表,需要在输出的value加上左右表信息,比如在value的String最开始处加上字符1表示左表,加上字符2表示右表。这样在map的结果中就形成了左表和右表,然后在shuffle过程中完成连接。reduce接收到连接的结果,其中每个key的value-list就包含了"grandchild--grandparent"关系。取出每个key的value-list进行解析,将左表中的child放入一个数组右表中的parent放入一个数组,然后对两个数组求笛卡尔积就是最后的结果了。

4.3 程序代码

    程序代码如下所示。

 

package com.hebut.mr;

 

import java.io.IOException;

import java.util.*;

 

import org.apache.hadoop.conf.Configuration;

import org.apache.hadoop.fs.Path;

import org.apache.hadoop.io.IntWritable;

import org.apache.hadoop.io.Text;

import org.apache.hadoop.mapreduce.Job;

import org.apache.hadoop.mapreduce.Mapper;

import org.apache.hadoop.mapreduce.Reducer;

import org.apache.hadoop.mapreduce.lib.input.FileInputFormat;

import org.apache.hadoop.mapreduce.lib.output.FileOutputFormat;

import org.apache.hadoop.util.GenericOptionsParser;

 

public class STjoin {

 

    public static int time = 0;

 

    /*

     * map将输出分割childparent,然后正序输出一次作为右表,

     * 反序输出一次作为左表,需要注意的是在输出的value中必须

     * 加上左右表的区别标识。

     */

    public static class Map extends Mapper<Object, Text, Text, Text> {

 

        // 实现map函数

        public void map(Object key, Text value, Context context)

                throws IOException, InterruptedException {

            String childname = new String();// 孩子名称

            String parentname = new String();// 父母名称

            String relationtype = new String();// 左右表标识

 

            // 输入的一行预处理文本

            StringTokenizer itr=new StringTokenizer(value.toString());

            String[] values=new String[2];

            int i=0;

            while(itr.hasMoreTokens()){

                values[i]=itr.nextToken();

                i++;

            }

           

            if (values[0].compareTo("child") != 0) {

                childname = values[0];

                parentname = values[1];

 

                // 输出左表

                relationtype = "1";

                context.write(new Text(values[1]), new Text(relationtype +

                        "+"+ childname + "+" + parentname));

 

                // 输出右表

                relationtype = "2";

                context.write(new Text(values[0]), new Text(relationtype +

                        "+"+ childname + "+" + parentname));

            }

        }

 

    }

 

    public static class Reduce extends Reducer<Text, Text, Text, Text> {

 

        // 实现reduce函数

        public void reduce(Text key, Iterable<Text> values, Context context)

                throws IOException, InterruptedException {

 

            // 输出表头

            if (0 == time) {

                context.write(new Text("grandchild"), new Text("grandparent"));

                time++;

            }

 

            int grandchildnum = 0;

            String[] grandchild = new String[10];

            int grandparentnum = 0;

            String[] grandparent = new String[10];

 

            Iterator ite = values.iterator();

            while (ite.hasNext()) {

                String record = ite.next().toString();

                int len = record.length();

                int i = 2;

                if (0 == len) {

                    continue;

                }

 

                // 取得左右表标识

                char relationtype = record.charAt(0);

                // 定义孩子和父母变量

                String childname = new String();

                String parentname = new String();

 

                // 获取value-listvaluechild

                while (record.charAt(i) != '+') {

                    childname += record.charAt(i);

                    i++;

                }

 

                i = i + 1;

 

                // 获取value-listvalueparent

                while (i < len) {

                    parentname += record.charAt(i);

                    i++;

                }

 

                // 左表,取出child放入grandchildren

                if ('1' == relationtype) {

                    grandchild[grandchildnum] = childname;

                    grandchildnum++;

                }

 

                // 右表,取出parent放入grandparent

                if ('2' == relationtype) {

                    grandparent[grandparentnum] = parentname;

                    grandparentnum++;

                }

            }

 

            // grandchildgrandparent数组求笛卡尔儿积

            if (0 != grandchildnum && 0 != grandparentnum) {

                for (int m = 0; m < grandchildnum; m++) {

                    for (int n = 0; n < grandparentnum; n++) {

                        // 输出结果

                        context.write(new Text(grandchild[m]), new Text(grandparent[n]));

                    }

                }

            }

        }

    }

 

    public static void main(String[] args) throws Exception {

        Configuration conf = new Configuration();

        // 这句话很关键

        conf.set("mapred.job.tracker", "192.168.1.2:9001");

 

        String[] ioArgs = new String[] { "STjoin_in", "STjoin_out" };

        String[] otherArgs = new GenericOptionsParser(conf, ioArgs).getRemainingArgs();

        if (otherArgs.length != 2) {

            System.err.println("Usage: Single Table Join <in> <out>");

            System.exit(2);

        }

 

        Job job = new Job(conf, "Single Table Join");

        job.setJarByClass(STjoin.class);

 

        // 设置MapReduce处理类

        job.setMapperClass(Map.class);

        job.setReducerClass(Reduce.class);

 

        // 设置输出类型

        job.setOutputKeyClass(Text.class);

        job.setOutputValueClass(Text.class);

 

        // 设置输入和输出目录

        FileInputFormat.addInputPath(job, new Path(otherArgs[0]));

        FileOutputFormat.setOutputPath(job, new Path(otherArgs[1]));

        System.exit(job.waitForCompletion(true) ? 0 : 1);

    }

}

 

4.4 代码结果

1)准备测试数据

    通过Eclipse下面的"DFS Locations"在"/user/hadoop"目录下创建输入文件"STjoin_in"文件夹(备注:"STjoin_out"不需要创建。)如图4.4-1所示,已经成功创建。

 

                  

图4.4-1 创建"STjoin_in"                                       图4.4.2 上传"child-parent"表

 

    然后在本地建立一个txt文件,通过Eclipse上传到"/user/hadoop/STjoin_in"文件夹中,一个txt文件的内容如"实例描述"那个文件一样。如图4.4-2所示,成功上传之后。

    从SecureCRT远处查看"Master.Hadoop"的也能证实我们上传的文件,显示其内容如图4.4-3所示:

 

图4.4-3 表"child-parent"内容

    2)运行详解

    (1)Map处理:

    map函数输出结果如下所示。

 

child        parent                àà                    忽略此行

Tom        Lucy                   àà                <Lucy,1+Tom+Lucy>

                                                    <Tom,2+Tom+Lucy >

Tom        Jack                    àà                <Jack,1+Tom+Jack>

                                                    <Tom,2+Tom+Jack>

Jone        Lucy                 àà                <Lucy,1+Jone+Lucy>

                                                    <Jone,2+Jone+Lucy>

Jone        Jack                    àà                <Jack,1+Jone+Jack>

                                                    <Jone,2+Jone+Jack>

Lucy        Mary                   àà                <Mary,1+Lucy+Mary>

                                                    <Lucy,2+Lucy+Mary>

Lucy        Ben                    àà                <Ben,1+Lucy+Ben>

                                                     <Lucy,2+Lucy+Ben>

Jack        Alice                    àà                <Alice,1+Jack+Alice>

                                                      <Jack,2+Jack+Alice>

Jack        Jesse                   àà                <Jesse,1+Jack+Jesse>

                                                      <Jack,2+Jack+Jesse>

Terry        Alice                   àà                <Alice,1+Terry+Alice>

                                                      <Terry,2+Terry+Alice>

Terry        Jesse                  àà                <Jesse,1+Terry+Jesse>

                                                      <Terry,2+Terry+Jesse>

Philip        Terry                  àà                <Terry,1+Philip+Terry>

                                                      <Philip,2+Philip+Terry>

Philip        Alma                   àà                <Alma,1+Philip+Alma>

                                                      <Philip,2+Philip+Alma>

Mark        Terry                   àà                <Terry,1+Mark+Terry>

                                                      <Mark,2+Mark+Terry>

Mark        Alma                 àà                <Alma,1+Mark+Alma>

                                                      <Mark,2+Mark+Alma>

 

    (2)Shuffle处理

    在shuffle过程中完成连接。

 

map函数输出

排序结果

shuffle连接

<Lucy1+Tom+Lucy>

<Tom2+Tom+Lucy>

<Jack1+Tom+Jack>

<Tom2+Tom+Jack>

<Lucy1+Jone+Lucy>

<Jone2+Jone+Lucy>

<Jack1+Jone+Jack>

<Jone2+Jone+Jack>

<Mary1+Lucy+Mary>

<Lucy2+Lucy+Mary>

<Ben1+Lucy+Ben>

<Lucy2+Lucy+Ben>

<Alice1+Jack+Alice>

<Jack2+Jack+Alice>

<Jesse1+Jack+Jesse>

<Jack2+Jack+Jesse>

<Alice1+Terry+Alice>

<Terry2+Terry+Alice>

<Jesse1+Terry+Jesse>

<Terry2+Terry+Jesse>

<Terry1+Philip+Terry>

<Philip2+Philip+Terry>

<Alma1+Philip+Alma>

<Philip2+Philip+Alma>

<Terry1+Mark+Terry>

<Mark2+Mark+Terry>

<Alma1+Mark+Alma>

<Mark2+Mark+Alma>

<Alice1+Jack+Alice>

<Alice1+Terry+Alice>

<Alma1+Philip+Alma>

<Alma1+Mark+Alma>

<Ben1+Lucy+Ben>

<Jack1+Tom+Jack>

<Jack1+Jone+Jack>

<Jack2+Jack+Alice>

<Jack2+Jack+Jesse>

<Jesse1+Jack+Jesse>

<Jesse1+Terry+Jesse>

<Jone2+Jone+Lucy>

<Jone2+Jone+Jack>

<Lucy1+Tom+Lucy>

<Lucy1+Jone+Lucy>

<Lucy2+Lucy+Mary>

<Lucy2+Lucy+Ben>

<Mary1+Lucy+Mary>

<Mark2+Mark+Terry>

<Mark2+Mark+Alma>

<Philip2+Philip+Terry>

<Philip2+Philip+Alma>

<Terry2+Terry+Alice>

<Terry2+Terry+Jesse>

<Terry1+Philip+Terry>

<Terry1+Mark+Terry>

<Tom2+Tom+Lucy>

<Tom2+Tom+Jack>

<Alice1+Jack+Alice

        1+Terry+Alice

        1+Philip+Alma

        1+Mark+Alma >

<Ben1+Lucy+Ben>

<Jack1+Tom+Jack

        1+Jone+Jack

        2+Jack+Alice

        2+Jack+Jesse >

<Jesse1+Jack+Jesse

        1+Terry+Jesse >

<Jone2+Jone+Lucy

        2+Jone+Jack>

<Lucy1+Tom+Lucy

        1+Jone+Lucy

        2+Lucy+Mary

        2+Lucy+Ben>

<Mary1+Lucy+Mary

        2+Mark+Terry

        2+Mark+Alma>

<Philip2+Philip+Terry

        2+Philip+Alma>

<Terry2+Terry+Alice

        2+Terry+Jesse

        1+Philip+Terry

        1+Mark+Terry>

<Tom2+Tom+Lucy

        2+Tom+Jack>

 

    (3)Reduce处理

    首先由语句"0 != grandchildnum && 0 != grandparentnum"得知,只要在"value-list"中没有左表或者右表,则不会做处理,可以根据这条规则去除无效shuffle连接

 

无效shuffle连接

有效shuffle连接

<Alice1+Jack+Alice

        1+Terry+Alice

        1+Philip+Alma

        1+Mark+Alma >

<Ben1+Lucy+Ben>

<Jesse1+Jack+Jesse

        1+Terry+Jesse >

<Jone2+Jone+Lucy

        2+Jone+Jack>

<Mary1+Lucy+Mary

        2+Mark+Terry

        2+Mark+Alma>

<Philip2+Philip+Terry

        2+Philip+Alma>

<Tom2+Tom+Lucy

        2+Tom+Jack>

<Jack1+Tom+Jack

        1+Jone+Jack

        2+Jack+Alice

        2+Jack+Jesse >

<Lucy1+Tom+Lucy

        1+Jone+Lucy

        2+Lucy+Mary

        2+Lucy+Ben>

<Terry2+Terry+Alice

        2+Terry+Jesse

        1+Philip+Terry

        1+Mark+Terry>

    然后根据下面语句进一步对有效的shuffle连接做处理。

 

// 左表,取出child放入grandchildren

if ('1' == relationtype) {

    grandchild[grandchildnum] = childname;

    grandchildnum++;

}

 

// 右表,取出parent放入grandparent

if ('2' == relationtype) {

    grandparent[grandparentnum] = parentname;

    grandparentnum++;

}

 

    针对一条数据进行分析:

 

<Jack,1+Tom+Jack,

        1+Jone+Jack,

        2+Jack+Alice,

        2+Jack+Jesse >

 

    分析结果左表用"字符1"表示,右表用"字符2"表示,上面的<key,value-list>中的"key"表示左表与右表连接键。而"value-list"表示以"key"连接左表与右表相关数据

    根据上面针对左表与右表不同的处理规则,取得两个数组的数据如下所示:

 

grandchild

TomJonegrandchild[grandchildnum] = childname;

grandparent

AliceJessegrandparent[grandparentnum] = parentname;

    

    然后根据下面语句进行处理。

 

for (int m = 0; m < grandchildnum; m++) {

    for (int n = 0; n < grandparentnum; n++) {

        context.write(new Text(grandchild[m]), new Text(grandparent[n]));

    }

}

 

image  

 

处理结果如下面所示:

 

Tom        Jesse

Tom        Alice

Jone        Jesse

Jone        Alice 

    其他的有效shuffle连接处理都是如此

3)查看运行结果

    这时我们右击Eclipse的"DFS Locations"中"/user/hadoop"文件夹进行刷新,这时会发现多出一个"STjoin_out"文件夹,且里面有3个文件,然后打开双其"part-r-00000"文件,会在Eclipse中间把内容显示出来。如图4.4-4所示。

 

图4.4-4 运行结果

5、多表关联

    多表关联和单表关联类似,它也是通过对原始数据进行一定的处理,从其中挖掘出关心的信息。下面进入这个实例。

5.1 实例描述

    输入是两个文件,一个代表工厂表,包含工厂名列和地址编号列;另一个代表地址表,包含地址名列和地址编号列。要求从输入数据中找出工厂名地址名对应关系,输出"工厂名——地址名"表。

    样例输入如下所示。

    1)factory:

 

factoryname                    addressed

Beijing Red Star                    1

Shenzhen Thunder                3

Guangzhou Honda                2

Beijing Rising                       1

Guangzhou Development Bank      2

Tencent                        3

Back of Beijing                     1

 

    2)address:

 

addressID    addressname

1            Beijing

2            Guangzhou

3            Shenzhen

4            Xian

 

    样例输出如下所示。

 

factoryname                        addressname

Back of Beijing                          Beijing

Beijing Red Star                        Beijing

Beijing Rising                          Beijing

Guangzhou Development Bank          Guangzhou

Guangzhou Honda                    Guangzhou

Shenzhen Thunder                    Shenzhen

Tencent                            Shenzhen

 

5.2 设计思路

    多表关联和单表关联相似,都类似于数据库中的自然连接。相比单表关联,多表关联的左右表和连接列更加清楚。所以可以采用和单表关联的相同处理方式,map识别出输入的行属于哪个表之后,对其进行分割,将连接的列值保存在key中,另一列和左右表标识保存在value中,然后输出。reduce拿到连接结果之后,解析value内容,根据标志将左右表内容分开存放,然后求笛卡尔积,最后直接输出。

    这个实例的具体分析参考单表关联实例。下面给出代码。

5.3 程序代码

    程序代码如下所示:

 

package com.hebut.mr;

 

import java.io.IOException;

import java.util.*;

 

import org.apache.hadoop.conf.Configuration;

import org.apache.hadoop.fs.Path;

import org.apache.hadoop.io.IntWritable;

import org.apache.hadoop.io.Text;

import org.apache.hadoop.mapreduce.Job;

import org.apache.hadoop.mapreduce.Mapper;

import org.apache.hadoop.mapreduce.Reducer;

import org.apache.hadoop.mapreduce.lib.input.FileInputFormat;

import org.apache.hadoop.mapreduce.lib.output.FileOutputFormat;

import org.apache.hadoop.util.GenericOptionsParser;

 

public class MTjoin {

 

    public static int time = 0;

 

    /*

     * map中先区分输入行属于左表还是右表,然后对两列值进行分割,

     * 保存连接列在key值,剩余列和左右表标志在value中,最后输出

     */

    public static class Map extends Mapper<Object, Text, Text, Text> {

 

        // 实现map函数

        public void map(Object key, Text value, Context context)

                throws IOException, InterruptedException {

            String line = value.toString();// 每行文件

            String relationtype = new String();// 左右表标识

 

            // 输入文件首行,不处理

            if (line.contains("factoryname") == true

                    || line.contains("addressed") == true) {

                return;

            }

 

            // 输入的一行预处理文本

            StringTokenizer itr = new StringTokenizer(line);

            String mapkey = new String();

            String mapvalue = new String();

            int i = 0;

            while (itr.hasMoreTokens()) {

                // 先读取一个单词

                String token = itr.nextToken();

                // 判断该地址ID就把存到"values[0]"

                if (token.charAt(0) >= '0' && token.charAt(0) <= '9') {

                    mapkey = token;

                    if (i > 0) {

                        relationtype = "1";

                    } else {

                        relationtype = "2";

                    }

                    continue;

                }

 

                // 存工厂名

                mapvalue += token + " ";

                i++;

            }

 

            // 输出左右表

            context.write(new Text(mapkey), new Text(relationtype + "+"+ mapvalue));

        }

    }

 

    /*

     * reduce解析map输出,将value中数据按照左右表分别保存,

  * 然后求出笛卡尔积,并输出。

     */

    public static class Reduce extends Reducer<Text, Text, Text, Text> {

 

        // 实现reduce函数

        public void reduce(Text key, Iterable<Text> values, Context context)

                throws IOException, InterruptedException {

 

            // 输出表头

            if (0 == time) {

                context.write(new Text("factoryname"), new Text("addressname"));

                time++;

            }

 

            int factorynum = 0;

            String[] factory = new String[10];

            int addressnum = 0;

            String[] address = new String[10];

 

            Iterator ite = values.iterator();

            while (ite.hasNext()) {

                String record = ite.next().toString();

                int len = record.length();

                int i = 2;

                if (0 == len) {

                    continue;

                }

 

                // 取得左右表标识

                char relationtype = record.charAt(0);

 

                // 左表

                if ('1' == relationtype) {

                    factory[factorynum] = record.substring(i);

                    factorynum++;

                }

 

                // 右表

                if ('2' == relationtype) {

                    address[addressnum] = record.substring(i);

                    addressnum++;

                }

            }

 

            // 求笛卡尔积

            if (0 != factorynum && 0 != addressnum) {

                for (int m = 0; m < factorynum; m++) {

                    for (int n = 0; n < addressnum; n++) {

                        // 输出结果

                        context.write(new Text(factory[m]),

                                new Text(address[n]));

                    }

                }

            }

 

        }

    }

 

    public static void main(String[] args) throws Exception {

        Configuration conf = new Configuration();

        // 这句话很关键

        conf.set("mapred.job.tracker", "192.168.1.2:9001");

 

        String[] ioArgs = new String[] { "MTjoin_in", "MTjoin_out" };

        String[] otherArgs = new GenericOptionsParser(conf, ioArgs).getRemainingArgs();

        if (otherArgs.length != 2) {

            System.err.println("Usage: Multiple Table Join <in> <out>");

            System.exit(2);

        }

 

        Job job = new Job(conf, "Multiple Table Join");

        job.setJarByClass(MTjoin.class);

 

        // 设置MapReduce处理类

        job.setMapperClass(Map.class);

        job.setReducerClass(Reduce.class);

 

        // 设置输出类型

        job.setOutputKeyClass(Text.class);

        job.setOutputValueClass(Text.class);

 

        // 设置输入和输出目录

        FileInputFormat.addInputPath(job, new Path(otherArgs[0]));

        FileOutputFormat.setOutputPath(job, new Path(otherArgs[1]));

        System.exit(job.waitForCompletion(true) ? 0 : 1);

    }

}

 

5.4 代码结果

1)准备测试数据

    通过Eclipse下面的"DFS Locations"在"/user/hadoop"目录下创建输入文件"MTjoin_in"文件夹(备注:"MTjoin_out"不需要创建。)如图5.4-1所示,已经成功创建。

 

                 

图5.4-1 创建"MTjoin_in"                                                             图5.4.2 上传两个数据表

 

    然后在本地建立两个txt文件,通过Eclipse上传到"/user/hadoop/MTjoin_in"文件夹中,两个txt文件的内容如"实例描述"那两个文件一样。如图5.4-2所示,成功上传之后。

    从SecureCRT远处查看"Master.Hadoop"的也能证实我们上传的两个文件。

 

图5.4.3 两个数据表的内容

2)查看运行结果

    这时我们右击Eclipse的"DFS Locations"中"/user/hadoop"文件夹进行刷新,这时会发现多出一个"MTjoin_out"文件夹,且里面有3个文件,然后打开双其"part-r-00000"文件,会在Eclipse中间把内容显示出来。如图5.4-4所示。

 

图5.4-4 运行结果

6、倒排索引

    "倒排索引"是文档检索系统最常用数据结构,被广泛地应用于全文搜索引擎。它主要是用来存储某个单词(或词组)一个文档或一组文档中的存储位置映射,即提供了一种根据内容来查找文档方式。由于不是根据文档来确定文档所包含的内容,而是进行相反的操作,因而称为倒排索引(Inverted Index)。

6.1 实例描述

    通常情况下,倒排索引由一个单词(或词组)以及相关的文档列表组成,文档列表中的文档或者是标识文档的ID号,或者是指文档所在位置的URL,如图6.1-1所示。

 image

图6.1-1 倒排索引结构

    从图6.1-1可以看出,单词1出现在{文档1,文档4,文档13,……}中,单词2出现在{文档3,文档5,文档15,……}中,而单词3出现在{文档1,文档8,文档20,……}中。在实际应用中,还需要每个文档添加一个权值,用来指出每个文档与搜索内容的相关度,如图6.1-2所示。

 

 image

图6.1-2 添加权重的倒排索引

    最常用的是使用词频作为权重,即记录单词在文档中出现的次数。以英文为例,如图6.1-3所示,索引文件中的"MapReduce"一行表示:"MapReduce"这个单词在文本T0中出现过1次,T1中出现过1次,T2中出现过2次。当搜索条件为"MapReduce"、"is"、"Simple"时,对应的集合为:{T0,T1,T2}∩{T0,T1}∩{T0,T1}={T0,T1},即文档T0和T1包含了所要索引的单词,而且只有T0是连续的。

 

 image

图6.1-3 倒排索引示例

    更复杂的权重还可能要记录单词在多少个文档中出现过,以实现TF-IDF(Term Frequency-Inverse Document Frequency)算法,或者考虑单词在文档中的位置信息(单词是否出现在标题中,反映了单词在文档中的重要性)等。

    样例输入如下所示。

    1)file1:

 

MapReduce is simple

 

    2)file2:

 

MapReduce is powerful is simple

 

    3)file3:

 

Hello MapReduce bye MapReduce

 

    样例输出如下所示。

 

MapReduce      file1.txt:1;file2.txt:1;file3.txt:2;

is            file1.txt:1;file2.txt:2;

simple           file1.txt:1;file2.txt:1;

powerful      file2.txt:1;

Hello          file3.txt:1;

bye            file3.txt:1;

 

6.2 设计思路

    实现"倒排索引"只要关注的信息为:单词文档URL词频,如图3-11所示。但是在实现过程中,索引文件的格式与图6.1-3会略有所不同,以避免重写OutPutFormat类。下面根据MapReduce的处理过程给出倒排索引设计思路

    1)Map过程

    首先使用默认的TextInputFormat类对输入文件进行处理,得到文本中每行偏移量及其内容。显然,Map过程首先必须分析输入的<key,value>对,得到倒排索引中需要的三个信息:单词、文档URL和词频,如图6.2-1所示。

 image

图6.2-1 Map过程输入/输出

 

  这里存在两个问题第一,<key,value>对只能有两个值,在不使用Hadoop自定义数据类型的情况下,需要根据情况将其中两个值合并成一个值,作为key或value值;第二,通过一个Reduce过程无法同时完成词频统计生成文档列表,所以必须增加一个Combine过程完成词频统计

    这里讲单词和URL组成key值(如"MapReduce:file1.txt"),将词频作为value,这样做的好处是可以利用MapReduce框架自带的Map端排序,将同一文档相同单词词频组成列表,传递给Combine过程,实现类似于WordCount的功能。

    2)Combine过程

    经过map方法处理后,Combine过程将key值相同的value值累加,得到一个单词在文档在文档中的词频,如图6.2-2所示。如果直接将图6.2-2所示的输出作为Reduce过程的输入,在Shuffle过程时将面临一个问题:所有具有相同单词的记录(由单词、URL和词频组成)应该交由同一个Reducer处理,但当前的key值无法保证这一点,所以必须修改key值和value值。这次将单词作为key值,URL和词频组value值(如"file1.txt:1")。这样做的好处是可以利用MapReduce框架默认的HashPartitioner类完成Shuffle过程,将相同单词所有记录发送给同一个Reducer进行处理

 

 image

图6.2-2 Combine过程输入/输出

    3)Reduce过程

    经过上述两个过程后,Reduce过程只需将相同key值的value值组合成倒排索引文件所需的格式即可,剩下的事情就可以直接交给MapReduce框架进行处理了。如图6.2-3所示。索引文件的内容除分隔符外与图6.1-3解释相同。

    4)需要解决的问题

    本实例设计的倒排索引在文件数目没有限制,但是单词文件不宜过大(具体值与默认HDFS块大小及相关配置有关),要保证每个文件对应一个split。否则,由于Reduce过程没有进一步统计词频,最终结可能出现词频未统计完全单词。可以通过重写InputFormat类将每个文件为一个split,避免上述情况。或者执行两次MapReduce第一次MapReduce用于统计词频第二次MapReduce用于生成倒排索引。除此之外,还可以利用复合键值对等实现包含更多信息的倒排索引。

 

 image

图6.2-3 Reduce过程输入/输出

6.3 程序代码

  程序代码如下所示:

 

package com.hebut.mr;

 

import java.io.IOException;

import java.util.StringTokenizer;

 

import org.apache.hadoop.conf.Configuration;

import org.apache.hadoop.fs.Path;

import org.apache.hadoop.io.IntWritable;

import org.apache.hadoop.io.Text;

import org.apache.hadoop.mapreduce.Job;

import org.apache.hadoop.mapreduce.Mapper;

import org.apache.hadoop.mapreduce.Reducer;

import org.apache.hadoop.mapreduce.lib.input.FileInputFormat;

import org.apache.hadoop.mapreduce.lib.input.FileSplit;

import org.apache.hadoop.mapreduce.lib.output.FileOutputFormat;

import org.apache.hadoop.util.GenericOptionsParser;

 

public class InvertedIndex {

 

    public static class Map extends Mapper<Object, Text, Text, Text> {

 

        private Text keyInfo = new Text(); // 存储单词和URL组合

        private Text valueInfo = new Text(); // 存储词频

        private FileSplit split; // 存储Split对象

 

        // 实现map函数

        public void map(Object key, Text value, Context context)

                throws IOException, InterruptedException {

 

            // 获得<key,value>对所属的FileSplit对象

            split = (FileSplit) context.getInputSplit();

 

            StringTokenizer itr = new StringTokenizer(value.toString());

 

            while (itr.hasMoreTokens()) {

                // key值由单词和URL组成,如"MapReducefile1.txt"

                // 获取文件的完整路径

                // keyInfo.set(itr.nextToken()+":"+split.getPath().toString());

                // 这里为了好看,只获取文件的名称。

                int splitIndex = split.getPath().toString().indexOf("file");

                keyInfo.set(itr.nextToken() + ":"

                    + split.getPath().toString().substring(splitIndex));

                // 词频初始化为1

                valueInfo.set("1");

 

                context.write(keyInfo, valueInfo);

            }

        }

    }

 

    public static class Combine extends Reducer<Text, Text, Text, Text> {

 

        private Text info = new Text();

 

        // 实现reduce函数

        public void reduce(Text key, Iterable<Text> values, Context context)

                throws IOException, InterruptedException {

 

            // 统计词频

            int sum = 0;

            for (Text value : values) {

                sum += Integer.parseInt(value.toString());

            }

 

            int splitIndex = key.toString().indexOf(":");

            // 重新设置value值由URL和词频组成

            info.set(key.toString().substring(splitIndex + 1) + ":" + sum);

            // 重新设置key值为单词

            key.set(key.toString().substring(0, splitIndex));

 

            context.write(key, info);

        }

    }

 

    public static class Reduce extends Reducer<Text, Text, Text, Text> {

 

        private Text result = new Text();

 

        // 实现reduce函数

        public void reduce(Text key, Iterable<Text> values, Context context)

                throws IOException, InterruptedException {

 

            // 生成文档列表

            String fileList = new String();

            for (Text value : values) {

                fileList += value.toString() + ";";

            }

 

            result.set(fileList);

 

            context.write(key, result);

        }

    }

 

    public static void main(String[] args) throws Exception {

        Configuration conf = new Configuration();

        // 这句话很关键

        conf.set("mapred.job.tracker", "192.168.1.2:9001");

 

        String[] ioArgs = new String[] { "index_in", "index_out" };

        String[] otherArgs = new GenericOptionsParser(conf, ioArgs)

                .getRemainingArgs();

        if (otherArgs.length != 2) {

            System.err.println("Usage: Inverted Index <in> <out>");

            System.exit(2);

        }

 

        Job job = new Job(conf, "Inverted Index");

        job.setJarByClass(InvertedIndex.class);

 

        // 设置MapCombineReduce处理类

        job.setMapperClass(Map.class);

        job.setCombinerClass(Combine.class);

        job.setReducerClass(Reduce.class);

 

        // 设置Map输出类型

        job.setMapOutputKeyClass(Text.class);

        job.setMapOutputValueClass(Text.class);

 

        // 设置Reduce输出类型

        job.setOutputKeyClass(Text.class);

        job.setOutputValueClass(Text.class);

 

        // 设置输入和输出目录

        FileInputFormat.addInputPath(job, new Path(otherArgs[0]));

        FileOutputFormat.setOutputPath(job, new Path(otherArgs[1]));

        System.exit(job.waitForCompletion(true) ? 0 : 1);

    }

}

 

6.4 代码结果

1)准备测试数据

    通过Eclipse下面的"DFS Locations"在"/user/hadoop"目录下创建输入文件"index_in"文件夹(备注:"index_out"不需要创建。)如图6.4-1所示,已经成功创建。

 

               

图6.4-1 创建"index_in"                                             图6.4.2 上传"file*.txt"

 

    然后在本地建立三个txt文件,通过Eclipse上传到"/user/hadoop/index_in"文件夹中,三个txt文件的内容如"实例描述"那三个文件一样。如图6.4-2所示,成功上传之后。

    从SecureCRT远处查看"Master.Hadoop"的也能证实我们上传的三个文件。

 

图6.4.3 三个"file*.txt"的内容

2)查看运行结果

    这时我们右击Eclipse的"DFS Locations"中"/user/hadoop"文件夹进行刷新,这时会发现多出一个"index_out"文件夹,且里面有3个文件,然后打开双其"part-r-00000"文件,会在Eclipse中间把内容显示出来。如图6.4-4所示。

 

图6.4-4 运行结果

 

 

  文章下载地址:http://files.cnblogs.com/xia520pi/HadoopCluster_Vol.9.rar

Hadoop集群(第10期)_MySQL关系数据库

1、MySQL安装

    MySQL下载地址:http://www.mysql.com/downloads/

1.1 Windows平台

  1)准备软件

  MySQL版本:mysql-5.5.21-win32.msi

  2)安装环境:

  操作系统:Windows 7旗舰版

  3)开始安装

  第一步:双击"msi"安装文件,出现如图1.1-1界面——"MySQL安装向导",按"Next"继续。

 

图1.1-1 MySQL安装向导

  第二步:在"I accept …."前面勾上,同意协议,按"Next"按钮继续。

 

图1.1-2 软件协议

  第三步:选择安装类型,有"Typical(默认)"、"Custom(定制安装)"、"Complete(完全)"三个选项。

  • 典型安装:安装只安装MySQL服务器、mysql命令行客户端和命令行实用程序。命令行客户端和实用程序包括mysqldump、myisamchk和其它几个工具来帮助你管理MySQL服务器。
  • 定制安装:安装允许你完全控制你想要安装的软件包和安装路径。
  • 完全安装:安装将安装软件包内包含的所有组件。完全安装软件包包括的组件包括嵌入式服务器库、基准套件、支持脚本和文档。

  我们选择"Custom",有更多的选项,也方便熟悉安装过程。

 

图1.1-3 安装类型

  第四步:选择组件及更改文件夹位置。

 

图1.1-4 自定义界面

  所有可用组件列入定制安装对话框左侧的树状视图内。未安装的组件用红色X 图标表示;已经安装的组件有灰色图标。要想更改组件,点击该组件的图标并从下拉列表中选择新的选项。组件选择了默认安装位置我会更改一下,点击Browse。

 

图1.1-5 路径选择

  按"OK"按钮返回,并按"Next"按钮继续。

  备注:安装mysql的路径中,不能含有中文

  第五步:确认一下先前的设置,如果有误,按"Back"返回重做。按"Install"开始安装。

 

图1.1-6 准备安装

  第六步:正在安装中,请稍候……

 

图1.1-7 正在安装

  第七步:弹出一个页面来,是关于介绍MySQL企业版的信息,没有什么可操作的,按"Next"按钮继续。

 

图1.1-8 MySQL企业版介绍

  然后弹出一个类似界面,接着按"Next"按钮继续。

  第八步:那个带复选框的是"MySQL服务器实例配置向导",保持默认勾选,按"Finish"按钮。至此,安装过程已经结束了,但是还需要配置一下。

 

图1.1-9 安装结束

  第九步:MySQL配置向导启动界面,按"Next"按钮继续。

 

图1.1-10 配置向导

  MySQL Configuration Wizard(配置向导)可以帮助自动配置Windows中的服务器。MySQL Configuration Wizard(配置向导)问你一系列问题,然后将回答放到模板中生成一个my.ini文件,该文件与你的安装一致。目前只适用于Windows用户。

  一般情况当MySQL安装帮助退出时,从MySQL安装帮助启动MySQL Configuration Wizard(配置向导)。还可以点击Windows启动菜单中MySQL服务器实例配置向导条目中的MySQL部分来启动MySQL Configuration Wizard(配置向导)。并且,还可以进入MySQL安装bin目录直接启动MySQLInstanceConfig.exe文件。

  第十步:选择配置类型,可以选择两种配置类型:Detailed Configuration(详细配置)和Standard Configuration(标准配置)。Standard Configuration(标准配置)选项适合想要快速启动MySQL而不必考虑服务器配置的新用户。详细配置选项适合想要更加细粒度控制服务器配置的高级用户。我们选择"Detailed Configuration",方便熟悉配置过程

 

图1.1-11 配置类型

  备注

  如果你是MySQL的新手,需要配置为单用户开发机的服务器,Standard Configuration(标准配置)应当适合你的需求。选择Standard Configuration(标准配置)选项,则 MySQL Configuration Wizard(配置向导)自动设置所有配置选项,但不包括服务选项和安全选项。

  Standard Configuration(标准配置)设置选项可能与安装MySQL的系统不兼容。如果系统上已经安装了MySQL和你想要配置的安装,建议选择详细配置。

  第十一步:选择服务器类型,可以选择3种服务器类型,选择哪种服务器将影响到MySQL Configuration Wizard(配置向导)对内存、硬盘和过程或使用的决策。

 

  • Developer Machine(开发机器):该选项代表典型个人用桌面工作站。假定机器上运行着多个桌面应用程序。将MySQL服务器配置成使用最少的系统资源。

 

  • Server Machine(服务器):该选项代表服务器,MySQL服务器可以同其它应用程序一起运行,例如FTP、email和web服务器。MySQL服务器配置成使用适当比例的系统资源。

 

  • Dedicated MySQL Server Machine(专用MySQL服务器):该选项代表只运行MySQL服务的服务器。假定运行没有运行其它应用程序。MySQL服务器配置成使用所有可用系统资源。

 

大家根据自己的类型选择了,一般选"Server Machine",不会太少,也不会占满。我们选择"Server Machine",按"Next"按钮继续。

 

图1.1-12 服务器类型

  第十二步:选择数据库用途,通过Database Usage(数据库使用)对话框,你可以指出创建MySQL表时使用的表处理器。通过该选项,你可以选择是否使用InnoDB储存引擎,以及InnoDB占用多大比例的服务器资源。"Multifunctional Database(通用多功能型,)"、"Transactional Database Only(服务器类型,专注于事务处理,一般)"、"Non-Transactional Database Only(非事务处理型,较简单,主要做一些监控、记数用,对MyISAM 数据类型的支持仅限于non-transactional)",随自己的用途而选择了,一般选择第一种多功能的。我们选择"Multifunctional Database",按"Next"按钮继续。

 

图1.1-13 数据库用途

  第十三步:对InnoDB Tablespace 进行配置,就是为InnoDB 数据库文件选择一个存储空间,如果修改了,要记住位置,重装的时候要选择一样的地方,否则可能会造成数据库损坏,当然,对数据库做个备份就没问题了,这里不详述。这里没有修改,使用用默认位置,按"Next"按钮继续。

 

图1.1-14 配置InnoDB表空间

  第十四步:选择MySQL允许的最大连接数,限制所创建的与MySQL服务器之间的并行连接数量很重要,以便防止服务器耗尽资源。在Concurrent Connections(并行连接)对话框中,可以选择服务器的使用方法,并根据情况限制并行连接的数量。还可以手动设置并行连接的限制。第一种是最大20个连接并发数,第二种是最大500个并发连接数,最后一种是自定义。我们选择"Online Transaction PRocessing(OLTP)",按"Next"按钮继续。

 

图1.1-15 最大连接数

  第十五步:进行网络配置,在Networking Options(网络选项)对话框中可以启用或禁用TCP/IP网络,并配置用来连接MySQL服务器的端口号。默认情况启用TCP/IP网络。要想禁用TCP/IP网络,取消选择Enable TCP/IP Networking选项旁边的检查框。默认使用3306端口。要想更访问MySQL使用的端口,从下拉框选择一个新端口号或直接向下拉框输入新的端口号。如果你选择的端口号已经被占用,将提示确认选择的端口号。我们保持默认选项,按"Next"按钮继续。

 

图1.1-16 网络配置

  第十六步:对数据库语言编码进行设置,非常重要,因为Hadoop里默认编码为UTF-8,所以为了避免出现乱码,我们这里选择"UTF-8"作为MySQL数据库的语言编码。

 

图1.1-17 数据库编码

  第十七步:是否要把MySQL设置成Windows的服务,一般选择设成服务,这样以后就可以通过服务中启动和关闭mysql数据库了。推荐:下面的复选框也勾选上,这样,在cmd模式下,不必非到mysql的bin目录下执行命令。我们全部打上了勾Service Name 不变。按"Next"按钮继续。

 

图1.1-18 服务选项

  第十八步:设置MySQL的超级用户密码,这个超级用户非常重要,对MySQL拥有全部的权限,请设置好并牢记超级用户的密码,下面有个复选框是选择是否允许远程机器用root用户连接到你的MySQL服务器上面,如果有这个需求,也请勾选。我们这里的root用户密码设置为"hadoop",并勾上"允许远程连接"复选框,按"Next"按钮继续。

 

图1.1-19 安全选项

  备注

  • "Enable root access from remote machines(是否允许root 用户在其它的机器上登陆,如果要安全,就不要勾上,如果要方便,就勾上它)"。
  • "Create An Anonymous Account(新建一个匿名用户,匿名用户可以连接数据库,不能操作数据,包括查询)",一般就不用勾了。

  第十九步:确认设置无误,如果有误,按"Back"返回检查。如果没有,按"Execute"使设置生效。

 

图1.1-20 确认配置

  第二十步:设置完毕,按"Finish"按钮结束MySQL的安装与配置。

 

图1.1-21 配置完成

  备注:这里有一个比较常见的错误,就是不能"Start service",一般出现在以前有安装MySQL的服务器上,解决的办法,先保证以前安装的MySQL 服务器彻底卸载掉了;不行的话,检查是否按上面一步所说,之前的密码是否有修改,照上面的操作;如果依然不行,将MySQL 安装目录下的data 文件夹备份,然后删除,在安装完成后,将安装生成的 data 文件夹删除,备份的data 文件夹移回来,再重启MySQL 服务就可以了,这种情况下,可能需要将数据库检查一下,然后修复一次,防止数据出错。

  4)验证成功

  第一种:打开任务管理器 看到MySQL服务是否已经启动。

 

图1.1-22 任务管理器

  第二种:"开始à启动cmdà开打cmd模式",输入"mysql –u root –p"连接数据库。

 

图1.1-23 连接数据库

1.2 Linux平台

  1)准备软件

  MySQL数据库:MySQL-server-5.5.21-1.linux2.6.i386.rpm

  MySQL客户端:MySQL-client-5.5.21-1.linux2.6.i386.rpm

  2)安装环境:

  操作系统:CentOS6.0 Linux

  3)检查安装

  在安装MySQL之前,先检查CentOS系统中是否已经安装了一个MySQL,如果已经安装先卸载,不然会导致安装的MySQL失败

  用下面命令查看系统之前是否已安装MySQL。

 

rpm -qa | grep mysql

 

  查看结果如下:

 

  

 

  从上图得知,CentOS6.0系统自带了一个MySQL,我们需要删除这个老版本,用root用户执行下面语句。

 

rpm -e --nodeps mysql-libs-5.1.47-4.el6.i686

 

  

 

  上图中,我们先切换到"root"用户下,然后执行删除语句,删除之后,我们再次查看,发现已经成功删除了CentOS6.0自带的旧MySQL版本。

  在删除MySQL的rpm后,还要进行一些扫尾操作,网上有两种操作。(备注:我在这里两种都没有用到,发现系统中并没有其他残余的MySQL信息。)

  第一种善后处理:使用下面命令进行处理。

 

rm -rf /var/lib/mysql*

rm -rf /usr/share/mysql*

 

  另一种善后处理:卸载后/var/lib/mysql中的/etc/my.cnf会重命名为my.cnf.rpmsave,/var/log/mysqld.log 会重命名为/var/log/mysqld.log.rpmsave,如果确定没用后就手工删除。

  4)开始安装

  第一步:上传所需软件。通过"FlashFXP"软件使用"vsftpd"上传用到的两个软件到"/home/hadoop"目录下。

 

  第二步:安装MySQL服务端。用"root"用户运行如下命令进行安装:(备注:以下步骤都是用"root"用户执行。)

 

rpm -ivh MySQL-server-5.5.21-1.linux2.6.i386.rpm

 

  通过SecureCRT查看如下:

 

  

  

  如出现如上信息,服务端安装完毕。

  第三步:检测MySQL 3306端口是否安打开。测试是否成功可运行netstat看MySQL端口是否打开,如打开表示服务已经启动,安装成功。MySQL默认的端口是3306。

 

netstat -nat

 

  

  从上图中发现并没有与"3306"有关的信息,说明"MySQL服务器"没有启动。通过下面命令启动MySQL。

 

service mysql start

 

  

  从上图中已经发现我们的MySQL服务器已经起来了。

  第四步:安装MySQL客户端。用下面命令进行安装:

 

rpm -ivh MySQL-client-5.5.21-1.linux2.6.i386.rpm

 

  执行命令显示如下:

  

  从上图中显示MySQL客户端已经安装完毕。

  第五步:MySQL的几个重要目录。MySQL安装完成后不像SQL Server默认安装在一个目录,它的数据库文件配置文件命令文件分别不同目录,了解这些目录非常重要,尤其对于Linux的初学者,因为 Linux本身的目录结构就比较复杂,如果搞不清楚MySQL的安装目录那就无从谈起深入学习。

  下面就介绍一下这几个目录。

 

a、数据库目录

/var/lib/mysql/

 

b、配置文件

/usr/share/mysql(mysql.server命令及配置文件)

 

c、相关命令

/usr/bin(mysqladmin mysqldump等命令)

 

d、启动脚本

/etc/rc.d/init.d/(启动脚本文件mysql的目录)

如:/etc/rc.d/init.d/mysql start/restart/stop/status

 

  下面就分别展示上面的几个目录内容:

  • 数据库目录

 

  

 

  • 配置文件

 

  

 

  • 相关命令

  

 

  • 启动脚本

 

  

 

  第六步:更改MySQL目录。由于MySQL数据库目录占用磁盘比较大,而MySQL默认的数据文件存储目录为/"var/lib/mysql",所以我们要把目录移到"/"根目录下的"mysql_data"目录中。

  需要以下几个步骤:

  • "/"根目录下建立"mysql_data"目录

 

cd /

mkdir mysql_data

 

  

 

  • 把MySQL服务进程停掉

  可以用两种方法:

 

service mysql stop

 

  或者

 

mysqladmin -u root -p shutdown

 

  

  从上图中我们得知"MySQL服务进程"已经停掉。

  备注MySQL默认用户名为"root",此处的"root"与Linux的最高权限用户"root"不是一会儿,而且默认的用户"root"的密码为,所以上图中让输入密码,直接点击回车即可。

    

  • 把"/var/lib/mysql"整个目录移到"/mysql_data"

 

mv /var/lib/mysql /mysql_data

 

  

  这样就把MySQL的数据文件移动到了"/mysql_data/mysql"下。

 

  • 找到my.cnf配置文件

  如果"/etc/"目录下my.cnf配置文件,请到"/usr/share/mysql/"下找到*.cnf文件,拷贝其中一个合适的配置文件到"/etc/"并改名为"my.cnf"中。命令如下:

 

cp /usr/share/mysql/my-medium.cnf  /etc/my.cnf

 

  

  上图中,下查看"/etc/"下面是否有"my.cnf"文件,发现没有,然后通过上面的命令进行拷贝,拷贝完之后,进行查看,发现拷贝成功。

  备注:"/usr/share/mysql/"下有好几个结尾为cnf的文件,它们的作用分别是。

 

  

 

a、my-small.cnf:是为了小型数据库而设计的。不应该把这个模型用于含有一些常用项目的数据库。

b、my-medium.cnf:是为中等规模的数据库而设计的。如果你正在企业中使用RHEL,可能会比这个操作系统的最小RAM需求(256MB)明显多得多的物理内存。由此可见,如果有那么多RAM内存可以使用,自然可以在同一台机器上运行其它服务。

c、my-large.cnf:是为专用于一个SQL数据库的计算机而设计的。由于它可以为该数据库使用多达512MB的内存,所以在这种类型的系统上将需要至少1GB的RAM,以便它能够同时处理操作系统与数据库应用程序。

d、my-huge.cnf:是为企业中的数据库而设计的。这样的数据库要求专用服务器和1GB或1GB以上的RAM。

这些选择高度依赖于内存的数量、计算机的运算速度、数据库的细节大小、访问数据库的用户数量以及在数据库中装入并访问数据的用户数量。随着数据库和用户的不断增加,数据库的性能可能会发生变化。

 

  备注:这里我们根据实际情况,选择了"my-medium.cnf"进行配置。

 

  • 编辑MySQL的配置文件"/etc/my.cnf"

  为保证MySQL能够正常工作,需要指明"mysql.sock"文件的产生位置,以及默认编码修改为UTF-8。用下面命令:

 

vim /etc /my.cnf

 

  

  需要修改和添加的内容如下:

    【client

 

socket = /mysql_data/mysql/mysql.sock

default-character-set=utf8

 

    【mysqld

 

socket = /mysql_data/mysql/mysql.sock

datadir         =/mysql_data/mysql

character-set-server=utf8

lower_case_table_names=1(注意linux下mysql安装完后是默认:区分表名的大小写,不区分列名的大小写;lower_case_table_names = 0 0:区分大小写,1:不区分大小写)

 

  备注:【client】和【mysqld】设置的编码时前地名称不一样。

 

  

 

  • 修改MySQL启动脚本"/etc/rc.d/init.d/mysql"

  最后,需要修改MySQL启动脚本/etc/rc.d/init.d/mysql,修改datadir=/mysql_data/mysql

 

vim /etc/rc.d/init.d/mysql

 

  

 

  

 

  • 重新启动MySQL服务

 

service mysql start

 

  

  正准备高兴时,发现MySQL启动不了了,网上搜了一下午,各种都没有解决。后来在一篇文章才得知又是"SELinux"惹得祸。解决办法如下:

  打开/etc/selinux/config,把SELINUX=enforcing改为SELINUX=disabled后存盘退出重启机器试试,必须要重启,很关键。

 

  

 

  机器重启之后,在把"mysql服务"启动。

  

  第七步:修改登录密码。

  MySQL默认没有密码,安装完毕增加密码的重要性是不言而喻的。

  • 修改前,直接登录

 

  

  在没有添加密码前,直接输入"mysql"就能登录到MySQL数据库里。

  • 修改登录密码

  用到的命令如下:

 

mysqladmin -u root password 'new-password'

格式:mysqladmin -u用户名 -p旧密码 password 新密码

 

  我们这里设置MySQL数据库"root"用户的密码为"hadoop"。执行的命令如下:

 

mysqladmin –u root password hadoop

 

  

 

  • 测试是否修改成功

  (1)不用密码登录

 

  

  此时显示错误,说明密码已经修改。

  (2)用修改后的密码登录

 

  

  从上图中得知,我们已经成功修改了密码,并且用新的密码登录了MySQL服务器。

  第八步:配置防火墙

  第一种:修改防火墙配置文件"/etc/sysconfig/iptables",添加如下内容:

 

-A INPUT -m state --state NEW -m tcp -p tcp --sport 3306 -j ACCEPT

-A OUTPUT -m state --state NEW -m tcp -p tcp --dport 3306 -j ACCEPT

 

  然后执行下面命令,使防火墙立即生效。

 

service iptables restart

 

  第二种:关闭防火墙

  通过下面两个命令使防火墙关闭,并且永远不起作用。

 

service iptables stop

chkconfig iptables off

 

  我们在这里为了方便,采用第二种方法,执行效果如下。

 

  

    第九步:验证MySQL数据库编码是否为UTF-8。

  连接上数据库之后,输入命令:"SHOW VARIABLES LIKE '%char%';"即可查看到现在你的数据库所使用的字符集了。

 

  

  第十步:删除空用户,增强安全。

  目前为止我们都是以"root"的身份进行的,但是当我们切换至普通用户登录MySQL时,直接输入"mysql"就进去了,我们刚才不是设置密码了吗?怎么就失效了呢?说明有空用户存在。先用命令"exit"退出,在按照下面命令进行修正。

 

  

 

     解决步骤如下:

  • 以MySQL用户"root"用密码形式登录。

 

mysql -u root -p

 

  • 删除空用户,强烈建议。

 

mysql>delete from mysql.user where user='';

 

  • 刷新权限表,以便可以使更改立即生效。

 

mysql>flush privileges;

 

  • 输入"exit",退出MySQL。

 

mysql>exit

 

  • 再重新以"mysql"登录测试

 

mysql

 

     发现以"mysql"登录已经失效,必须以"mysql –u root -p"才能登录。

 

     下面是执行效果截图:

 

  

2、MapReduce与MySQL交互

  MapReduce技术推出后,曾遭到关系数据库研究者的挑剔和批评,认为MapReduce不具备有类似于关系数据库中的结构化数据存储和处理能力。为此,Google和MapReduce社区进行了很多努力。一方面,他们设计了类似于关系数据中结构化数据表的技术(Google的BigTable,Hadoop的HBase)提供一些粗粒度的结构化数据存储和处理能力;另一方面,为了增强与关系数据库的集成能力,Hadoop MapReduce提供了相应的访问关系数据库库的编程接口。

  MapReduce与MySQL交互的整体架构如下图所示。

 

图2-1整个环境的架构

  具体到MapReduce框架读/写数据库,有2个主要的程序分别是 DBInputFormatDBOutputFormat,DBInputFormat 对应的是SQL语句select,而DBOutputFormat 对应的是 Inster/update,使用DBInputFormat和DBOutputForma时候需要实现InputFormat这个抽象类,这个抽象类含有getSplits()和createRecordReader()抽象方法,在DBInputFormat类中由 protected String getCountQuery() 方法传入结果集的个数,getSplits()方法再确定输入的切分原则,利用SQL中的 LIMIT 和 OFFSET 进行切分获得数据集的范围 ,请参考DBInputFormat源码中public InputSplit[] getSplits(JobConf job, int chunks) throws IOException的方法,在DBInputFormat源码中createRecordReader()则可以按一定格式读取相应数据。

      1)建立关系数据库连接

  • DBConfiguration:提供数据库配置和创建连接的接口。

      DBConfiguration类中提供了一个静态方法创建数据库连接:

 

public static void configureDB(Job job,String driverClass,String dbUrl,String userName,String Password)

 

      其中,job为当前准备执行的作业,driverClasss为数据库厂商提供的访问其数据库的驱动程序,dbUrl为运行数据库的主机的地址,userName和password分别为数据库提供访问地用户名和相应的访问密码。

      2)相应的从关系数据库查询和读取数据的接口

  • DBInputFormat:提供从数据库读取数据的格式。
  • DBRecordReader:提供读取数据记录的接口。

  3)相应的向关系数据库直接输出结果的编程接口

  • DBOutputFormat:提供向数据库输出数据的格式。
  • DBRecordWrite:提供数据库写入数据记录的接口。

  数据库连接完成后,即可完成从MapReduce程序向关系数据库写入数据的操作。为了告知数据库将写入哪个表中的哪些字段,DBOutputFormat中提供了一个静态方法来指定需要写入的数据表和字段:

 

public static void setOutput(Job job,String tableName,String ... fieldName)

 

      其中,tableName指定即将写入的数据表,后续参数将指定哪些字段数据将写入该表。

2.1 从数据库中输入数据

      虽然Hadoop允许从数据库中直接读取数据记录作为MapReduce的输入,但处理效率较低,而且大量频繁地从MapReduce程序中查询读取关系数据库可能会大大增加数据库访问负载,因此DBInputFormat仅适合读取小量数据记录计算和应用不适合数据仓库联机数据分析大量数据读取处理

      读取大量数据记录一个更好的解决办法是:用数据库中的Dump工具将大量待分析数据输出文本数据文件,并上载到HDFS中进行处理。

 

      1)首先创建要读入的数据

  • Windows环境

  首先创建数据库"school",使用下面命令进行:

 

create database school;

 

      然后通过以下几句话,把我们事先准备好的sql语句(student.sql事先放到了D盘目录)导入到刚创建的"school"数据库中。用到的命令如下:

 

use school;

source d:\student.sql

 

      "student.sql"中的内容如下所示:

 

DROP TABLE IF EXISTS `school`.`student`;

 

CREATE TABLE `school`.`student` (

`id` int(11) NOT NULL default '0',

`name` varchar(20) default NULL,

`sex` varchar(10) default NULL,

`age` int(10) default NULL,

PRIMARY KEY (`id`)

) ENGINE=InnoDB DEFAULT CHARSET=utf8;

 

INSERT INTO `student` VALUES ('201201', '张三', '男', '21');

INSERT INTO `student` VALUES ('201202', '李四', '男', '22');

INSERT INTO `student` VALUES ('201203', '王五', '女', '20');

INSERT INTO `student` VALUES ('201204', '赵六', '男', '21');

INSERT INTO `student` VALUES ('201205', '小红', '女', '19');

INSERT INTO `student` VALUES ('201206', '小明', '男', '22');

 

      执行结果如下所示:

 

 

      查询刚才创建的数据库表"student"的内容。

 

 

      结果发现显示是乱码,记得我当时是设置的UTF-8,怎么就出现乱码了呢?其实我们使用的操作系统的系统为中文,且它的默认编码是gbk,而MySQL的编码有两种,它们分别是:

  【client】:客户端的字符集。客户端默认字符集。当客户端向服务器发送请求时,请求以该字符集进行编码。

  【mysqld】:服务器字符集,默认情况下所采用的。

 

      找到安装MySQL目录,比如我们的安装目录为:

 

E:\HadoopWorkPlat\MySQL Server 5.5

 

      从中找到"my.ini"配置文件,最终发现my.ini里的2个character_set把client改成gbk,把server改成utf8就可以了。

    【client】端:

 

[client]

port=3306

[mysql]

default-character-set=gbk

 

    【mysqld】端:

 

[mysqld]

# The default character set that will be used when a new schema or table is

# created and no character set is defined

character-set-server=utf8

 

      按照上面修改完之后,重启MySQL服务。

 

 

      此时在Windows下面的数据库表已经准备完成了。

 

  • Linux环境

  首先通过"FlashFXP"把我们刚才的"student.sql"上传到"/home/hadoop"目录下面,然后按照上面的语句创建"school"数据库。

 

  

      查看我们上传的"student.sql"内容:

 

  

      创建"school"数据库,并导入"student.sql"语句。

 

  

 

      显示数据库"school"中的表"student"信息。

 

  

     显示表"student"中的内容。

 

  

 

      到此为止在"Windows"和"Linux"两种环境下面都创建了表"student"表,并初始化了值。下面就开始通过MapReduce读取MySQL库中表"student"的信息。

      2)使MySQL能远程连接

      MySQL默认是允许别的机器进行远程访问地,为了使Hadoop集群能访问MySQL数据库,所以进行下面操作。

  • 用MySQL用户"root"登录。

 

mysql -u root -p

 

  • 使用下面语句进行授权,赋予任何主机访问数据的权限。

 

GRANT ALL PRIVILEGES ON *.* TO 'root'@'%' IDENTIFIED BY 'hadoop' WITH GRANT OPTION;

 

  • 刷新,使之立即生效。

 

FLUSH PRIVILEGES;

 

      执行结果如下图。

      Windows下面:

 

  

      Linux下面:

 

  

      到目前为止,如果连接Win7上面的MySQL数据库还不行,大家还应该记得前面在Linux下面关掉了防火墙但是我们在Win7下对防火墙并没有做任何处理,如果不对防火墙做处理,即使执行了上面的远程授权,仍然不能连接。下面是设置Win7上面的防火墙,使远程机器能通过3306端口访问MySQL数据库。

      解决方案:只要在'入站规则'上建立一个3306端口即可。

  执行顺序控制面板à管理工具à高级安全的Windows防火墙à入站规则

  然后新建规则à选择'端口'à在'特定本地端口'上输入一个'3306' à选择'允许连接'=>选择'域'、'专用'、'公用'=>给个名称,如:MySqlInput

 

      3)对JDBC的Jar包处理

      因为程序虽然用Eclipse编译运行但最终要提交到Hadoop集群上,所以JDBC的jar必须放到Hadoop集群中。有两种方式:

      (1)在每个节点下的${HADOOP_HOME}/lib下添加该包,重启集群,一般是比较原始的方法。

      我们的Hadoop安装包在"/usr/hadoop",所以把Jar放到"/usr/hadoop/lib"下面,然后重启,记得是Hadoop集群中所有的节点都要放,因为执行分布式是程序是在每个节点本地机器上进行。

      (2)在Hadoop集群的分布式文件系统中创建"/lib"文件夹,并把我们的的JDBC的jar包上传上去,然后在主程序添加如下语句,就能保证Hadoop集群中所有的节点都能使用这个jar包。因为这个jar包放在了HDFS上,而不是本地系统,这个要理解清楚。

 

DistributedCache.addFileToClassPath(new Path("/lib/mysql-connector-java-5.1.18-bin.jar"), conf);

 

      我们用的JDBC的jar如下所示:

 

mysql-connector-java-5.1.18-bin.jar

 

      通过Eclipse下面的DFS Locations进行创建"/lib"文件夹,并上传JDBC的jar包。执行结果如下:

  

      备注我们这里采用第二种方式

      4)源程序代码如下所示

 

package com.hebut.mr;

 

import java.io.IOException;

import java.io.DataInput;

import java.io.DataOutput;

import java.sql.Connection;

import java.sql.DriverManager;

import java.sql.PreparedStatement;

import java.sql.ResultSet;

import java.sql.SQLException;

 

import org.apache.hadoop.filecache.DistributedCache;

import org.apache.hadoop.fs.Path;

import org.apache.hadoop.io.LongWritable;

import org.apache.hadoop.io.Text;

import org.apache.hadoop.io.Writable;

import org.apache.hadoop.mapred.JobClient;

import org.apache.hadoop.mapred.JobConf;

import org.apache.hadoop.mapred.MapReduceBase;

import org.apache.hadoop.mapred.Mapper;

import org.apache.hadoop.mapred.OutputCollector;

import org.apache.hadoop.mapred.FileOutputFormat;

import org.apache.hadoop.mapred.Reporter;

import org.apache.hadoop.mapred.lib.IdentityReducer;

import org.apache.hadoop.mapred.lib.db.DBWritable;

import org.apache.hadoop.mapred.lib.db.DBInputFormat;

import org.apache.hadoop.mapred.lib.db.DBConfiguration;

 

public class ReadDB {

 

    public static class Map extends MapReduceBase implements

            Mapper<LongWritable, StudentRecord, LongWritable, Text> {

 

        // 实现map函数

        public void map(LongWritable key, StudentRecord value,

        OutputCollector<LongWritable, Text> collector, Reporter reporter)

                throws IOException {

            collector.collect(new LongWritable(value.id),

                    new Text(value.toString()));

        }

 

    }

 

    public static class StudentRecord implements Writable, DBWritable {

        public int id;

        public String name;

        public String sex;

        public int age;

 

        @Override

        public void readFields(DataInput in) throws IOException {

            this.id = in.readInt();

            this.name = Text.readString(in);

            this.sex = Text.readString(in);

            this.age = in.readInt();

        }

 

        @Override

        public void write(DataOutput out) throws IOException {

            out.writeInt(this.id);

            Text.writeString(out, this.name);

            Text.writeString(out, this.sex);

            out.writeInt(this.age);

        }

 

        @Override

        public void readFields(ResultSet result) throws SQLException {

            this.id = result.getInt(1);

            this.name = result.getString(2);

            this.sex = result.getString(3);

            this.age = result.getInt(4);

        }

 

        @Override

        public void write(PreparedStatement stmt) throws SQLException {

            stmt.setInt(1, this.id);

            stmt.setString(2, this.name);

            stmt.setString(3, this.sex);

            stmt.setInt(4, this.age);

        }

 

        @Override

        public String toString() {

            return new String("学号:" + this.id + "_姓名:" + this.name

                    + "_性别:"+ this.sex + "_年龄:" + this.age);

        }

    }

 

    public static void main(String[] args) throws Exception {

 

        JobConf conf = new JobConf(ReadDB.class);

 

        // 这句话很关键

        conf.set("mapred.job.tracker", "192.168.1.2:9001");

 

        // 非常重要,值得关注

        DistributedCache.addFileToClassPath(new Path(

         "/lib/mysql-connector-java-5.1.18-bin.jar"), conf);

 

        // 设置输入类型

        conf.setInputFormat(DBInputFormat.class);

 

        // 设置输出类型

        conf.setOutputKeyClass(LongWritable.class);

        conf.setOutputValueClass(Text.class);

 

        // 设置MapReduce

        conf.setMapperClass(Map.class);

        conf.setReducerClass(IdentityReducer.class);

 

        // 设置输出目录

        FileOutputFormat.setOutputPath(conf, new Path("rdb_out"));

 

        // 建立数据库连接

        DBConfiguration.configureDB(conf, "com.mysql.jdbc.Driver",

            "jdbc:mysql://192.168.1.24:3306/school", "root", "hadoop");

 

        // 读取"student"表中的数据

        String[] fields = { "id", "name", "sex", "age" };

        DBInputFormat.setInput(conf, StudentRecord.class, "student", null,"id", fields);

 

        JobClient.runJob(conf);

    }

}

 

      备注:由于Hadoop1.0.0新的API对关系型数据库暂不支持,只能用旧的API进行,所以下面的"向数据库中输出数据"也是如此。

 

      5)运行结果如下所示

      经过上面的设置后,已经通过连接Win7和Linux上的MySQL数据库,执行结果都一样。唯独变得就是代码中"DBConfiguration.configureDB"中MySQL数据库所在机器的IP地址。

 

 

2.2 向数据库中输出数据

      基于数据仓库数据分析挖掘输出结果的数据量一般不会太大,因而可能适合直接向数据库写入我们这里尝试与"WordCount"程序相结合,把单词统计的结果存入到关系型数据库中。

      1)创建写入的数据库表

      我们还使用刚才创建的数据库"school",只是在里添加一个新的表"wordcount",还是使用下面语句执行:

 

use school;

source sql脚本全路径

 

      下面是要创建的"wordcount"表的sql脚本。

 

DROP TABLE IF EXISTS `school`.`wordcount`;

 

CREATE TABLE `school`.`wordcount` (

`id` int(11) NOT NULL auto_increment,

`word` varchar(20) default NULL,

`number` int(11) default NULL,

PRIMARY KEY (`id`)

) ENGINE=InnoDB DEFAULT CHARSET=utf8;

 

      执行效果如下所示:

  • Windows环境

  • Linux环境

 

      2)程序源代码如下所示

 

package com.hebut.mr;

 

import java.io.IOException;

import java.io.DataInput;

import java.io.DataOutput;

import java.sql.PreparedStatement;

import java.sql.ResultSet;

import java.sql.SQLException;

import java.util.Iterator;

import java.util.StringTokenizer;

 

import org.apache.hadoop.filecache.DistributedCache;

import org.apache.hadoop.fs.Path;

import org.apache.hadoop.io.IntWritable;

import org.apache.hadoop.io.Text;

import org.apache.hadoop.io.Writable;

import org.apache.hadoop.mapred.FileInputFormat;

import org.apache.hadoop.mapred.JobClient;

import org.apache.hadoop.mapred.JobConf;

import org.apache.hadoop.mapred.MapReduceBase;

import org.apache.hadoop.mapred.Mapper;

import org.apache.hadoop.mapred.OutputCollector;

import org.apache.hadoop.mapred.Reducer;

import org.apache.hadoop.mapred.Reporter;

import org.apache.hadoop.mapred.TextInputFormat;

import org.apache.hadoop.mapred.lib.db.DBOutputFormat;

import org.apache.hadoop.mapred.lib.db.DBWritable;

import org.apache.hadoop.mapred.lib.db.DBConfiguration;

 

public class WriteDB {

    // Map处理过程

    public static class Map extends MapReduceBase implements

            Mapper<Object, Text, Text, IntWritable> {

 

        private final static IntWritable one = new IntWritable(1);

        private Text word = new Text();

 

        @Override

        public void map(Object key, Text value,

            OutputCollector<Text, IntWritable> output, Reporter reporter)

                throws IOException {

            String line = value.toString();

            StringTokenizer tokenizer = new StringTokenizer(line);

            while (tokenizer.hasMoreTokens()) {

                word.set(tokenizer.nextToken());

                output.collect(word, one);

            }

        }

    }

 

    // Combine处理过程

    public static class Combine extends MapReduceBase implements

            Reducer<Text, IntWritable, Text, IntWritable> {

 

        @Override

        public void reduce(Text key, Iterator<IntWritable> values,

            OutputCollector<Text, IntWritable> output, Reporter reporter)

                throws IOException {

            int sum = 0;

            while (values.hasNext()) {

                sum += values.next().get();

            }

            output.collect(key, new IntWritable(sum));

        }

    }

 

    // Reduce处理过程

    public static class Reduce extends MapReduceBase implements

            Reducer<Text, IntWritable, WordRecord, Text> {

 

        @Override

        public void reduce(Text key, Iterator<IntWritable> values,

            OutputCollector<WordRecord, Text> collector, Reporter reporter)

                throws IOException {

 

            int sum = 0;

            while (values.hasNext()) {

                sum += values.next().get();

            }

 

            WordRecord wordcount = new WordRecord();

            wordcount.word = key.toString();

            wordcount.number = sum;

 

            collector.collect(wordcount, new Text());

        }

    }

 

    public static class WordRecord implements Writable, DBWritable {

        public String word;

        public int number;

 

        @Override

        public void readFields(DataInput in) throws IOException {

            this.word = Text.readString(in);

            this.number = in.readInt();

        }

 

        @Override

        public void write(DataOutput out) throws IOException {

            Text.writeString(out, this.word);

            out.writeInt(this.number);

        }

 

        @Override

        public void readFields(ResultSet result) throws SQLException {

            this.word = result.getString(1);

            this.number = result.getInt(2);

        }

 

        @Override

        public void write(PreparedStatement stmt) throws SQLException {

            stmt.setString(1, this.word);

            stmt.setInt(2, this.number);

        }

    }

 

    public static void main(String[] args) throws Exception {

 

        JobConf conf = new JobConf(WriteDB.class);

 

        // 这句话很关键

        conf.set("mapred.job.tracker", "192.168.1.2:9001");

 

        DistributedCache.addFileToClassPath(new Path(

                "/lib/mysql-connector-java-5.1.18-bin.jar"), conf);

 

        // 设置输入输出类型

        conf.setInputFormat(TextInputFormat.class);

        conf.setOutputFormat(DBOutputFormat.class);

        // 不加这两句,通不过,但是网上给的例子没有这两句。

        conf.setOutputKeyClass(Text.class);

        conf.setOutputValueClass(IntWritable.class);

 

        // 设置MapReduce

        conf.setMapperClass(Map.class);

        conf.setCombinerClass(Combine.class);

        conf.setReducerClass(Reduce.class);

 

        // 设置输如目录

        FileInputFormat.setInputPaths(conf, new Path("wdb_in"));

 

        // 建立数据库连接

        DBConfiguration.configureDB(conf, "com.mysql.jdbc.Driver",

            "jdbc:mysql://192.168.1.24:3306/school", "root", "hadoop");

 

        // 写入"wordcount"表中的数据

        String[] fields = { "word", "number" };

        DBOutputFormat.setOutput(conf, "wordcount", fields);

 

        JobClient.runJob(conf);

    }

}

 

      3)运行结果如下所示

  • Windows环境

  测试数据:

(1)file1.txt

 

hello word

hello hadoop

 

    (2)file2.txt

 

虾皮 hadoop

虾皮 word

软件 软件

 

      运行结果:

 

      我们发现上图中出现了"?",后来查找原来是因为我的测试数据时在Windows用记事本写的然后保存为"UTF-8",在保存时为了区分编码,自动在前面加了一个"BOM",但是不会显示任何结果。然而我们的代码把它识别为"?"进行处理。这就出现了上面的结果,如果我们在每个要处理的文件前面的第一行加一个空格,结果就成如下显示:

 

      接着又做了一个测试,在Linux上面用下面命令创建了一个文件,并写上中文内容。结果显示并没有出现"?",而且网上说不同的记事本软件(EmEditor、UE)保存为"UTF-8"就没有这个问题。经过修改之后的Map类,就能够正常识别了。

 

    // Map处理过程

    public static class Map extends MapReduceBase implements

            Mapper<Object, Text, Text, IntWritable> {

 

        private final static IntWritable one = new IntWritable(1);

        private Text word = new Text();

 

        @Override

        public void map(Object key, Text value,

            OutputCollector<Text, IntWritable> output, Reporter reporter)

                throws IOException {

            String line = value.toString();

           

            //处理记事本UTF-8的BOM问题

            if (line.getBytes().length > 0) {

                if ((int) line.charAt(0) == 65279) {

                    line = line.substring(1);

                }

            }

           

            StringTokenizer tokenizer = new StringTokenizer(line);

            while (tokenizer.hasMoreTokens()) {

                word.set(tokenizer.nextToken());

                output.collect(word, one);

            }

        }

    }

 

      处理之后的结果:

 

 

      从上图中得知,我们的问题已经解决了,因此,在编辑、更改任何文本文件时,请务必使用不会乱加BOM的编辑器。Linux下的编辑器应该都没有这个问题。Windows下,请勿使用记事本等编辑器。推荐的编辑器是: Editplus 2.12版本以上; EmEditor; UltraEdit(需要取消'添加BOM'的相关选项); Dreamweaver(需要取消'添加BOM'的相关选项) 等。

  对于已经添加了BOM的文件,要取消的话,可以用以上编辑器另存一次。(Editplus需要先另存为gb,再另存为UTF-8。) DW解决办法如下: 用DW打开指定文件,按Ctrl+Jà标题/编码à编码选择"UTF-8",去掉"包括Unicode签名(BOM)"勾选à保存/另存为,即可。

    国外有一个牛人已经把这个问题解决了,使用"UnicodeInputStream"、"UnicodeReader"。

    地址:http://koti.mbnet.fi/akini/java/unicodereader/

    示例:Java读带有BOM的UTF-8文件乱码原因及解决方法

    代码:http://download.csdn.net/detail/xia520pi/4146123

 

  • Linux环境

  测试数据:

    (1)file1.txt

 

MapReduce is simple

 

    (2)file2.txt

 

MapReduce is powerful is simple

 

    (3)file2.txt

 

Hello MapReduce bye MapReduce

 

      运行结果:

 

 

      到目前为止,MapReduce与关系型数据库交互已经结束,从结果中得知,目前新版的API还不能很好的支持关系型数据库的操作,上面两个例子都是使用的旧版的API。关于更多的MySQL操作,具体参考"Hadoop集群_第10期副刊_常用MySQL数据库命令_V1.0"。

 

      本期历时五天,终于完成,期间遇到的关键问题如下:

 

  • MySQL的JDBC的jar存放问题。
  • Win7对MySQL防火墙的设置。
  • Linux中MySQL变更目录不能启动。
  • MapReduce处理带BOM的UTF-8问题。
  • 设置MySQL可以远程访问。
  • MySQL处理中文乱码问题。

 

  从这几天对MapReduce的了解,发现其实Hadoop对关系型数据库的处理还不是很强,主要是Hadoop和关系型数据做的事不是同一类型,各有所特长。下面几期我们将对Hadoop里的HBase和Hive进行全面了解。

 

  文章下载地址:http://files.cnblogs.com/xia520pi/HadoopCluster_Vol.10.rar

Hadoop集群(第10期副刊)_常用MySQL数据库命令

1、系统管理

1.1 连接MySQL

  格式: mysql -h主机地址 -u用户名 -p用户密码

  举例

  例1:连接到本机上的MySQL。

  首先在打开DOS窗口,然后进入目录 mysqlbin,再键入命令"mysql –u root –p",回车后提示你输密码,如果刚安装好MySQL,超级用户"root"是没有密码的,故直接回车即可进入到MySQL中了,MySQL的提示符是: mysql>。

  例2:连接到远程主机上的MYSQL。假设远程主机的IP为:110.110.110.110,用户名为root,密码为abcd123。则键入以下命令:

 

mysql -h 110.110.110.110 -u root –p abcd123

 

  备注:u与root可以不用加空格,其它也一样。

  退出MySQL命令: exit (回车)。

1.2 修改新密码

  格式:mysqladmin -u用户名 -p旧密码 password 新密码

  举例

  例1:给root加个密码ab12。首先在DOS下进入目录mysqlbin,然后键入以下命令:

 

mysqladmin -u root -password ab12

 

  备注:因为开始时root没有密码,所以-p旧密码一项就可以省略了。

  例2:再将root的密码改为djg345。

 

mysqladmin -u root -p ab12 password djg345

 

1.3 增加新用户

  备注:和上面不同,下面的因为是MySQL环境中的命令,所以后面都带一个分号";"作为命令结束符。

  格式:grant select on 数据库.* to 用户名@登录主机 identified by '密码'

  举例

  例1:增加一个用户test1密码为abc,让他可以在任何主机上登录,并对所有数据库有查询、插入、修改、删除的权限。首先用以root用户连入MySQL,然后键入以下命令:

 

grant select,insert,update,delete on *.* to test2@localhost identified by 'abc';

 

  或者

 

grant all privileges on *.* to test2@localhost identified by 'abc';

 

  然后刷新权限设置。

 

flush privileges;

 

  例2:如果你不想test2有密码操作数据库"mydb"里的数据表,可以再打一个命令将密码消掉。

 

grant select,insert,update,delete on mydb.* to test2@localhost identified by '';

 

1.4 启动停止MySQL

  1)Windows环境下

  首先进入DOS环境,然后进行下面操作。

  • 启动服务

 

net start mysql

 

  • 停止服务

 

net stop mysql

 

  2)Linux环境下

  • 启动服务

 

service mysql start

 

  • 停止服务

 

service mysql stop

 

2、数据库操作

2.1 库操作

  1)创建数据库

  命令:create database <数据库名>

  例如:建立一个名为xhkdb的数据库

 

mysql> create database xhkdb;

 

  2)显示所有的数据库

  命令:show databases (注意:最后有个s

 

mysql> show databases;

 

  3)删除数据库

  命令:drop database <数据库名>

  例如:删除名为 xhkdb的数据库

 

mysql> drop database xhkdb;

 

  4)连接数据库

  命令: use <数据库名>

  例如:如果xhkdb数据库存在,尝试存取它

 

mysql> use xhkdb;

 

  屏幕提示:Database changed

  5)查看当前使用的数据库

 

mysql> select database();

 

  6)当前数据库包含的表信息

 

mysql> show tables; (注意:最后有个s)

 

2.2 表操作

  备注:操作之使用"use <数据库名>"应连接某个数据库。

 

  1)建表

  命令:create table <表名> ( <字段名1> <类型1> [,..<字段名n> <类型n>]);

 

mysql> create table MyClass(

> id int(4) not null primary key auto_increment,

> name char(20) not null,

> sex int(4) not null default '0',

> degree double(16,2));

 

  2)获取表结构

  命令: desc 表名,或者show columns from 表名

 

mysql>DESCRIBE MyClass

mysql> desc MyClass;

mysql> show columns from MyClass;

 

  3)删除表

  命令:drop table <表名>

  例如:删除表名为 MyClass 的表

 

mysql> drop table MyClass;

 

  4)插入数据

  命令:insert into <表名> [( <字段名1>[,..<字段名n > ])] values ( 值1 )[, ( 值n )]

  例如:往表 MyClass中插入二条记录,这二条记录表示:编号为1的名为Tom的成绩为96.45,编号为2 的名为Joan 的成绩为82.99,编号为3 的名为Wang 的成绩为96.5。

 

mysql> insert into MyClass values(1,'Tom',96.45),(2,'Joan',82.99), (2,'Wang', 96.59);

 

  5)查询表中的数据

  • 查询所有行

  命令: select <字段1,字段2,...> from < 表名 > where < 表达式 >

  例如:查看表 MyClass 中所有数据

 

mysql> select * from MyClass;

 

  • 查询前几行数据

  例如:查看表 MyClass 中前2行数据

 

mysql> select * from MyClass order by id limit 0,2;

 

  或者

 

mysql> select * from MyClass limit 0,2;

 

  6)删除表中数据

  命令:delete from 表名 where 表达式

  例如:删除表 MyClass中编号为1 的记录

 

mysql> delete from MyClass where id=1;

 

  7)修改表中数据

  命令:update 表名 set 字段=新值,… where 条件

 

mysql> update MyClass set name='Mary' where id=1;

 

  8)在表中增加字段

  命令:alter table 表名 add字段 类型 其他;

  例如:在表MyClass中添加了一个字段passtest,类型为int(4),默认值为0

 

mysql> alter table MyClass add passtest int(4) default '0'

 

  9)更改表名

  命令:rename table 原表名 to 新表名;

  例如:在表MyClass名字更改为YouClass

 

mysql> rename table MyClass to YouClass;

 

  10)更新字段内容

  命令:update 表名 set 字段名 = 新内容

     update 表名 set 字段名 = replace(字段名,'旧内容','新内容');

  例如:文章前面加入4个空格

 

update article set content=concat('  ',content);

 

3、数据库导入导出

3.1 从数据库导出数据库文件

  使用"mysqldump"命令

  首先进入DOS界面,然后进行下面操作。

  1)导出所有数据库

  格式:mysqldump -u [数据库用户名] -p -A>[备份文件的保存路径]

  2)导出数据和数据结构

  格式:mysqldump -u [数据库用户名] -p [要备份的数据库名称]>[备份文件的保存路径]

  举例

  例1:将数据库mydb导出到e:\MySQL\mydb.sql文件中。

  打开开始à运行à输入"cmd",进入命令行模式。

 

c:\> mysqldump -h localhost -u root -p mydb >e:\MySQL\mydb.sql

 

  然后输入密码,等待一会导出就成功了,可以到目标文件中检查是否成功。

  例2:将数据库mydb中的mytable导出到e:\MySQL\mytable.sql文件中。

 

c:\> mysqldump -h localhost -u root -p mydb mytable>e:\MySQL\mytable.sql

 

  例3:将数据库mydb的结构导出到e:\MySQL\mydb_stru.sql文件中。

 

c:\> mysqldump -h localhost -u root -p mydb --add-drop-table >e:\MySQL\mydb_stru.sql

 

  备注:-h localhost可以省略,其一般在虚拟主机上用。

  3)只导出数据不导出数据结构

  格式:mysqldump -u [数据库用户名] -p -t [要备份的数据库名称]>[备份文件的保存路径]

  4)导出数据库中的Events

  格式:mysqldump -u [数据库用户名] -p -E [数据库用户名]>[备份文件的保存路径]

  5)导出数据库中的存储过程和函数

  格式:mysqldump -u [数据库用户名] -p -R [数据库用户名]>[备份文件的保存路径]

3.2 从外部文件导入数据库中

  1)使用"source"命令

  首先进入"mysql"命令控制台,然后创建数据库,然后使用该数据库。最后执行下面操作。

 

mysql>source [备份文件的保存路径]

 



重点看第9期,转载地址:

  2)使用"<"符号

  首先进入"mysql"命令控制台,然后创建数据库,然后退出MySQL,进入DOS界面。最后执行下面操作。

 

mysql -u root –p < [备份文件的保存路径]

 

  文章下载地址:http://files.cnblogs.com/xia520pi/HadoopCluster_Vol.10S.rar

http://www.cnblogs.com/xia520pi/category/346943.html  :地址 (重点第9期)
  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值