#
WireframeSketcher是一个Eclipse 插件,用于创建线框图,界面模型和UI原型。
项目正式开发前创建原型可以帮助用户和开发者理解系统,使用WireframeSketcher在Eclipse中创建能够更好的集成进入你的项目开发流程。
WireframeSketcher 如何工作?它提供了一个pre-drawn,text-driven 预制图,文本驱动的widgets,能够展现通用UI界面,你可以拖拽他们进入编辑器迅速画出你的界面。界面用XML存储。
- HBASE的SHELL命令使用
- HBASE的JAVA CLIENT的使用
新增和修改记录用PUT。
PUT的执行流程:
首先会在内存中增加MEMSTORE,如果这个表有N个COLOUMN FAMILY,则会产生N个MEMSTORE,记录中的值属于不同的COLOUMN FAMILY的,会保存到不同的MEMSTORE中。MEMSTORE中的值不会马上FLUSH到文件中,而是到MEMSTORE满的时候再FLUSH,且FLUSH的时候不会写入已存在的HFILE中,而是新增一个HFILE去保存。另外会写WRITE AHEAD LOG,这是由于新增记录时不是马上写入HFILE的,如果中途出现DOWN机时,则HBASE重启时会根据这个LOG来恢复数据。
删除记录用DELETE。
删除时并不会将在HFILE中的内容删除,而是作一标记,然后在查询的时候可以不取这些记录。
读取单条记录用GET。
读取的时候会将记录保存到CAHE中,同样如果这个表有N个COLOUMN FAMILY,则会产生N个CAHE
,记录中的值属于不同的COLOUMN FAMILY的,会保存到不同的CAHE中。这样下次客户端再取记录时会综合CAHE和MEMSTORE来返回数据。
新增表用HADMIN。
查询多条记录用SCAN和FILTER。
- HBASE的分布式计算
为什么会有分布式计算
前面的API是针对ONLINE的应用,即要求低延时的,相当于OLTP。而针对大量数据时这些API就不适用了。
如要针对全表数据进行分析时用SCAN,这样会将全表数据取回本地,如果数据量在100G时会耗几个小时,为了节省时间,引入多线程做法,但要引入多线程时,需遵从新算法:将全表数据分成N个段,每段用一个线程处理,处理完后,交结果合成,然后进行分析。
如果数据量在200G或以上时间就加倍了,多线程的方式不能满足了,因此引入多进程方式,即将计算放在不同的物理机上处理,这时就要考虑每个物理机DOWN机时的处理方式等情况了,HADOOP的MAPREDUCE则是这种分布式计算的框架了,对于应用者而言,只须处理分散和聚合的算法,其他的无须考虑。
HBASE的MAPREDUCE
使用TABLEMAP和TABLEREDUCE。
HBASE的部署架构和组成的组件
架构在HADOOP和ZOOPKEEPER之上。
HBASE的查询记录和保存记录的流程
说见前一编博文。
HBASE作为数据来源地、保存地和共享数据源的处理方式
即相当于数据库中JOIN的算法:REDUCE SIDE JOIN、MAP SIDE JOIN。
@import url(http://www.blogjava.net/CuteSoft_Client/CuteEditor/Load.ashx?type=style&file=SyntaxHighlighter.css);@import url(/css/cuteeditor.css);
Hadoop/Hbase是开源版的google Bigtable, GFS, MapReduce的实现,随着互联网的发展,大数据的处理显得越发重要,Hadoop/Hbase的用武之地也越发广泛。为了更好的使用Hadoop/Hbase系统,需要有一套完善的监控系统,来了解系统运行的实时状态,做到一切尽在掌握。Hadoop/Hbase有自己非常完善的metrics framework, 里面包种各种维度的系统指标的统计,另外,这套metrics framework设计的也非常不错,用户可以很方便地添加自定义的metrics。更为重要的一点是metrics的展示方式,目前它支持三种方式:一种是落地到本地文件,一种是report给Ganglia系统,另一种是通过JMX来展示。本文主要介绍怎么把Hadoop/Hbase的metrics report给Ganglia系统,通过浏览器来查看。
介绍后面的内容之前有必要先简单介绍一下Ganglia系统。Ganglia是一个开源的用于系统监控的系统,它由三部分组成:gmond, gmetad, webfrontend, 三部分是这样分工的:
gmond: 是一个守护进程,运行在每一个需要监测的节点上,收集监测统计,发送和接受在同一个组播或单播通道上的统计信息
gmetad: 是一个守护进程,定期检查gmond,从那里拉取数据,并将他们的指标存储在RRD存储引擎中
webfrontend: 安装在有gmetad运行的机器上,以便读取RRD文件,用来做前台展示
简单总结它们三者的各自的功用,gmond收集数据各个node上的metrics数据,gmetad汇总gmond收集到的数据,webfrontend在前台展示gmetad汇总的数据。Ganglia缺省是对系统的一些metric进行监控,比如cpu/memory/net等。不过Hadoop/Hbase内部做了对Ganglia的支持,只需要简单的改配置就可以将Hadoop/Hbase的metrics也接入到ganglia系统中进行监控。
接下来介绍如何把Hadoop/Hbase接入到Ganglia系统,这里的Hadoop/Hbase的版本号是0.94.2,早期的版本可能会有一些不同,请注意区别。Hbase本来是Hadoop下面的子项目,因此所用的metrics framework原本是同一套Hadoop metrics,但后面hadoop有了改进版本的metrics framework:metrics2(metrics version 2), Hadoop下面的项目都已经开始使用metrics2, 而Hbase成了Apache的顶级子项目,和Hadoop成为平行的项目后,目前还没跟进metrics2,它用的还是原始的metrics.因此这里需要把Hadoop和Hbase的metrics分开介绍。
Hadoop接入Ganglia:
1. Hadoop metrics2对应的配置文件为:hadoop-metrics2.properties
2. hadoop metrics2中引用了source和sink的概念,source是用来收集数据的, sink是用来把source收集的数据consume的(包括落地文件,上报ganglia,JMX等)
3. hadoop metrics2配置支持Ganglia:
#*.sink.ganglia.class=org.apache.hadoop.metrics2.sink.ganglia.GangliaSink30
*.sink.ganglia.class=org.apache.hadoop.metrics2.sink.ganglia.GangliaSink31
*.sink.ganglia.period=10
*.sink.ganglia.supportsparse=true
*.sink.ganglia.slope=jvm.metrics.gcCount=zero,jvm.metrics.memHeapUsedM=both
*.sink.ganglia.dmax=jvm.metrics.threadsBlocked=70,jvm.metrics.memHeapUsedM=40
#uncomment as your needs
namenode.sink.ganglia.servers=10.235.6.156:8649
#datanode.sink.ganglia.servers=10.235.6.156:8649
#jobtracker.sink.ganglia.servers=10.0.3.99:8649
#tasktracker.sink.ganglia.servers=10.0.3.99:8649
#maptask.sink.ganglia.servers=10.0.3.99:8649
#reducetask.sink.ganglia.servers=10.0.3.99:8649
这里需要注意的几点:
(1) 因为Ganglia3.1与3.0不兼容,需要根据Ganglia的版本选择使用GangliaSink30或者GangliaSink31
(2) period配置上报周期,单位是秒(s)
(3) namenode.sink.ganglia.servers指定Ganglia gmetad所在的host:port,用来向其上报数据
(4) 如果同一个物理机器上同时启动了多个hadoop进程(namenode/datanode, etc),根据需要把相应的进程的sink.ganglia.servers配置好即可
Hbase接入Ganglia:
1. Hbase所用的hadoop metrics对应的配置文件是: hadoop-metrics.properties
2. hadoop metrics里核心是Context,写文件有写文件的TimeStampingFileContext, 向Ganglia上报有GangliaContext/GangliaContext31
3. hadoop metrics配置支持Ganglia:
# Configuration of the "hbase" context for ganglia
# Pick one: Ganglia 3.0 (former) or Ganglia 3.1 (latter)
# hbase.class=org.apache.hadoop.metrics.ganglia.GangliaContext
hbase.class=org.apache.hadoop.metrics.ganglia.GangliaContext31
hbase.period=10
hbase.servers=10.235.6.156:8649
这里需要注意几点:
(1) 因为Ganglia3.1和3.0不兼容,所以如果是3.1以前的版本,需要用GangliaContext, 如果是3.1版的Ganglia,需要用GangliaContext31
(2) period的单位是秒(s),通过period可以配置向Ganglia上报数据的周期
(3) servers指定的是Ganglia gmetad所在的host:port,把数据上报到指定的gmetad
(4) 对rpc和jvm相关的指标都可以进行类似的配置
REGIONS SERVER和TASK TRACKER SERVER不要在同一台机器上,最好如果有MAPREDUCE JOB运行的话,应该分开两个CLUSTER,即两群不同的服务器上,这样MAPREDUCE 的线下负载不会影响到SCANER这些线上负载。
如果主要是做MAPREDUCE JOB的话,将REGIONS SERVER和TASK TRACKER SERVER放在一起是可以的。
原始集群模式
10个或以下节点,无MAPREDUCE JOB,主要用于低延迟的访问。每个节点上的配置为:CPU4-6CORE,内存24-32G,4个SATA硬盘。Hadoop NameNode, JobTracker, HBase Master, 和ZooKeeper全都在同一个NODE上。
小型集群模式(10-20台服务器)
HBase Master放在单独一台机器上, 以便于使用较低配置的机器。ZooKeeper也放在单独一台机器上,NameNode和JobTracker放在同一台机器上。
中型集群模式(20-50台服务器)
由于无须再节省费用,可以将HBase Master和ZooKeeper放在同一台机器上, ZooKeeper和HBase Master要三个实例。NameNode和JobTracker放在同一台机器上。
大型集群模式(>50台服务器)
和中型集群模式相似,但ZooKeeper和HBase Master要五个实例。NameNode和Second NameNode要有足够大的内存。
HADOOP MASTER节点
NameNode和Second NameNode服务器配置要求:(小型)8CORE CPU,16G内存,1G网卡和SATA 硬盘,中弄再增加多16G内存,大型则再增加多32G内存。
HBASE MASTER节点
服务器配置要求:4CORE CPU,8-16G内存,1G网卡和2个SATA 硬盘,一个用于操作系统,另一个用于HBASE MASTER LOGS。
HADOOP DATA NODES和HBASE REGION SERVER节点
DATA NODE和REGION SERVER应在同一台服务器上,且不应该和TASK TRACKER在一起。服务器配置要求:8-12CORE CPU,24-32G内存,1G网卡和12*1TB SATA 硬盘,一个用于操作系统,另一个用于HBASE MASTER LOGS。
ZOOPKEEPERS节点
服务器配置和HBASE MASTER相似,也可以与HBASE MASTER放在一起,但就要多增加一个硬盘单独给ZOOPKEEPER使用。
-Xmx8g—设置HEAP的最大值到8G,不建议设到15 GB.
-Xms8g—设置HEAP的最小值到8GS.
-Xmn128m—设置新生代的值到128 MB,默认值太小。
-XX:+UseParNewGC—设置对于新生代的垃圾回收器类型,这种类型是会停止JAVA进程,然后再进行回收的,但由于新生代体积比较小,持续时间通常只有几毫秒,因此可以接受。
-XX:+UseConcMarkSweepGC—设置老生代的垃圾回收类型,如果用新生代的那个会不合适,即会导致JAVA进程停止的时间太长,用这种不会停止JAVA进程,而是在JAVA进程运行的同时,并行的进行回收。
-XX:CMSInitiatingOccupancyFraction—设置CMS回收器运行的频率。
GET、PUT是ONLINE的操作,MAPREDUCE是OFFLINE的操作
HDFS写流程
客户端收到要保存文件的请求后,将文件以64M为单位拆成若干份BLOCK,形成一个列表,即由几个BLOCK组成,将这些信息告诉NAME NODE,我要保存这个,NAME NODE算出一个列表,哪段BLOCK应该写到哪个DATA NODE,客户端将第一个BLOCK传到第一个节点DATA NODE A,通知其保存,同时让它通知DATA NODE D和DATA NODE B也保存一份,DATA NODE D收到信息后进行了保存,同时通知DATA NODE B保存一份,DATA NODE B保存完成后则通知客户端保存完成,客户端再去向NAME NODE中取下一个BLOCK要保存的位置,重复以上的动作,直到所有的BLOCK都保存完成。
HDFS读流程
客户端向NAME NODE请求读一个文件,NAME NODE返回这个文件所构成的所有BLOCK的DATA NODE IP及BLOCK ID,客户端并行的向各DATA NODE发出请求,要取某个BLOCK ID的BLOCK,DATA NODE发回所要的BLOCK给客户端,客户端收集到所有的BLOCK后,整合成一个完整的文件后,此流程结束。
MAPREDUCE流程
输入数据 -- 非多线程了,而是多进程的挑选数据,即将输入数据分成多块,每个进程处理一块 -- 分组 -- 多进程的汇集数据 -- 输出
HBASE表结构
HBASE中将一个大表数据分成不同的小表,每个小表叫REGION,存放REGION的服务器叫REGIONSERVER,一个REGIONSERVER可以存放多个REGION。通常REGIONSERVER和DATA NODE是在同一服务器,以减少NETWORK IO。
-ROOT-表存放于MASTER SERVER上,记录了一共有多少个REGIONSERVER,每个REGION SERVER上都有一个.META.表,上面记录了本REGION SERVER放有哪几个表的哪几个REGION。如果要知道某个表共有几个REGION,就得去所有的REGION SERVER上查.META.表,进行汇总才能得知。
客户端如果要查ROW009的信息,先去咨询ZOOPKEEPER,-ROOT-表在哪里,然后问-ROOT-表,哪个.META.知道这个信息,然后去问.META.表,哪个REGION有这个信息,然后去那个REGION问ROW009的信息,然后那个REGION返回此信息。
HBASE MAPREDUCE
一个REGION一个MAP任务,而任务里的map方法执行多少次,则由查询出来的记录有多少条,则执行多少次。
REDUCE任务负责向REGION写数据,但写到哪个REGION则由那个KEY归属哪个REGION管,则写到哪个REGION,有可能REDUCE任务会和所有的REGION SERVER交互。
在HBASE的MAPREDUCE JOB中使用JOIN
REDUCE-SIDE JOIN
利用现有的SHUTTLE分组机制,在REDUCE阶段做JOIN,但由于MAP阶段数据大,可能会有性能问题。
MAP-SIDE JOIN
将数据较少的一表读到一公共文件中,然后在MPA方法中循环另一表的数据,再将要的数据从公共文件中读取。这样可以减少SHUTTLE和SORT的时间,同时也不需要REDUCE任务。
1) 在Reduce阶段进行Join,这样运算量比较小.(这个适合被Join的数据比较小的情况下.)
2) 压缩字段,对数据预处理,过滤不需要的字段.
3) 最后一步就是在Mapper阶段过滤,这个就是Bloom Filter的用武之地了.也就是需要详细说明的地方.
下面就拿一个我们大家都熟悉的场景来说明这个问题: 找出上个月动感地带的客户资费的使用情况,包括接入和拨出.
(这个只是我臆想出来的例子,根据实际的DB数据存储结构,在这个场景下肯定有更好的解决方案,大家不要太较真哦)
这个时候的两个个数据集都是比较大的,这两个数据集分别是:上个月的通话记录,动感地带的手机号码列表.
比较直接的处理方法有2种:
1)在 Reduce 阶段,通过动感地带号码来过滤. 优点:这样需要处理的数据相对比较少,这个也是比较常用的方法.
缺点:很多数据在Mapper阶段花了老鼻子力气汇总了,还通过网络Shuffle到Reduce节点,结果到这个阶段给过滤了.
2)在 Mapper 阶段时,通过动感地带号码来过滤数据. 优点:这样可以过滤很多不是动感地带的数据,比如神州行,全球通.这些过滤的数据就可以节省很多网络带宽了.
缺点:就是动感地带的号码不是小数目,如果这样处理就需要把这个大块头复制到所有的Mapper节点,甚至是Distributed Cache.(Bloom Filter就是用来解决这个问题的)
Bloom Filter就是用来解决上面方法2的缺点的.
方法2的缺点就是大量的数据需要在多个节点复制.Bloom Filter通过多个Hash算法, 把这个号码列表压缩到了一个Bitmap里面. 通过允许一定的错误率来换空间, 这个和我们平时经常提到的时间和空间的互换类似.详细情况可以参考:
http://blog.csdn.net/jiaomeng/article/details/1495500
但是这个算法也是有缺陷的,就是会把很多神州行,全球通之类的号码当成动感地带.但在这个场景中,这根本不是问题.因为这个算法只是过滤一些号码,漏网之鱼会在Reduce阶段进行精确匹配时顾虑掉.
这个方法改进之后基本上完全回避了方法2的缺点:
1) 没有大量的动感地带号码发送到所有的Mapper节点.
2) 很多非动感地带号码在Mapper阶段就过滤了(虽然不是100%),避免了网络带宽的开销及延时.
继续需要学习的地方:Bitmap的大小, Hash函数的多少, 以及存储的数据的多少. 这3个变量如何取值才能才能在存储空间与错误率之间取得一个平衡.
NAME NODE起保存DATA NODE上文件的位置信息用,主要有两个保存文件:FsImage和EditLog,FsImage保存了上一次NAME NODE启动时的状态,EditLog则记录每次成功后的对HDFS的操作行为。当NAME NODE重启时,会合并FsImage和EditLog成为一个新的FsImage,清空EditLog,如果EditLog非常大的时候,则NAME NODE启动的时间会非常长。因此就有SECOND NAME NODE。
SECOND NAME NODE会以HTTP的方式向NAME NODE要这两个文件,当NAME NODE收到请求时,就会韦一个新的EditLog来记录,这时SECOND NAME NODE就会将取得的这两个文件合并,成一个新的FsImage,再发给NAME NODE,NAME NODE收到后,就会以这个为准,旧的就会归档不用。
SECOND NAME NODE还有一个用途就是当NAME NODE DOWN了的时候,可以改SECOND NAME NODE的IP为NAME NODE所用的IP,当NAME NODE用。
secondary namenoded 配置很容易被忽视,如果jps检查都正常,大家通常不会太关心,除非namenode发生问题的时候,才会想起还有个secondary namenode,它的配置共两步:
- 集群配置文件conf/master中添加secondarynamenode的机器
- 修改/添加 hdfs-site.xml中如下属性:
<property>
<name>dfs.http.address</name>
<value>{your_namenode_ip}:50070</value>
<description>
The address and the base port where the dfs namenode web ui will listen on.
If the port is 0 then the server will start on a free port.
</description>
</property>
这两项配置OK后,启动集群。进入secondary namenode 机器,检查fs.checkpoint.dir(core-site.xml文件,默认为${hadoop.tmp.dir}/dfs/namesecondary)目录同步状态是否和namenode一致的。
如果不配置第二项则,secondary namenode同步文件夹永远为空,这时查看secondary namenode的log显示错误为:
2011-06-09 11:06:41,430 INFO org.apache.hadoop.hdfs.server.common.Storage: Recovering storage directory /tmp/hadoop-hadoop/dfs/namesecondary from failed checkpoint.
2011-06-09 11:06:41,433 ERROR org.apache.hadoop.hdfs.server.namenode.SecondaryNameNode: Exception in doCheckpoint:
2011-06-09 11:06:41,434 ERROR org.apache.hadoop.hdfs.server.namenode.SecondaryNameNode: java.net.ConnectException: Connection refused
at java.net.PlainSocketImpl.socketConnect(Native Method)
at java.net.PlainSocketImpl.doConnect(PlainSocketImpl.java:351)
at java.net.PlainSocketImpl.connectToAddress(PlainSocketImpl.java:211)
at java.net.PlainSocketImpl.connect(PlainSocketImpl.java:200)
at java.net.SocksSocketImpl.connect(SocksSocketImpl.java:366)
at java.net.Socket.connect(Socket.java:529)
at java.net.Socket.connect(Socket.java:478)
at sun.net.NetworkClient.doConnect(NetworkClient.java:163)
at sun.net.www.http.HttpClient.openServer(HttpClient.java:394)
at sun.net.www.http.HttpClient.openServer(HttpClient.java:529)
at sun.net.www.http.HttpClient.<init>(HttpClient.java:233)
at sun.net.www.http.HttpClient.New(HttpClient.java:306)
at sun.net.www.http.HttpClient.New(HttpClient.java:323)
at sun.net.www.protocol.http.HttpURLConnection.getNewHttpClient(HttpURLConnection.java:970)
at sun.net.www.protocol.http.HttpURLConnection.plainConnect(HttpURLConnection.java:911)
at sun.net.www.protocol.http.HttpURLConnection.connect(HttpURLConnection.java:836)
at sun.net.www.protocol.http.HttpURLConnection.getInputStream(HttpURLConnection.java:1172)
at org.apache.hadoop.hdfs.server.namenode.TransferFsImage.getFileClient(TransferFsImage.java:151)
at org.apache.hadoop.hdfs.server.namenode.SecondaryNameNode.downloadCheckpointFiles(SecondaryNameNode.java:256)
at org.apache.hadoop.hdfs.server.namenode.SecondaryNameNode.doCheckpoint(SecondaryNameNode.java:313)
at org.apache.hadoop.hdfs.server.namenode.SecondaryNameNode.run(SecondaryNameNode.java:225)
at java.lang.Thread.run(Thread.java:662)
可能用到的core-site.xml文件相关属性:
<property>
<name>fs.checkpoint.period</name>
<value>300</value>
<description>The number of seconds between two periodic checkpoints.
</description>
</property>
<property>
<name>fs.checkpoint.dir</name>
<value>${hadoop.tmp.dir}/dfs/namesecondary</value>
<description>Determines where on the local filesystem the DFS secondary
name node should store the temporary images to merge.
If this is a comma-delimited list of directories then the image is
replicated in all of the directories for redundancy.
</description>
</property>
采用Cloudera版本的hadoop/hbase:
hadoop-0.20.2-cdh3u0
hbase-0.90.1-cdh3u0
zookeeper-3.3.3-cdh3u0
默认已支持FairScheduler调度算法.
只需改配置使期用FairSchedule而非默认的JobQueueTaskScheduler即可.
配置fair-scheduler.xml (/$HADOOP_HOME/conf/):
<?xml version="1.0"?>
<property>
<name>mapred.fairscheduler.allocation.file</name>
<value>[HADOOP_HOME]/conf/fair-scheduler.xml</value>
</property>
<allocations>
<pool name="qiji-task-pool">
<minMaps>5</minMaps>
<minReduces>5</minReduces>
<maxRunningJobs>
<maxRunningJobs>5</maxRunningJobs>
<minSharePreemptionTimeout>300</minSharePreemptionTimeout>
<weight>1.0</weight>
</pool>
<user name="ecap">
<maxRunningJobs>
<maxRunningJobs>6</maxRunningJobs>
</user>
<poolMaxJobsDefault>10</poolMaxJobsDefault>
<userMaxJobsDefault>8</userMaxJobsDefault>
<defaultMinSharePreemptionTimeout>600
</defaultMinSharePreemptionTimeout>
<fairSharePreemptionTimeout>600</fairSharePreemptionTimeout>
</allocations>
配置$HADOOP_HOME/conf/mapred-site.xml,最后添加:
<property>
<name>mapred.jobtracker.taskScheduler</name>
<value>org.apache.hadoop.mapred.FairScheduler</value>
</property>
<property>
<name>mapred.fairscheduler.allocation.file</name>
<value>/opt/hadoop/conf/fair-scheduler.xml</value>
</property>
<property>
<name>mapred.fairscheduler.assignmultiple</name>
<value>true</value>
</property>
<property>
<name>mapred.fairscheduler.sizebasedweight</name>
<value>true</value>
</property>
然后重新运行集群,这样有几个Job(上面配置是5个并行)并行运行时,不会因为一个Job把Map/Reduce占满而使其它Job处于Pending状态.
可从: http://<masterip>:50030/scheduler查看并行运行的状态.
挺有意思的题目。
1. 给你A,B两个文件,各存放50亿条URL,每条URL占用64字节,内存限制是4G,让你找出:A,B文件共同的URL。 解法一:Hash成内存大小的小块文件,然后分块内存内查交集。
解法二:Bloom Filter(广泛应用于URL过滤、查重。参考http://en.wikipedia.org/wiki/Bloom_filter、http://blog.csdn.net/jiaomeng/archive/2007/01/28/1496329.aspx)
2. 有10个文件,每个文件1G, 每个文件的每一行都存放的是用户的query,每个文件的query都可能重复。要你按照query的频度排序。解法一:根据数据稀疏程度算法会有不同,通用方法是用Hash把文件重排,让相同query一定会在同一个文件,同时进行计数,然后归并,用最小堆来统计频度最大的。
解法二:类似1,但是用的是与简单Bloom Filter稍有不同的CBF(Counting Bloom Filter)或者更进一步的SBF(Spectral Bloom Filter,参考http://blog.csdn.net/jiaomeng/archive/2007/03/19/1534238.aspx)
解法三:MapReduce,几分钟可以在hadoop集群上搞定。参考http://en.wikipedia.org/wiki/MapReduce
3. 有一个1G大小的一个文件,里面每一行是一个词,词的大小不超过16个字节,内存限制大小是1M。返回频数最高的100个词。解法一:跟2类似,只是不需要排序,各个文件分别统计前100,然后一起找前100。