本书结合理论和实践,由浅入深,多方位介绍了Hadoop这一高性能的海量数据处理和分析平台。全书5部分24章,第Ⅰ部分介绍Hadoop基础知识,主题涉及Hadoop、MapReduce、Hadoop分布式文件系统、YARN、Hadoop的I/O操作。第Ⅱ部分介绍MapReduce,主题包括MapReduce应用开发;MapReduce的工作机制、MapReduce的类型与格式、MapReduce的特性。第Ⅲ部分介绍Hadoop的运维,主题涉及构建Hadoop集群、管理Hadoop。第Ⅳ部分介绍Hadoop相关开源项目,主题涉及Avro、Parquet、Flume、Sqoop、Pig、Hive、Crunch、Spark、HBase、ZooKeeper。第Ⅴ部分提供了三个案例,分别来自医疗卫生信息技术服务商塞纳(Cerner)、微软的人工智能项目ADAM(一种大规模分布式深度学习框架)和开源项目Cascading(一个新的针对MapReduce的数据处理API)。 本书是一本、的Hadoop参考书和工具书,阐述了Hadoop生态圈的发展和应用,程序员可以从中探索海量数据集的存储和分析,管理员可以从中了解Hadoop集群的安装和运维。
本书结合理论和实践,由浅入深,多方位介绍了Hadoop 这一高性能的海量数据处理和分析平台。全书5部分24 章,第Ⅰ部分介绍Hadoop 基础知识,第Ⅱ部分介绍MapReduce,第Ⅲ部分介绍Hadoop 的运维,第Ⅳ部分介绍Hadoop 相关开源项目,第Ⅴ部分提供了三个案例,分别来自医疗卫生信息技术服务商塞纳(Cerner)、微软的人工智能项目ADAM(一种大规模分布式深度学习框架)和开源项目Cascading(一个新的针对MapReduce 的数据处理API)。本书是一本专业、的Hadoop 参考书和工具书,阐述了Hadoop 生态圈的新发展和应用,程序员可以从中探索海量数据集的存储和分析,管理员可以从中了解Hadoop 集群的安装和运维。
作者简介
Tom White是最杰出的Hadoop专家之一。自2007年2月以来,Tom White一直是Apache Hadoop的提交者(committer),也是Apache软件基金会的成员。Tom是Cloudera的软件工程师,他是Cloudera的首批员工,对Apache和Cloudera做出了举足轻重的贡献。在此之前,他是一名独立的Hadoop顾问,帮助公司搭建、使用和扩展Hadoop。他是很多行业大会的专题演讲人,比如ApacheCon、OSCON和Strata。Tom在英国剑桥大学获得数学学士学位,在利兹大学获得科学哲学硕士学位。他目前与家人居住在威尔士。
译者简介
王海博士,解放军理工大学通信工程学院教授,博导,教研中心主任,长期从事无线自组网网络的设计与研发工作,主持国家自然科学基金、国家863计划课题等多项课题,近5年获军队科技进步二等奖1项,三等奖6项,作为及时发明人申请国家发明专利十余项,发表学术论文50余篇。
华东博士,现任南京医科大学计算机教研室教师,一直致力于计算机辅助教学的相关技术研究,陆续开发了人体解剖学网络自主学习考试平台、诊断学自主学习平台和面向执业医师考试的预约化考试平台等系统,并在各个学科得到广泛的使用,获得全国高等学校计算机课件评比一等奖和三等奖各一项。主编、副主编教材两部,获发明专利一项、软件著作权多项。
刘喻博士,长期从事软件开发、软件测试和软件工程化管理工作,目前任教于清华大学软件所。
吕粤海,长期从事军事通信网络技术研究与软件开发工作,先后通过华为光网络高级工程师认证、思科网络工程师认证。
第Ⅰ部分 Hadoop基础知识
第1章 初识Hadoop 3
MapReduce作业 37
数据 56
读取数据 58
压缩 106
集合 121
面向列的格式 136
第Ⅱ部分 关于MapReduce
第6章 MapReduce应用开发 141
Tool和ToolRunner 149
运行作业 156
界面 165
MapReduce作业 177 第Ⅰ部分 Hadoop基础知识
第3章 Hadoop分布式文件系统
当数据集的大小超过一台独立的物理计算机的存储能力时,就有必要对它进行分区(partition)并存储到若干台单独的计算机上。管理网络中跨多台计算机存储的文件系统称为分布式文件系统(distributed filesystem)。该系统架构于网络之上,势必会引入网络编程的复杂性,因此分布式文件系统比普通磁盘文件系统更为复杂。例如,使文件系统能够容忍节点故障且不丢失任何数据,就是一个极大的挑战。
Hadoop自带一个称为HDFS的分布式文件系统,即Hadoop Distributed Filesystem。在非正式文档或旧文档以及配置文件中,有时也简称为DFS,它们是一回事儿。HDFS是Hadoop的旗舰级文件系统,也是本章的重点,但实际上Hadoop是一个综合性的文件系统抽象,因此接下来我们将了解将Hadoop与其他存储系统集成的途径,例如本地文件系统和Amazon S3系统。
3.1 HDFS的设计
HDFS以流式数据访问模式来存储超大文件,运行于商用硬件集群上。①让我们仔细看看下面的描述。
超大文件 “超大文件”在这里指具有几百MB、几百GB甚至几百TB大小的文件。目前已经有存储PB级数据的Hadoop 集群了。②
流式数据访问 HDFS的构建思路是这样的:一次写入、多次读取是较高效的访问模式。数据集通常由数据源生成或从数据源复制而来,接着长时间在此数据集上进行各种分析。每次分析都将涉及该数据集的大部分数据甚至全部,因此读取整个数据集的时间延迟比读取及时条记录的时间延迟更重要。
商用硬件 Hadoop并不需要运行在昂贵且高的硬件上。它是设计运行在商用硬件(在各种零售店都能买到的普通硬件③)的集群上的,因此至少对于庞大的集群来说,节点故障的几率还是非常高的。HDFS遇到上述故障时,被设计成能够继续运行且不让用户察觉到明显的中断。
同样,那些不适合在HDFS上运行的应用也值得研究。目前HDFS对某些应用领域并不适合,不过以后可能会有所改进。
低时间延迟的数据访问 要求低时间延迟数据访问的应用,例如几十毫秒范围,不适合在HDFS上运行。记住,HDFS是为高数据吞吐量应用优化的,这可能会以提高时间延迟为代价。目前,对于低延迟的访问需求,HBase(参见第20 章)是更好的选择。
大量的小文件 由于namenode将文件系统的元数据存储在内存中,因此该文件系统所能存储的文件总数受限于namenode的内存容量。根据经验,每个文件、目录和数据块的存储信息大约占150字节。因此,举例来说,如果有一百万个文件,且每个文件占一个数据块,那至少需要300 MB 的内存。尽管存储上百万个文件是可行的,但是存储数十亿个文件就超出了当前硬件的能力。④
多用户写入,任意修改文件 HDFS中的文件写入只支持单个写入者,而且写操作总是以“只添加”方式在文件末尾写数据。它不支持多个写入者的操作,也不支持在文件的任意位置进行修改。可能以后会支持这些操作,但它们相对比较低效。
3.2 HDFS的概念
3.2.1 数据块
每个磁盘都有默认的数据块大小,这是磁盘进行数据读/写的最小单位。构建于单个磁盘之上的文件系统通过磁盘块来管理该文件系统中的块,该文件系统块的大小可以是磁盘块的整数倍。文件系统块一般为几千字节,而磁盘块一般为512字节。这些信息(文件系统块大小)对于需要读/写文件的文件系统用户来说是透明的。尽管如此,系统仍然提供了一些工具(如df和fsck)来维护文件系统,由它们对文件系统中的块进行操作。
HDFS同样也有块(block)的概念,但是大得多,默认为128 MB。与单一磁盘上的文件系统相似,HDFS上的文件也被划分为块大小的多个分块(chunk),作为独立的存储单元。但与面向单一磁盘的文件系统不同的是,HDFS中小于一个块大小的文件不会占据整个块的空间(例如,当一个 1MB的文件存储在一个128 MB 的块中时,文件只使用1 MB的磁盘空间,而不是128 MB)。如果没有特殊指出,本书中提到的“块”特指HDFS中的块。
HDFS中的块为什么这么大?
HDFS的块比磁盘的块大,其目的是为了最小化寻址开销。如果块足够大,从磁盘传输数据的时间会明显大于定位这个块开始位置所需的时间。因而,传输一个由多个块组成的大文件的时间取决于磁盘传输速率。
我们来做一个速算,如果寻址时间约为10 ms,传输速率为100 MB/s,为了使寻址时间仅占传输时间的1%,我们要将块大小设置约为100 MB。默认的块大小实际为128 MB,但是很多情况下HDFS安装时使用更大的块。以后随着新一代磁盘驱动器传输速率的提升,块的大小会被设置得更大。
但是这个参数也不会设置得过大。MapReduce中的map任务通常一次只处理一个块中的数据,因此如果任务数太少(少于集群中的节点数量),作业的运行速度就会比较慢。
对分布式文件系统中的块进行抽象会带来很多好处。及时个最明显的好处是,一个文件的大小可以大于网络中任意一个磁盘的容量。文件的所有块并不需要存储在同一个磁盘上,因此它们可以利用集群上的任意一个磁盘进行存储。事实上,尽管不常见,但对于整个HDFS集群而言,也可以仅存储一个文件,该文件的块占满集群中所有的磁盘。
第二个好处是,使用抽象块而非整个文件作为存储单元,大大简化了存储子系统的设计。简化是所有系统的目标,但是这对于故障种类繁多的分布式系统来说尤为重要。将存储子系统的处理对象设置为块,可简化存储管理(由于块的大小是固定的,因此计算单个磁盘能存储多少个块就相对容易)。同时也消除了对元数据的顾虑(块只是要存储的大块数据,而文件的元数据,如权限信息,并不需要与块一同存储,这样一来,其他系统就可以单独管理这些元数据)。
不仅如此,块还非常适合用于数据备份进而提供数据容错能力和提高可用性。将每个块复制到少数几个物理上相互独立的机器上(默认为3个),可以确保在块、磁盘或机器发生故障后数据不会丢失。如果发现一个块不可用,系统会从其他地方读取另一个复本,而这个过程对用户是透明的。一个因损坏或机器故障而丢失的块可以从其他候选地点复制到另一台可以正常运行的机器上,以保障复本的数量回到正常水平(参见5.1节对数据完整性的讨论,进一步了解如何应对数据损坏)。同样,有些应用程序可能选择为一些常用的文件块设置更高的复本数量进而分散集群中的读取负载。
与磁盘文件系统相似,HDFS中fsck指令可以显示块信息。例如,执行以下命令将列出文件系统中各个文件由哪些块构成,详情可以参见11.1.4节对文件系统检查(fsck)的讨论:
% hdfs fsck / -files -blocks
3.2.2 namenode和datanode
HDFS集群有两类节点以管理节点-工作节点模式运行,即一个namenode(管理节点)和多个datanode(工作节点)。namenode管理文件系统的命名空间。它维护着文件系统树及整棵树内所有的文件和目录。这些信息以两个文件形式长期保存在本地磁盘上:命名空间镜像文件和编辑日志文件。namenode也记录着每个文件中各个块所在的数据节点信息,但它并不长期保存块的位置信息,因为这些信息会在系统启动时根据数据节点信息重建。
客户端(client)代表用户通过与namenode和datanode交互来访问整个文件系统。客户端提供一个类似于POSIX(可移植操作系统界面)的文件系统接口,因此用户在编程时无需知道namenode和datanode也可实现其功能。
datanode是文件系统的工作节点。它们根据需要存储并检索数据块(受客户端或namenode调度),并且定期向namenode发送它们所存储的块的列表。
没有namenode,文件系统将无法使用。事实上,如果运行namenode服务的机器毁坏,文件系统上所有的文件将会丢失,因为我们不知道如何根据datanode的块重建文件。因此,对namenode实现容错非常重要,Hadoop为此提供两种机制。
及时种机制是备份那些组成文件系统元数据持久状态的文件。Hadoop可以通过配置使namenode在多个文件系统上保存元数据的持久状态。这些写操作是实时同步的,且是原子操作。一般的配置是,将持久状态写入本地磁盘的同时,写入一个远程挂载的网络文件系统(NFS)。
另一种可行的方法是运行一个辅助namenode,但它不能被用作namenode。这个辅助namenode的重要作用是定期合并编辑日志与命名空间镜像,以防止编辑日志过大。这个辅助namenode一般在另一台单独的物理计算机上运行,因为它需要占用大量CPU时间,并且需要与namenode一样多的内存来执行合并操作。它会保存合并后的命名空间镜像的副本,并在namenode发生故障时启用。但是,辅助namenode保存的状态总是滞后于主节点,所以在主节点全部失效时,难免会丢失部分数据。在这种情况下,一般把存储在NFS上的namenode元数据复制到辅助namenode并作为新的主namenode运行。(注意,也可以运行热备份namenode代替运行辅助namenode,具体参见3.2.5节对HDFS高可用性的讨论。
关于文件系统镜像和编辑日志的更多讨论,请参见11.1.1节。
3.2.3 块缓存
通常datanode从磁盘中读取块,但对于访问频繁的文件,其对应的块可能被显式地缓存在datanode的内存中,以堆外块缓存(off-heap block cache)的形式存在。默认情况下,一个块仅缓存在一个datanode的内存中,当然可以针每个文件配置datanode的数量。作业调度器(用于MapReduce、Spark和其他框架的)通过在缓存块的datanode上运行任务,可以利用块缓存的优势提高读操作的性能。例如,连接(join)操作中使用的一个小的查询表就是块缓存的一个很好的候选。
用户或应用通过在缓存池(cache pool)中增加一个cache directive来告诉namenode需要缓存哪些文件及存多久。缓存池是一个用于管理缓存权限和资源使用的管理性分组。
3.2.4 联邦HDFS
namenode在内存中保存文件系统中每个文件和每个数据块的引用关系,这意味着对于一个拥有大量文件的超大集群来说,内存将成为限制系统横向扩展的瓶颈(参见10.3.2节)。在2.x发行版本系列中引入的联邦HDFS允许系统通过添加namenode实现扩展,其中每个namenode管理文件系统命名空间中的一部分。例如,一个namenode可能管理/user目录下的所有文件,而另一个namenode可能管理/share目录下的所有文件。
在联邦环境下,每个namenode维护一个命名空间卷(namespace volume),由命名空间的元数据和一个数据块池(block pool)组成,数据块池包含该命名空间下文件的所有数据块。命名空间卷之间是相互独立的,两两之间并不相互通信,甚至其中一个namenode的失效也不会影响由其他namenode维护的命名空间的可用性。数据块池不再进行切分,因此集群中的datanode需要注册到每个namenode,并且存储着来自多个数据块池中的数据块。
要想访问联邦HDFS集群,客户端需要使用客户端挂载数据表将文件路径映射到namenode。该功能可以通过ViewFileSystem和viewfs://URI进行配置和管理。
3.2.5 HDFS的高可用性
通过联合使用在多个文件系统中备份namenode的元数据和通过备用namenode创建监测点能防止数据丢失,但是依旧无法实现文件系统的高可用性。namenode 依旧存在单点失效(SPOF, single point of failure)的问题。如果namenode失效了,那么所有的客户端,包括MapReduce作业,均无法读、写或列举(list)文件,因为namenode是存储元数据与文件到数据块映射的地方。在这一情况下,Hadoop系统无法提供服务直到有新的namenode上线。
在这样的情况下,要想从一个失效的namenode恢复,系统管理员得启动一个拥有文件系统元数据副本的新的namenode,并配置datanode和客户端以便使用这个新的namenode。新的namenode直到满足以下情形才能响应服务:(1)将命名空间的映像导入内存中;(2)重演编辑日志;(3)接收到足够多的来自datanode的数据块报告并退出安全模式。对于一个大型并拥有大量文件和数据块的集群,namenode的冷启动需要30分钟,甚至更长时间。
系统恢复时间太长,也会影响到日常维护。事实上,预期外的namenode失效出现概率很低,所以在现实中,计划内的系统失效时间实际更为重要。
Hadoop2针对上述问题增加了对HDFS高可用性(HA)的支持。在这一实现中,配置了一对活动-备用(active-standby) namenode。当活动namenode失效,备用namenode就会接管它的任务并开始服务于来自客户端的请求,不会有任何明显中断。实现这一目标需要在架构上做如下修改。
namenode之间需要通过高可用共享存储实现编辑日志的共享。当备用namenode接管工作之后,它将通读共享编辑日志直至末尾,以实现与活动namenode的状态同步,并继续读取由活动namenode写入的新条目。
datanode需要同时向两个namenode发送数据块处理报告,因为数据块的映射信息存储在namenode的内存中,而非磁盘。
客户端需要使用特定的机制来处理namenode的失效问题,这一机制对用户是透明的。
辅助namenode的角色被备用namenode所包含,备用namenode为活动的namenode命名空间设置周期性检查点。
可以从两种高可用性共享存储做出选择:NFS过滤器或群体日志管理器(QJM,quorum journal manager)。QJM是一个专用的HDFS实现,为提供一个高可用的编辑日志而设计,被推荐用于大多数HDFS部署中。QJM以一组日志节点(journal node)的形式运行,每一次编辑必须写入多数日志节点。典型的,有三个journal节点,所以系统能够忍受其中任何一个的丢失。这种安排与ZooKeeper的工作方式类似,当然必须认识到,QJM的实现并没使用ZooKeeper。(然而,值得注意的是,HDFS HA在选取活动的namenode时确实使用了ZooKeeper技术,详情参见下一章。
在活动namenode失效之后,备用namenode能够快速(几十秒的时间)实现任务接管,因为近期的状态存储在内存中:包括近期的编辑日志条目和近期的数据块映射信息。实际观察到的失效时间略长一点(需要1分钟左右),这是因为系统需要保守确定活动namenode是否真的失效了。
在活动namenode失效且备用namenode也失效的情况下,当然这类情况发生的概率非常低,管理员依旧可以声明一个备用namenode并实现冷启动。这类情况并不会比非高可用(non-HA)的情况更差,并且从操作的角度讲这是一个进步,因为上述处理已是一个标准的处理过程并植入Hadoop中。
故障切换与规避
系统中有一个称为故障转移控制器(failover controller)的新实体,管理着将活动namenode转移为备用namenode的转换过程。有多种故障转移控制器,但默认的一种是使用了ZooKeeper来确保有且仅有一个活动namenode。每一个namenode运行着一个轻量级的故障转移控制器,其工作就是监视宿主namenode是否失效(通过一个简单的心跳机制实现)并在namenode失效时进行故障切换。
管理员也可以手动发起故障转移,例如在进行日常维护时。这称为“平稳的故障转移”(graceful failover),因为故障转移控制器可以组织两个namenode有序地切换角色。
但在非平稳故障转移的情况下,无法确切知道失效namenode是否已经停止运行。例如,在网速非常慢或者网络被分割的情况下,同样也可能激发故障转移,但是先前的活动namenode依然运行着并且依旧是活动namenode。高可用实现做了更进一步的优化,以确保先前活动的namenode不会执行危害系统并导致系统崩溃的操作,该方法称为“规避”(fencing)。
同一时间QJM仅允许一个namenode向编辑日志中写入数据。然而,对于先前的活动namenode而言,仍有可能响应并处理客户过时的读请求,因此,设置一个SSH规避命令用于杀死namenode的进程是一个好主意。当使用NFS过滤器实现共享编辑日志时,由于不可能同一时间只允许一个namenode写入数据(这也是为什么推荐QJM的原因),因此需要更有力的规避方法。规避机制包括:撤销namenode访问共享存储目录的权限(通常使用供应商指定的NFS命令)、通过远程管理命令屏蔽相应的网络端口。诉诸的手段是,先前活动namenode可以通过一个相当形象的称为“一枪爆头”STONITH,shoot the other node in the head)的技术进行规避,该方法主要通过一个特定的供电单元对相应主机进行断电操作。
客户端的故障转移通过客户端类库实现透明处理。最简单的实现是通过客户端的配置文件实现故障转移的控制。HDFS URI使用一个逻辑主机名,该主机名映射到一对namenode地址(在配置文件中设置),客户端类库会访问每一个namenode地址直至处理完成。
3.3 命令行接口
现在我们通过命令行交互来进一步认识HDFS。HDFS还有很多其他接口,但命令行是最简单的,同时也是许多开发者最熟悉的。
参照附录A中伪分布模式下设置Hadoop的说明,我们先在一台机器上运行HDFS。稍后介绍如何在集群上运行HDFS,以提供可扩展性与容错性。
在我们设置伪分布配置时,有两个属性项需要进一步解释。及时项是fs.defaultFS,设置为hdfs://localhost/,用于设置Hadoop的默认文件系统。⑤文件系统是由URI指定的,这里我们已使用hdfs URI来配置HDFS为Hadoop的默认文件系统。HDFS的守护程序通过该属性项来确定HDFS namenode的主机及端口。我们将在localhost默认的HDFS端口8020上运行namenode。这样一来,HDFS客户端可以通过该属性得知namenode在哪里运行进而连接到它。
第二个属性dfs.replication,我们设为1,这样一来,HDFS就不会按默认设置将文件系统块复本设为3。在单独一个datanode上运行时,HDFS无法将块复制到3个datanode上,所以会持续给出块复本不足的警告。设置这个属性之后,上述问题就不会再出现了。
文件系统的基本操作
至此,文件系统已经可以使用了,我们可以执行所有常用的文件系统操作,例如,读取文件,新建目录,移动文件,删除数据,列出目录,等等。可以输入hadoop fs -help命令获取每个命令的详细帮助文件。
首先从本地文件系统将一个文件复制到HDFS:
% hadoop fs -copyFromLocal input/docs/quangle.txt \ hdfs://localhost/user/tom/quangle.txt
该命令调用Hadoop文件系统的shell命令fs,后者提供了一系列子命令,在这个例子中,我们执行的是-copyFromLocal。本地文件quangle.txt被复制到运行在localhost上的 HDFS实例中,路径为/user/tom/quangle.txt。事实上,我们可以简化命令格式以省略主机的URI并使用默认设置,即省略hdfs://localhost,因为该项已在core-site.xml中指定。
% hadoop fs -copyFromLocal input/docs/quangle.txt /user/tom/quangle.txt
我们也可以使用相对路径,并将文件复制到HDFS的home目录中,本例中为/user/tom:
% hadoop fs -copyFromLocal input/docs/quangle.txt quangle.txt
我们把文件复制回本地文件系统,并检查是否一致:
% hadoop fs -copyToLocal quangle.txt quangle.copy.txt
% md5 input/docs/quangle.txt quangle.copy.txt
MD5 (input/docs/quangle.txt) = e7891a2627cf263a079fb0f18256ffb2
MD5 (quangle.copy.txt) = e7891a2627cf263a079fb0f18256ffb2
MD5键值相同,表明这个文件在HDFS之旅中得以幸存并保存完整。
,看一下HDFS文件列表。我们新建一个目录,看它在列表中怎么显示:
% hadoop fs -mkdir books
% hadoop fs -ls .
Found 2 items
drwxr-xr-x - tom supergroup 0 2014-10-04 13:22 books
-rw-r--r-- 1 tom supergroup 119 2014-10-04 13:21 quangle.txt
返回的结果信息与Unix命令ls -l的输出结果非常相似,仅有细微差别。第1列显示的是文件模式。第2列是这个文件的备份数(这在传统Unix文件系统是没有的)。由于我们在整个文件系统范围内设置的默认复本数为1,所以这里显示的也都是1。这一列的开头目录为空,因为本例中没有使用复本的概念,目录作为元数据保存在namenode中,而非datanode中。第3列和第4列显示文件的所属用户和组别。第5列是文件的大小,以字节为单位,目录为0。第6列和第7列是文件的修改日期与时间。,第8列是文件或目录的名称。
H
较好的大数据书籍,值得阅读。
大数据必备
一直想买 终于买下来了
还没有看,不过纸张不错
目前在学基础概念部分
很不错的学习资料
呃!刚收到还没看!
不错,都是经典
内容不错,但有些代码使用的接口有点过时。
不错不错不错不错
书本很满意
差评,写了发票,没有给发票
个………………
书本还行,价格稍贵
初次接触分布式,前辈推荐这个,就买了。应该不错。当当物流快,赶上双11还半价,真心不错。
很好很好很好
很不错的书,推荐购买。
书绝对是好书,但是这快递,我简直不想多吐槽了,好好的书,被这么对待!
很好,不错的
刚拿到书本,感觉纸质一般吧,内容还没看,过后有机会会补充!
内容挺多的,要花时间去充电了
非常好,相当不错哦
到货速度快,赞赞赞赞赞赞赞赞赞赞赞赞
所以说没有自营配送的自营=耍流氓。100块钱买的书给中通砸核桃用了。
大数据从业者必备,从第二版到第四版,第二版最好,第三版错误较多,第四版翻译偏差。买了第二天某东就比当当便宜了10块
书不错,快递太差劲,糟糕的购物体验。
纸质、印刷、物流等等依然很不错,之所以打一分是因为这本书才看了二十来页就出现了多出错字漏字现象,即使不影响理解,依然十分失望。不知道是书本身翻译校错的问题,还是买的盗版书...非常不愉快的一次购物经历...
还没看 给单位买的 以后慢慢看。
很不错,学习一下,要这么多字数吗,有啥用
不错很好的
非常不错,值得信赖。
数据算法还不错,做工精良,就是书的背面有中等程度的褶皱。希望在包装上多多下功夫
物流不错,物美价廉
很厚,应该比较男肯。
如图,我还能说啥。要是当当以后都用邮政,就不会再买了。
书不错正在看
hao~~
从下单到收到书本速度快,快递员服务好。
很好。。。
经典之作,学习hadoop的好书
收到了,正版,完好无损,物流很快
书不错就是物流太慢了
我就吐槽一下包装吧,收到的时候书已经破了一角,虽然不影响使用,但是就是有点不爽,懒得退了
很好收获很快
书还不错。
做活动买的,很便宜,是正版书,就是包装很差,书角都磕破了
很好的一本书
内容很不错,但纸质差了点,单看书的质量,不值这个价。。。
快递员就一傻逼,妈的货到了之后,就扔到蜂巢柜里,他们也不给说一声,没有任何通知形式,短信,电话都没通知,之后我打电话问他,他才说放到柜子里了,而且他妈的态度也不好,我要不问,是不是他妈的我就一直等着。