技术,互联网,eLearning …

搜索的用户界面

Search User Interfaces

从Greg博客里的书评知道了这本书:Search User Interfaces

这本书的官网上提供了免费的HTML版,如果你想买一本的话,到这里

我还没有看这本书,不过从Greg列出的节录来看,本书对于搜索的界面设计做了深入的研究,并且回答了我们经常会提出 的一些问题,摘录几条:

为什么搜索的界面如此简单?

人们做搜索是要到达一个目的地,而搜索本身不是目的。当人们在寻找信息的时候,他们通常是沉浸在一些重要的工作里,而不希望被打断,阅读时被分散的精力越小,搜索的可用性越高。

关于细节:

一些小的细节的改变可能会产生很大的效果,比如,Franzen and Karlgren发现将表单的输入项变宽会使用户倾向于输入更多的内容。

另外一个例子是,Google早期的拼写纠正提示是放在搜索结果页面上方的。而用户更关注搜索结果,他们通常不会注意到这个提示,他们会一直向下拉页面找相关的结果,在搜索词拼错的情况下,用户很难找到他们想要搜索的结果,但并不会意识到自己输错词了,因为上方的拼写提示已经被忽略。于是用户会放弃搜索并抱怨。后来Google就改成了在页面下方也同样放上拼写纠正提示。

如何给用户提供信息?

提供一条带有用户搜索词的摘要信息是很重要的,它被称为keyword-in-context(KWIC)提取。通常提取摘要是为了概述文章的主要内容,这可能不包含用户输入的关键词,面向查询(query-oriented)的关键词提取则需要展示关键词是怎样在文档内被展示的。

高亮关键词有助于将用户的注意力吸引到文档中最和关键词相关的部分,并展示出关键词和文档中相应的词的语义是否相近。对过多的词做高亮显示也是不合适的,这会减少高亮所带来的好处。

用户在搜索过程中的行为

50%的搜索过程包含着重新构造查询词,拼写纠正的使用次数大约占其展示次数的35%。

10-15%的查询包含着拼写错误,用户更喜欢这些错误被自动纠正,而不是通过鼠标点击纠正词进行再次查询。

用户很少看第一页以外的搜索结果,如果他们在第一页无法找到想要的结果,用户通常会放弃或者更换查询词。用户期望他们想出的结果出现在前两条。

图书推荐:Even Faster Websites

Even Faster Website

High Performance Web Sites之后,Steve Souders在今年6月又推出了他的新作Even Faster Web Sites。前一本书完全是由Steve Souders一人所写,后一本书则Steve Souders与从多高手合作完成的。

High Performance Web Sites一书从网站配置方面阐述了各种网站性能调优的方法,其中所列出的各条原则对于开发者来说已经是耳熟能详。Even Faster Web Sites一书不仅提出了一些新的原则,而且深入分析了程序设计方面细节对于网站性能的影响。

Steve Souders的两本书给人的感觉就是压榨!压榨!从通常开发者所不关注的地方压榨出最后一滴性能,其结论多有出人意料之处,其分析问题的过程和方法颇值得学习。

列一下各章目录:

  • Chapter 1. Understanding Ajax Performance, Douglas Crockford.
  • Chapter 2. Creating Responsive Web Applications, Ben Galbraith and Dion Almaer.
  • Chapter 3. Splitting the Initial Payload.
  • Chapter 4. Loading Scripts Without Blocking.
  • Chapter 5. Coupling Asynchronous Scripts.
  • Chapter 6. Positioning Inline Scripts.
  • Chapter 7. Writing Efficient JavaScript, Nicholas C. Zakas.
  • Chapter 8. Scaling with Comet, Dylan Schiemann.
  • Chapter 9. Going Beyond Gzipping, Tony Gentilcore.
  • Chapter 10. Optimizing Images, Stoyan Stefanov and Nicole Sullivan.
  • Chapter 11. Sharding Dominant Domains.
  • Chapter 12. Flushing the Document Early.
  • Chapter 13. Using Iframes Sparingly.
  • Chapter 14. Simplifying CSS Selectors.
  • Appendix. Performance Tools.

一组关于社会化媒体的数据

数据来自Tony Karrer的博客。比较有意思,顺手翻译了一下。英文原文在这里(需翻墙)。

这些是美国的数据,国内应该有很大的不同。

  • 到2010年前,Y一代(Gen Y)的人数会超过战后“婴儿潮”出生的人数,96%的Y一代会加入社会化网络
  • 社会化媒体已经超过色情网络,成为网络第一活动
  • 去年美国新婚夫妇,每8对里有1对是通过社会化媒体相识的
  • 2009年美国教育部的一项研究表明,在线学习的学生的成绩要好于通过面对面授课学习的学生
  • 每6个高校学生里就有一个参与网络课程
  • 80%的twitter更新发生在手机上
  • Y一代和Z一代(Generation Y and Z)认为email已经过时了。。。2009年波士顿大学已经不在给新生分配email地址
  • 对世界前20的品牌进行搜索,其中25%的搜索结果会链接到用户生成的内容上
  • 34%的博客写手写产品和品牌相关的内容
  • 25%的美国人说他在上一个月通过手机看过一部视频短片
  • 据Jeff Bezos说,亚玛逊销售的书籍里有35%用于kindle阅读
  • 25家最大的报纸里有24家的订阅量在下降,因为我们不再找新闻,新闻会主动找到我们

Hadoop the definite guide 读书笔记(五)

MapReduce的类型和格式

MapReduce类型

hadoop里的MapReduce有如下通用的格式:

map: (K1, V1) → list(K2, V2)
reduce: (K2, list(V2)) → list(K3, V3)

通常情况下,输入的key和value的类型(K1和V1)和输出的类型(K2和V2)是不同的,当然,Reduce的输入和Map的输出类型必须是一样的,虽然Reduce的输出可能是另外一种类型(K3和V3)。Java接口定义:

public interface Mapper<K1, V1, K2, V2> extends JobConfigurable, Closeable {
    void map(K1 key, V1 value, OutputCollector<K2, V2> output, Reporter reporter)
    throws IOException;
}

public interface Reducer<K2, V2, K3, V3> extends JobConfigurable, Closeable {
    void reduce(K2 key, Iterator<V2> values,
    OutputCollector<K3, V3> output, Reporter reporter) throws IOException;
}

如果使用combine函数的话,它同reduce函数的形式是一样的(同时也实现Reducer接口),不同之处在于输出的类型是一些中间的key和value:

map: (K1, V1) → list(K2, V2)
combine: (K2, list(V2)) → list(K2, V2)
reduce: (K2, list(V2)) → list(K3, V3)

partitation函数处理中间的key和value的类型,并且返回其partation index:

 public interface Partitioner<K2, V2> extends JobConfigurable {
   int getPartition(K2 key, V2 value, int numPartitions);
 }

Input Splits 和 Records

Input会被分成input split,split由record 组成。map处理每一个record,并且返回key和value的对。

InputSplit由InputSplit接口来定义:

public interface InputSplit extends Writable {
    long getLength() throws IOException;
    String[] getLocations() throws IOException;
}

MapReduce程序作者并不需要直接处理InputSplit,因为它是由InputFormat创建的:

public interface InputFormat<K, V> {
    InputSplit[] getSplits(JobConf job, int numSplits) throws IOException;
    RecordReader<K, V> getRecordReader(InputSplit split,
    JobConf job,
    Reporter reporter) throws IOException;
}

InputFormat类结构图:

InputFormat类结构图

OutputFormat

OutputFormat和InputFormat的类型相对应

OutputFormat

Hadoop the definite guide 读书笔记(四)

MapReduce是如何工作的

MapReduce的整个工作过程如下图所示:

Hadoop的工作过程

整个模型的最上层有四个实体:

  • 客户端,负责提交MapReduce Job
  • jobtracker, 负责调节job运行,jobtracker是一个java应用程序,main class名为JobTracker
  • tasktrackers,job被分成多个split,tasktracker负责运行split,tasktracker是一个java应用程序,main class名为TaskTracker
  • HDFS,用于共享job所需的文件

Job提交

JobClient调用submitJob方法提交一个job,一旦Job被提交,runJob方法会每隔一秒检查一下job的进度,如果进度有变化则显示在控制台。job完成后,如果job成功,在控制台显示job计数器,失败则在控制台显示错误原因。

JobClient的submitJob方法会完成以下工作:

  • 从jobtracker那里拿到一个新job ID(通过调用JobTracker的getNewJobId()方法)
  • 检查job的输出目录是否正常,比如,如果没有指定输出目录或者输出目录已经存在,则不提交job并抛出异常
  • 计算job的input split,如果input split不能被计算出来,则抛出异常
  • 拷贝job运行所需要的必要资源到jobtracker的文件系统里以job ID命名的目录下,这些资源包括jar文件,配置文件和input splits
  • 告诉jobtracker可以运行job了

Job初始化

jobtracker接到submitJob的调用后,会将其放入一个内部的队列中,job scheduler会从队列里将job取出并初始化。

为了创建一个可以运行的task的队列,job scheduler首先从共享的文件系统中将input split取出来,并为每一个split创建一个map task,Reduce task的数据由mapred.reduce.tasks决定

Task分配

tasktracker定期向jobtracker发送heartbeat信息,做为heartbeat的一部分,tasktracker会表明其是否能可以接受新任务,如果可以,jobtracker通过heartbeat的返回值为其分配一个新的任务。

在为tasktracker分配task之前,jobtracker必须先选择一个job,有多种调度算法,简单的是保存一个job的优先级队列。一旦选择了一个job,jobtracker就选择一个task。

Task 执行

现在tasktracker已经被分配了一个task,下一步就是运行这个task。首先,从共享文件系统里拷贝job的jar文件至本地,其次,为task创建一个本地工作目录,把jar包解压至该目录下,最后,创建一个TaskRunner来运行这个task。

TaskRunner启动一个新的java虚拟机来运行每一个task。当然,也可以在不同的task之间共享JVM

进度和状态更新

MapReduce job是长期运行的批处理作业,因为运行时间很长,用户需要了解进度信息。Job以及其所属的task都有status信息,在整个job的运行过程中,status信息都会发生变化。

task运行时会记录其进度信息,并定期通过heartbeat向tasktracker报告。

Job完成

当jobtracker接收到信息,最后一个job已经完成,它会将job的状态设为”success”,这样,当JobClient查询job的状态时,它知道job已经成功完成,并通知用户,同时从runJob()方法中退出。

返回顶部