2009年2月24日星期二

关于增量备份的小故事

所谓增量备份,顾名思义即是每次备份仅操作那些发生了"变化"的数据块。RMAN中增量备份有两种:Differential方式和Cumulative方式。

说起Differential,相当有意思,大家可以这样理解。有一家名为Differential的红社会组织,他们民主自由善良博为人忠恳正直(以下省略5000个褒义形容词),总之呢,黑黑,他们会按照你与其约定的周期来向你收取保护费,因为他们的组织非常严密,所有成员按照0,1,2分为不同等级,0级最高就是老大。贵为老大自然身份尊崇,手底下小弟多,开销也大,所以如果0级老大亲自登门收取的话,没啥说的,甭管它什么时候来,你的家底他都要重新清点一遍,从你成立开始到现在,总共应交多少保护费,一个子儿都不能少的都要交出来。每次来都是这样。而1级成员就显的温和多了,它每次来,只要求你将上次0级收到之后到现在应交的税款给交了就行了。甚至于如果上次也是个1级成员(与它平级)来收取的话,它也认同。当然,如果上次来收的是个2级成员,它是不承认的,歹它也是个有身份的人,比它低级的成员打的收条它向来是不认同地。它至少要求将最后一个与它平级或级别比它要高的成员收取日期到现在应结的给它。1级成员带了头,2级成员也按这个来。

名为Cumulative的红社会组织相比Differential差距就比较大,虽然它们也会按照与你约定的同期来收取,但是,这家组织显然作风是属于比较凶悍的。仍然以上图为例,假设它们也按照0,1,2分为三个等级,0级老大身份尊崇,表现倒与Differential家的相同。但级别比它低的那些小弟表现与Differential家的就相差较多。对于那些级别高于它们的成员打的收条,他们还是会认可,但是其它人,甚至与它们平级的成员它们都不认。哪怕上次就是它来收取的,他也能翻脸不认帐。比如某个1级成员昨天来时就直接从上次0级收取的时间开始算的,而今天来的又是这个家伙,可它对昨天的所为都拒不认帐,坚持还要从上次0级收取的时间开始算。

注意:这两家非0级成员都有个毛病,假如它们来收费时发现自你成立起,自家的0级老大从来都没来过,本着为老大尽心为老大尽责的高贵品格,他们都会替老大把你的家底翻个底朝天,来个大清算。

今天群里一个哥们发的,挺逗的。

2009年2月23日星期一

关于JOB的一点问题

今天有人在群里问:

有个JOB,定时执行,每5秒执行一次。现在如果有一次它执行时间超过5秒,还没执行完毕,会怎么样?

关于这个问题,我的解释是:
排队执行,等着。
当第n次执行到第5秒时,如果新加JOB,数据库会另起一个进程或者线程来执行第N+1个JOB,如果碰上N+2...则依次类推,一直到达到数据库参数里设定的JOB进程数的最大值。

设置初始化参数 job_queue_processes可以调整JOB进程数的最大值(最大只能为1000)。
sql> alter system set job_queue_processes=n;

由于ORACLE采取了这样的方式处理JOB,增加了并发能力。但是针对实际情况还要考虑到MVCC带来的问题。

2009年2月19日星期四

十大高明的Google搜索技巧

转自月光博客。

前言:多数人在使用Google搜索的过程是非常低效和无谓的,如果你只是输入几个关键词,然后按搜索按钮,你将是那些无法得到Google全部信息的用户,在这篇文章中,Google搜索专家迈克尔.米勒将向您展示如何更智能、更高效地进行Google的系列搜索。
Google是一个非常精密成熟的搜索引擎,但大多数的用户都没有能完全地利用到它的能力。一般人就是在Google的搜索框中输入一两个关键字,然后点击“搜索”按钮,等着Google显示出它第一页的搜索结果。这是一个相当简单模式匹配算法的搜索,不幸的是,通常此时出现的大部分都是并不需要的结果。

其实,还是有更好的方式能够让搜索产生一些更少、更为准确的结果。你所需要做的事只是学习一些简单的技巧,你就能很快得到更多更好的Google搜索结果。

技巧一:使用正确的方法

无论你是使用一个简单或是高级的Google搜索,在此都存在你应该使用的某种可靠的方法。遵循适当的方法你就能获得非常准确的结果;要是忽略这条建议的话,你也许就会看到大量不相关的结果或是缺乏你想要的相关结果。

虽然有很多不同(且同样有效的)方法用于网络搜索,我保证这个特别的方法将能带来最棒的结果。这是一个分六步骤的过程,如下:

1、首先,想好你想要寻找什么。哪些词能够最好地描述你要寻找的信息或者概念?哪些词是你能够用来替换的?有没有那些词是可以不必包括在你想要搜索的更好定义你的需求之内?

2、构建你的搜索要求。使用尽可能多你所需要的关键词;越多越好。如果皆存在可能的话,试着用适当的搜索操作来使你的搜索更精炼——或者,如果你愿意的话,可以使用高级搜索页面。

3、点击“搜索”按钮进行搜索。

4、评估一下搜索结果页面上的匹配程度。如果一开始的结果与你想要的不一致,再精炼你的搜索要求并重新搜索——或转向更合适的搜索站点再进行搜索。

5、选择你想要查看的匹配的页面,点击进行浏览。

6、保存这些最符合你需求的信息。

换言之,这需要你在搜索之前思考清楚,接着在获得最初结果后精炼你的搜索。这些多做的努力都是轻微的,但确实很值得。

技巧二:合理利用一个“与/或”的搜索

大多数的用户都没有意识到,Google会自动假定一次搜索要求中所有的词之间都是一种“和”的关系。也就是,如果你输入两个词,它就会假定你所寻找的页面是包含这两个词的。它不会反馈给你仅包含其中一个词的页面。

这就使得你无须在输入搜索要求时输入一个“和”。如果你想要搜索的包括“Bob”和“Ted”,你所需要做的就是输入bob ted即可。Google会自己假定一个“和”,并自动地将它包括在内部的索引搜索内。

这与在所要求的词之间假定“或”是不同的。例如,对比输入的要求“bob ted”(记得,这个实际上是bob和ted)与“bob或ted”。根据第一个要求所得的结果所包含的页面会共同提到Bob和Ted.而后者,结果所包含的页面会只单独提到Bob,也有些页面是单独提到Ted,还有一些是共同提到他们二者的。这是一个微妙的差异,但却是很重要的。

因此,如果你想要一个“与/或”的搜索——搜索包括一个或另一个词的页面,但不一定是都包括二者——你必须在两个关键词之间插入一个带有“或者”功能的操作。当你输入这个“OR”的表示“或者”操作时,请确保所输入的要大写,否则Google会将它忽视为一个忽略的单词(stop word)——也我们接下去将要讲到的。

技巧三:你的搜索中包括或不包括的词

关于这些“and”和“or”的词,Google会自动地将这些在你输入的搜索要求中的不重要的、普通的词忽略掉。这些被称作是“忽略的单词”,包括 “and”、“the,”、“where”、“how”、“what”、“or”(所有字母皆为小写,还有其它一些类似的词——包括一些单独的数字或单独的字母(例如 “a”)。

在搜索中包含忽略的单词并没有什么大碍,不过会使搜索速度有些下降,这就是Google将它们剔除的原因。举一个例子,你想要搜索的是“how a toaster works”(烤箱如何工作),Google会移除“how”和“a”两个词,并自行按新的更短的关键词“toaster works”进行搜索。

如果你想要让这些一般的词包含在你的搜索要求内,你可以通过让Google必须在搜索中包含这些特定的词,使它不去排除“忽略的单词”。想要做到这点,你可以在你确实需要的词之前加上一个“+”符号。例如,要在搜索要求中包含“how”,你应该输入“+how”。请确保在+符号之前有一个空格符,而不是在它之后。

从另一方面来说,有时你会想要通过排除一些包含特定词的页面来精炼你的搜索结果。你可以通过使用一个“-”号来去掉搜索结果中不想要包括在内的词;在你的搜索要求中任何之前加上了“-”符号的词都会自动地排除在搜索结果之外。同样地,也请记住在“-”符号之前留一个空格符。

例如,如果你想要搜索“bass”,你所得到的页面可能会包括男歌手一类的或是关于鱼的一类的。如果你仅向搜索的是歌手这类的页面,输入搜索要求时应如下:“bass -fish”。

技巧四:搜索近似的词

不确定你在一次搜索中想要搜索的词是正确的?你是否担心一些页面会使用可替代的词来描述你想要的东西呢?

幸运的是,Google能够让你搜索近似的词——叫做同义词——通过使用“~”符号。只要在想要搜索的词之前加上“~”符号,Google就会搜索所有包括这个词以及合适的近义词的页面。

例如,要搜索类似“elderly”的词,输入“~elderly”,所得到的页面就会不仅是包括“elderly”这个词,还会有包括“senior”、“older”、“aged”等等词的页面。

在此还有个额外的技巧:如果要只是列出近义词的页面,而不需要给出许多原先输入的那个词的页面,可以用“-”符号来连接“~”操作,例如 “~keyword -keyword”。这样就能在近义词所得的结果中排除原先输入的词。在先前的例子中,要得到仅有“elderly”近义词的搜索结果,就输入 “~elderly -elderly”即可。

技巧五:搜索特定的词组

当你搜索一个特定词组时,如果你只是简单地输入词组中所有的词你是无法得到最好的结果的。Google也许能够反馈出包含这个词组的结果,但它也会列出包含你所输入所有词的结果,却未必让这些词按照正确的顺序。

如果你要搜索一个特定的词组,你应该将整个词组放在一个引号内。这样就能让Google搜索规定顺序的精确的关键词。

例如,如果你要搜索“Monty Python”,你可以输入monty python作为你的搜索要求,接着你也许会获得可接受的结果;这些结果中会包含有着“monty”和“python”两个词的页面。但这些结果并不仅是包含了关于英国喜剧团体的页面,还包括了名叫Monty的蛇以及名叫Monty的家伙,他养了蛇来当宠物,还有其它一些包括了“monty”和 “python”的词的页面,即使它们之间看起来似乎毫无关联。

为了将搜索结果限定在只关于Monty Python喜剧团之内,也就是你想要搜索的页面是按规定的顺序,将这两个词作为一个词组包含在内的,你就应该在输入搜索要求时输入"monty python"——确保这个词组在引号之内。这样的话,如果没有按照规定的顺序将这两个次匹配为一个精确的词组,这个页面就不会被列在搜索结果当中。

技巧六:列出相似的页面

你是否有发现过一个网页是你确实很喜欢的,又想知道是否还有与它类似的其它网页呢?不需要再疑虑地思考了;你可以使用Google的相关来寻找:这个操作算符所显示的页面会与特定的页面在某些方面是相似的。例如,如果你很喜欢InformIT上的文章,你可以通过输入 “related:http://www.informit.com”来寻找类似的页面。

技巧七:通过其它的操作算符调整你的搜索

使用相关操作算符只是众多你可用来调整Google搜索结果的方法之一。所有的这些输入的操作算符都是以相同的方式工作的,将这些算符作为你搜索请求的一部分输入,再将变量紧接在这些输入的操作算符之后的冒号之后(而不是空格),就像这样:“‘操作算符’:‘变量’”。

有哪些搜索的操作算符是你可以利用的呢?以下是一个简短的清单:

算符 用途 用法
allinanchor: 限制搜索的词语是网页中链接内包含的关键词(可使用多个关键词)allinanchor:keyword1 keyword2
allintext: 限制搜索的词语是网页内文包含的关键词(可使用多个关键词)allintext:keyword1 keyword2
allintitle: 限制搜索的词语是网页标题中包含的关键词(可使用多个关键词)allintitle:keyword1 keyword2
allinurl: 限制搜索的词语是网页网址中包含的关键词(可使用多个关键词)inurl:keyword1 keyword2
filetype: 限制所搜索的文件一个特定的格式 filetype:extension
inanchor: 限制搜索的词语是网页中链接内包含的关键词 inanchor:keyword
intext: 限制搜索的词语是网页内文包含的关键词 intext:keyword
intitle: 限制搜索的词语是网页标题中包含的关键词 intitle:keyword
inurl: 限制搜索的网页的地址 inurl:keyword
site: 限制所进行的搜索在指定的域名或网站内 site:domain
技巧八:搜索特定的事实

如果你要搜索一些客观事实,Google也许能够帮得上忙。是的,Google总是能够反馈给你一个匹配你指定的搜索要求的清单,但只要你能够正确地描述了你的搜索要求,且接着搜索的事实是Google已经预先鉴定了额,你就能在搜索结果页面的最顶端得到你所需要的精确信息。

我们在此谈到的有哪些类型的信息呢?是一些事实性的信息,例如诞生日、诞生地、人口等等。你所需要做的就是输入你想要知道的描述事实的搜索要求。例如:

要查询圣弗朗西斯科的人口,则输入“人口 圣弗朗西斯科”。

要查询马克·吐温在哪里出生,则输入“出生地 马克吐温”。

要查询总统比尔·克林顿什么时候出生,则输入“生日 比尔克林顿”。

要查询雷蒙德·钱德勒什么时候去世,则输入“去世 雷蒙德·钱德勒”。

要查询谁是德国总统,则输入“总统 德国”。

这些问题的答案就会显示在搜索结果页面的顶部。你能够获根据关联的网站得应对你的疑问的正确答案。点击相关的链接还能从这个资源里获悉更多的东西。

技巧九:搜索Google Directory

Google在它的搜索数据库中将成千上万的网页索引化——这就能使得不会产生压倒性数量的搜索结果。量确实已经够了,但有时你也许会更愿意得到一些高质量的结果。

由于质量较数量更为重要,就可以绕过主要的Google搜索引擎而使用Google Directory来代替。Google Directory是一个网页清单相对较小的数据库,它们都是通过一个人工编辑团队手动精心挑选的。Google Directory是有被注释和组织到相关的话题类目下的。你可以通过类目来浏览网页目录,或是搜索指定的项目。

Google Directory是一个可用来搜索大量Google网页索引实用选择。Google Directory的结果比起你在更大的搜索索引范围中的搜索结果更为集中且高质,也能够帮助你在任何给定的类目下更好地认识什么是可用的信息。另外,如果你喜欢,你也可以利用浏览类目来替代搜索。

要进入Google Directory,点击Google主页上的“更多”链接,在接下来的页面中选择类别。当然,你也可以直接进入Google的Directory,只要在浏览器中输入directory.google.com即可。

技巧十:使用Google的其它专业搜索

Google Directory不仅仅是Google所提供的除了主搜索引擎之外的唯一选择。根据你所做的搜索类型,你也可以通过使用其中Google更为专业的搜索站点之一来得到更好的结果。它们包括:

Froogle可以用来搜索那些有着最低价格的特定商品的在线购物网站。

Google Answers所提供的服务是直接让你的搜索需求面向专业人员的小组,大多数都是用于当你有着更为复杂的问题,而无法通过简单的搜索来解答的时候。

Google Apple Macintosh Search主要是在apple.com域名中以及和其它与苹果相关的站点进行搜索。

Google Blog Search是用来搜索博客和博客文章的。

Google Book Search可以搜索成千上万的小说和非小说类图书的全文。

Google BSD UNIX Search可以搜索到大量专门为BSD版本的UNIX操作系统专业化的站点。

Google Groups可以搜索到Google Groups档案的相关文章。

Google Linux Search用来搜索大量与Linux相关的网站。

Google Microsoft Search可以搜索主要是microsoft.com域名中的内容以及其它微软相关的站点。

Google News可用来搜索大量有最新新闻和头条的新闻网站,也能够搜索历史的新闻资料,一直可以追溯到两个世纪之前。

Google Scholar在一个有着学术杂志、文章、报纸、论文和书籍的数据库中进行搜索,也能够选择大学或研究书库。

Google U.S. Government Search是用来搜索那些美国政府的网站——这是一个最好的用来搜索官方性政府举措、信息、报告等等的地方。

Google University Search能在一个有着超过600所大学网站的数据库中进行搜索——能够用来查询课程安排、入学资料等等。

原文作者:Michael Miller

原文标题:Ten Tips for Smarter Google Searches

原文地址:http://www.informit.com/articles/article.asp?p=675274&rl=1

编译人:雪影蓝风(IT168)

译文地址:http://publish.it168.com/2006/1213/20061213001101.shtml

2009年2月13日星期五

黑客的基本技能

摘自《如何成为黑客》

黑客态度重要,但技术更加重要。态度无法替代技术,在你被别的黑客称为黑客之前,有一些基本的技术你必须掌握。

这些基本技术随着新技术的出现和老技术的过时也随时间在缓慢改变。例如,过去内容包括使用机器语言编程,而直到最近才包括了HTML。总的来说现在主要包括以下技术:

学习如何编程。

这当然是最基本的黑客技能。如果你还不会任何编程语言,我建议你从Python开始。它设计清晰,文档齐全,合适初学者入门。它是一门很好的入门语言,并且不仅仅只是个玩具;它非常强大、灵活,也适合做大型项目。我有一篇 Python评价详细说明这点。好的教程可以在Python网站得到。(译者:比较好的中文Python站点可能是 http://pythonrecord.51.net )

Java也是好的入门语言。它比Python难得多,但是生成的代码速度也快得多。它同时也是一种优秀的计算机语言,不止是用来入门。

但是注意,如果你只会一两门语言,你将不会达到黑客所要求的技术水平,甚至也不能达到一个程序员的水平——你需要学会如何以抽象的方式思考编程问题,独立于任何语言。要做一名真正的黑客,你需要学会在几天内通过一些手册,结合你现在所知,迅速掌握一门新语言。这意味着你应该学会几种截然不同的语言。

如果要做一些重要的编程工作,你将不得不学习C语言,Unix的核心语言。C++与C非常其他类似;如果你了解其中一种,学习另一种应该不难。但这两种都不适合编程入门者学习。而且事实上,你越避免用C编程,你的工作效率会越高。

C非常有效率,节约你的机器资源。不幸的是,C的高效是通过你手动做很多底层的管理(如内存)来达到的。底层代码都是复杂极易出现bug 的,会使你花极多的时间调试。如今的机器速度如此之快,这通常是得不偿失——比较明智的做法是使用一种运行较慢、较低效率,但大幅节省你的时间的语言。因此,选择Python。

其他对黑客而言比较重要的语言包括 Perl和 LISP。 Perl实用,值得一学;它被广泛用于动态网页和系统管理,因此即便你从不用Perl写程序,至少也应该学会看。许多人使用Perl的理由和我建议你使用 Python的理由一样,都是为了避免用C完成那些不需要C高效率的工作。你会需要理解那些工作的代码的。

LISP值得学习的理由不同——最终掌握了它时你会得到丰富的启迪和经验。这些经验会使你在以后的日子里成为一个更好的程序员,即使你实际上很少使用LISP本身。

当然,实际上你最好五种都会(Python,Java,C/C++,Perl和LISP)。除了是最重要的黑客语言外,它们还代表了截然不同的编程思路和方法,每种都会让你受益非浅。

这里我无法给你完完全全的指导教会你如何编程——这是个复杂的技能。但我可以告诉你,书本和上课也不能作到(最好的黑客中,有许多,也许几乎都是自学成材的)。你可以从书本上学到语言的特点——只是一些皮毛,但要使书面知识成为自身技能只能通过实践和虚心向他人学习。因此要作到(一)读代码及(二)写代码。

学习如何编程就象学习用优美的自然语言写作一样。最好的做法是读一些大师的名著,试着自己写点东西,再读些,再写点,再读些,再写点…… 如此往复,直到你的文章达到你体会到的范文的简洁和力量。

过去找到适合阅读的好的代码是困难的,因为几乎没有大型程序的源代码能让新手练手。这种状况已经戏剧性地发生变化;开放源代码软件,编程工具和操作系统(全都由黑客写成)现在已经随处可见。让我们在下一个话题中继续讨论……


得到一个开放源代码的Unix并学会使用、运行它。

我假设你已经拥有或者能使用一台个人电脑(今天的孩子们真幸福 Smile )。新手们能够朝学习黑客技能迈出的最基本的一步就是得到一份Linux或BSD-Unix的一种,安装在个人电脑上,并运行它。

没错,这世界上除了Unix还有其他操作系统。但它们都是以二进制形式发布的——你无法读到它的源代码,也不可能修改它。尝试在运行DOS或Windows或MacOS的机器上学习黑客技术,就象是带着脚镣学跳舞。

除此之外,Unix还是Internet的操作系统。你可以学会上网却不知道Unix,但你不了解Unix就无法成为一名Internet 黑客。因此,今天的黑客文化在很大程度上是以Unix为中心的。(这点并不总是真的,一些很早的黑客对此一直很不高兴,但Unix和Internet之间的联系已是如此之强,甚至连Microsoft也无可奈何。)

所以, 安装一套UNIX——我个人喜爱LINUX但还有其他种类的(是的,你可以同时安装Linux及DOS/Windows在同一电脑上)。学习它,使用它,配置它。用它在Internet上冲浪。阅读它的源代码。修改它的源代码。你会得到比在Microsoft操作系统上更好的编程工具(包括 C,LISP,Python及Perl)。你会觉得乐趣无穷,学到在你成为大师之前意识不到的更多的知识。

想知道更多关于学习Unix的信息,访问 The Loginataka。

想知道如何得到一份Linux,访问 我在哪里可以获得Linux。(译者:对于中文读者来讲,最简单的方式未过于前往附近的D版/正版光盘店。)

你可以在 www.bsd.org找到BSD Unix的求助及其他资源。

我有写一篇关于 Unix和Internet基础的入门文章。

(注:如果你是一个新手,我不推荐自己独立安装Linux或者BSD。安装Linux的话,寻求本地Linux用户组的帮助;或联系 Open Projects Network。 LISC维护着一些 IRC频道,在那里你可以获得帮助。)

学会如何使用WWW和写HTML


黑客文化建造的大多东西都在你看不见的地方发挥着作用,帮助工厂、办公室和大学正常运转,表面上很难看到它对非黑客的普通人的生活的影响。Web是一个大大的例外。即便政客也同意,这个巨大耀眼的黑客玩具正在改变整个世界。单是这个原因(还有许多其它的),你就需要学习掌握Web。

这并不是仅仅意味着如何使用浏览器(谁都会),而是要学会如何写HTML,Web的标记语言。如果你不会编程,写HTML会教你一些有助于学习的思考习惯。因此,先完成一个主页。(网上有很多好的教程; 这是一个。)

但仅仅拥有一个主页不能使你成为一名黑客。 Web里充满了各种网页。大多数是毫无意义的,零信息量垃圾——界面时髦的垃圾,注意,垃圾的水准都类似(更多信息访问 The HTML Hell Page)。

要想有价值,你的网页必须有内容—— 它必须有趣或对其它黑客有帮助。这是下一个话题所涉及的……

如果你不懂实用性的英语,学习吧。

作为一个美国人和一个以英语为母语的人,我以前很不情愿提到这点,免得成为一种文化上的帝国主义。但相当多以其他语言为母语的人一直劝我指出这一点,那就是英语是黑客文化和Internet的工作语言,你需要懂得以便在黑客社区顺利工作。

这一点千真万确。大概1991年的时候我就了解到许多黑客在技术讨论中使用英语,甚至当他们的母语都相同,英语对他们而言只是第二语言的时候;据我知道的报导,当前英语有着比其他语言丰富得多的技术词汇,因此是一个对于工作来说相当好的工具。基于类似的原因,英文技术书籍的翻译通常不令人满意(如果有翻译的话)。

Linus Torvalds,一个芬兰人,用英语注释他的代码(很明显这对他来说不是凑巧)。他流利的英语成为他能够管理全球范围的Linux开发人员社区的重要因素。这是一个值得学习的例子。

2009年2月12日星期四

MapReduce: 一个巨大的倒退

这是我从postgresql的wiki转载过来的,原文请看这里

前言

databasecolumn 的数据库大牛们(其中包括PostgreSQL的最初伯克利领导:Michael Stonebraker)最近写了一篇评论当前如日中天的MapReduce 技术的文章,引发剧烈的讨论。我抽空在这儿翻译一些,一起学习。

译者注:这种 Tanenbaum vs. Linus 式的讨论自然会导致非常热烈的争辩。但是老实说,从 Tanenbaum vs. Linus 的辩论历史发展来看,Linux是越来越多地学习并以不同方式应用了 Tanenbaum 等 OS 研究者的经验(而不是背弃); 所以 MapReduce vs. DBMS 的讨论,希望也能给予后来者更多的启迪,而不是对立。

原文见:
http://www.databasecolumn.com/2008/01/mapreduce-a-major-step-back.html

MapReduce: A major step backwards/MapReduce: 一个巨大的倒退

注:作者是 David J. DeWitt 和 Michael Stonebraker

On January 8, a Database Column reader asked for our views on new distributed database research efforts, and we'll begin here with our views on MapReduce. This is a good time to discuss it, since the recent trade press has been filled with news of the revolution of so-called "cloud computing." This paradigm entails harnessing large numbers of (low-end) processors working in parallel to solve a computing problem. In effect, this suggests constructing a data center by lining up a large number of "jelly beans" rather than utilizing a much smaller number of high-end servers.

1月8日,一位Database Column的读者询问我们对各种新的分布式数据库研究工作有何看法,我们就从MapReduce谈起吧。现在讨论MapReduce恰逢其时,因为最近商业媒体充斥着所谓“云计算(cloud computing)”革命的新闻。这种计算方式通过大量(低端的)并行工作的处理器来解决计算问题。实际上,就是用大量便宜货(原文是jelly beans)代替数量小得多的高端服务器来构造数据中心。

For example, IBM and Google have announced plans to make a 1,000 processor cluster available to a few select universities to teach students how to program such clusters using a software tool called MapReduce [1]. Berkeley has gone so far as to plan on teaching their freshman how to program using the MapReduce framework.

例如,IBM和Google已经宣布,计划构建一个1000处理器的集群,开放给几个大学,教授学生使用一种名为MapReduce [1]的软件工具对这种集群编程。加州大学伯克利分校甚至计划教一年级新生如何使用MapReduce框架编程。

As both educators and researchers, we are amazed at the hype that the MapReduce proponents have spread about how it represents a paradigm shift in the development of scalable, data-intensive applications. MapReduce may be a good idea for writing certain types of general-purpose computations, but to the database community, it is:

我们都既是教育者也是研究人员,MapReduce支持者们大肆宣传它代表了可伸缩、数据密集计算发展中的一次范型转移,对此我们非常惊讶。MapReduce就编写某些类型的通用计算程序而言,可能是个不错的想法,但是从数据库界看来,并非如此:

1. A giant step backward in the programming paradigm for large-scale data intensive applications
2. A sub-optimal implementation, in that it uses brute force instead of indexing
3. Not novel at all -- it represents a specific implementation of well known techniques developed nearly 25 years ago
4. Missing most of the features that are routinely included in current DBMS
5. Incompatible with all of the tools DBMS users have come to depend on

1. 在大规模的数据密集应用的编程领域,它是一个巨大的倒退
2. 它是一个非最优的实现,使用了蛮力而非索引
3. 它一点也不新颖——代表了一种25年前已经开发得非常完善的技术
4. 它缺乏当前DBMS基本都拥有的大多数特性
5. 它和DBMS用户已经依赖的所有工具都不兼容

First, we will briefly discuss what MapReduce is; then we will go into more detail about our five reactions listed above.

首先,我们简要地讨论一下MapReduce是什么,然后更详细地阐述上面列出的5点看法。

What is MapReduce?/何谓MapReduce?

The basic idea of MapReduce is straightforward. It consists of two programs that the user writes called map and reduce plus a framework for executing a possibly large number of instances of each program on a compute cluster.

MapReduce的基本思想很直接。它包括用户写的两个程序:map和reduce,以及一个framework,在一个计算机簇中执行大量的每个程序的实例。

The map program reads a set of "records" from an input file, does any desired filtering and/or transformations, and then outputs a set of records of the form (key, data). As the map program produces output records, a "split" function partitions the records into M disjoint buckets by applying a function to the key of each output record. This split function is typically a hash function, though any deterministic function will suffice. When a bucket fills, it is written to disk. The map program terminates with M output files, one for each bucket.

map程序从输入文件中读取"records"的集合,执行任何需要的过滤或者转换,并且以(key,data)的形式输出 records的集合。当map程序产生输出记录,"split"函数对每一个输出的记录的key应用一个函数,将records分割为M个不连续的块 (buckets)。这个split函数有可能是一个hash函数,而其他确定的函数也是可用的。当一个块被写满后,将被写道磁盘上。然后map程序终止,输出M个文件,每一个代表一个块(bucket)。

In general, there are multiple instances of the map program running on different nodes of a compute cluster. Each map instance is given a distinct portion of the input file by the MapReduce scheduler to process. If N nodes participate in the map phase, then there are M files on disk storage at each of N nodes, for a total of N * M files; Fi,j, 1 ≤ i ≤ N, 1 ≤ j ≤ M.

通常情况下,map程序的多个实例持续运行在compute cluster的不同节点上。每一个map实例都被MapReduce scheduler分配了input file的不同部分,然后执行。如果有N个节点参与到map阶段,那么在这N个节点的磁盘储存都有M个文件,总共有N*M个文件。

The key thing to observe is that all map instances use the same hash function. Hence, all output records with the same hash value will be in corresponding output files.

值得注意的地方是,所有的map实例都使用同样的hash函数。因此,有相同hash值的所有output record会出被放到相应的输出文件中。

The second phase of a MapReduce job executes M instances of the reduce program, Rj, 1 ≤ j ≤ M. The input for each reduce instance Rj consists of the files Fi,j, 1 ≤ i ≤ N. Again notice that all output records from the map phase with the same hash value will be consumed by the same reduce instance -- no matter which map instance produced them. After being collected by the map-reduce framework, the input records to a reduce instance are grouped on their keys (by sorting or hashing) and feed to the reduce program. Like the map program, the reduce program is an arbitrary computation in a general-purpose language. Hence, it can do anything it wants with its records. For example, it might compute some additional function over other data fields in the record. Each reduce instance can write records to an output file, which forms part of the "answer" to a MapReduce computation.

MapReduce的第二个阶段执行M个reduce程序的实例, Rj, 1 <= j <= M. 每一个reduce实例的输入是Rj,包含文件Fi,j, 1<= i <= N. 注意,每一个来自map阶段的output record,含有相同的hash值的record将会被相同的reduce实例处理 -- 不论是哪一个map实例产生的数据。在map-reduce架构处理过后,input records将会被以他们的keys来分组(以排序或者哈希的方式),到一个reduce实例然后给reduce程序处理。和map程序一样,reduce程序是任意计算言表示的。因此,它可以对它的records做任何想做事情。例如,可以添加一些额外的函数,来计算record的其他 data field。每一个reduce实例可以将records写到输出文件中,组成MapReduce计算的"answer"的一部分。

To draw an analogy to SQL, map is like the group-by clause of an aggregate query. Reduce is analogous to the aggregate function (e.g., average) that is computed over all the rows with the same group-by attribute.

和SQL可以做对比的是,map程序和聚集查询中的 group-by 语句相似。Reduce函数和聚集函数(例如,average,求平均)相似,在所有的有相同group-by的属性的列上计算。

We now turn to the five concerns we have with this computing paradigm.

现在来谈一谈我们对这种计算方式的5点看法。

MapReduce is a step backwards in database access

As a data processing paradigm, MapReduce represents a giant step backwards. The database community has learned the following three lessons from the 40 years that have unfolded since IBM first released IMS in 1968.

* Schemas are good.
* Separation of the schema from the application is good.
* High-level access languages are good.

* Schemas是有益的。
* 将schema和程序分开处理是有益的。
* High-level存取语言是有益的。

MapReduce has learned none of these lessons and represents a throw back to the 1960s, before modern DBMSs were invented.

MapReduce没有学到任何一条,并且倒退回了60年代,倒退回了现代数据库管理系统发明以前的时代。

The DBMS community learned the importance of schemas, whereby the fields and their data types are recorded in storage. More importantly, the run-time system of the DBMS can ensure that input records obey this schema. This is the best way to keep an application from adding "garbage" to a data set. MapReduce has no such functionality, and there are no controls to keep garbage out of its data sets. A corrupted MapReduce dataset can actually silently break all the MapReduce applications that use that dataset.

DBMS社区懂得schemas的重要性,凭借fields和他们的数据类型记录在储存中。更重要的,运行状态的DBMS系统可以确定输入的记录都遵循这个schema。这是最佳的保护程序不会添加任何垃圾信息到数据集中。MapReduce没有任何这样的功能,没有任何控制数据集的预防垃圾数据机制。一个损坏的MapReduce数据集事实上可以无声无息的破坏所有使用这个数据集的MapReduce程序。

It is also crucial to separate the schema from the application program. If a programmer wants to write a new application against a data set, he or she must discover the record structure. In modern DBMSs, the schema is stored in a collection of system catalogs and can be queried (in SQL) by any user to uncover such structure. In contrast, when the schema does not exist or is buried in an application program, the programmer must discover the structure by an examination of the code. Not only is this a very tedious exercise, but also the programmer must find the source code for the application. This latter tedium is forced onto every MapReduce programmer, since there are no system catalogs recording the structure of records -- if any such structure exists.

将schema和程序分开也非常重要。如果一个程序员想要对一个数据集写一个新程序,他必须知道数据集的结构(record structure)。现代DBMS系统中,shcema储存在系统目录中,并且可以被任意用户查询(使用SQL)它的结构。相反的,如果schema不存在或者存在于程序中,程序员必须检查程序的代码来获得数据的结构。这不仅是一个单调枯燥的尝试,而且程序员必须能够找到先前程序的source code。每一个MapReduce程序员都必须承受后者的乏味,因为没有系统目录用来储存records的结构 -- 就算这些结构存在。

During the 1970s the DBMS community engaged in a "great debate" between the relational advocates and the Codasyl advocates. One of the key issues was whether a DBMS access program should be written:

* By stating what you want - rather than presenting an algorithm for how to get it (relational view)
* By presenting an algorithm for data access (Codasyl view)

70年代DBMS社区,在关系型数据库支持者和Codasys型数据库支持者之间发有一次"大讨论"。一个重点议题就是是否DBMS存取程序应该写入:

* 直接开始你想要的 -- 而不是展示一个算法,解释如何工作的。 (关系型数据库的观点)
* 展示数据存取的算法。(Codasyl 的观点)

The result is now ancient history, but the entire world saw the value of high-level languages and relational systems prevailed. Programs in high-level languages are easier to write, easier to modify, and easier for a new person to understand. Codasyl was rightly criticized for being "the assembly language of DBMS access." A MapReduce programmer is analogous to a Codasyl programmer -- he or she is writing in a low-level language performing low-level record manipulation. Nobody advocates returning to assembly language; similarly nobody should be forced to program in MapReduce.

MapReduce advocates might counter this argument by claiming that the datasets they are targeting have no schema. We dismiss this assertion. In extracting a key from the input data set, the map function is relying on the existence of at least one data field in each input record. The same holds for a reduce function that computes some value from the records it receives to process.

Writing MapReduce applications on top of Google's BigTable (or Hadoop's HBase) does not really change the situation significantly. By using a self-describing tuple format (row key, column name, {values}) different tuples within the same table can actually have different schemas. In addition, BigTable and HBase do not provide logical independence, for example with a view mechanism. Views significantly simplify keeping applications running when the logical schema changes.

MapReduce is a poor implementation

2. MapReduce是一个糟糕的实现

All modern DBMSs use hash or B-tree indexes to accelerate access to data. If one is looking for a subset of the records (e.g., those employees with a salary of 10,000 or those in the shoe department), then one can often use an index to advantage to cut down the scope of the search by one to two orders of magnitude. In addition, there is a query optimizer to decide whether to use an index or perform a brute-force sequential search.

所有现代DBMS都使用散列或者B树索引加速数据存取。如果要寻找记录的某个子集(比如薪水为10000的雇员或者鞋部的雇员),经常可以使用索引有效地将搜索范围缩小一到两个数量级。而且,还有查询优化器来确定是使用索引还是执行蛮力顺序搜索。

MapReduce has no indexes and therefore has only brute force as a processing option. It will be creamed whenever an index is the better access mechanism.

MapReduce没有索引,因此处理时只有蛮力一种选择。在索引是更好的存取机制时,MapReduce将劣势尽显。

One could argue that value of MapReduce is automatically providing parallel execution on a grid of computers. This feature was explored by the DBMS research community in the 1980s, and multiple prototypes were built including Gamma [2,3], Bubba [4], and Grace [5]. Commercialization of these ideas occurred in the late 1980s with systems such as Teradata.

有人可能会说,MapReduce的价值在于在计算机网格上自动地提供并行执行。这种特性数据库研究界在上世纪80年代就已经探讨过了,而且构建了许多原型,包括 Gamma [2,3], Bubba [4], 和 Grace [5]。而Teradata这样的系统早在80年代晚期,就将这些想法商业化了。

In summary to this first point, there have been high-performance, commercial, grid-oriented SQL engines (with schemas and indexing) for the past 20 years. MapReduce does not fare well when compared with such systems.

There are also some lower-level implementation issues with MapReduce, specifically skew and data interchange.

One factor that MapReduce advocates seem to have overlooked is the issue of skew. As described in "Parallel Database System: The Future of High Performance Database Systems," [6] skew is a huge impediment to achieving successful scale-up in parallel query systems. The problem occurs in the map phase when there is wide variance in the distribution of records with the same key. This variance, in turn, causes some reduce instances to take much longer to run than others, resulting in the execution time for the computation being the running time of the slowest reduce instance. The parallel database community has studied this problem extensively and has developed solutions that the MapReduce community might want to adopt.

There is a second serious performance problem that gets glossed over by the MapReduce proponents. Recall that each of the N map instances produces M output files -- each destined for a different reduce instance. These files are written to a disk local to the computer used to run the map instance. If N is 1,000 and M is 500, the map phase produces 500,000 local files. When the reduce phase starts, each of the 500 reduce instances needs to read its 1,000 input files and must use a protocol like FTP to "pull" each of its input files from the nodes on which the map instances were run. With 100s of reduce instances running simultaneously, it is inevitable that two or more reduce instances will attempt to read their input files from the same map node simultaneously -- inducing large numbers of disk seeks and slowing the effective disk transfer rate by more than a factor of 20. This is why parallel database systems do not materialize their split files and use push (to sockets) instead of pull. Since much of the excellent fault-tolerance that MapReduce obtains depends on materializing its split files, it is not clear whether the MapReduce framework could be successfully modified to use the push paradigm instead.

Given the experimental evaluations to date, we have serious doubts about how well MapReduce applications can scale. Moreover, the MapReduce implementers would do well to study the last 25 years of parallel DBMS research literature.

MapReduce is not novel

The MapReduce community seems to feel that they have discovered an entirely new paradigm for processing large data sets. In actuality, the techniques employed by MapReduce are more than 20 years old. The idea of partitioning a large data set into smaller partitions was first proposed in "Application of Hash to Data Base Machine and Its Architecture" [11] as the basis for a new type of join algorithm. In "Multiprocessor Hash-Based Join Algorithms," [7], Gerber demonstrated how Kitsuregawa's techniques could be extended to execute joins in parallel on a shared-nothing [8] cluster using a combination of partitioned tables, partitioned execution, and hash based splitting. DeWitt [2] showed how these techniques could be adopted to execute aggregates with and without group by clauses in parallel. DeWitt and Gray [6] described parallel database systems and how they process queries. Shatdal and Naughton [9] explored alternative strategies for executing aggregates in parallel.

Teradata has been selling a commercial DBMS utilizing all of these techniques for more than 20 years; exactly the techniques that the MapReduce crowd claims to have invented.

While MapReduce advocates will undoubtedly assert that being able to write MapReduce functions is what differentiates their software from a parallel SQL implementation, we would remind them that POSTGRES supported user-defined functions and user-defined aggregates in the mid 1980s. Essentially, all modern database systems have provided such functionality for quite a while, starting with the Illustra engine around 1995.

MapReduce is missing features

All of the following features are routinely provided by modern DBMSs, and all are missing from MapReduce:

* Bulk loader -- to transform input data in files into a desired format and load it into a DBMS
* Indexing -- as noted above
* Updates -- to change the data in the data base
* Transactions -- to support parallel update and recovery from failures during update
* Integrity constraints -- to help keep garbage out of the data base
* Referential integrity -- again, to help keep garbage out of the data base
* Views -- so the schema can change without having to rewrite the application program


In summary, MapReduce provides only a sliver of the functionality found in modern DBMSs.


MapReduce is incompatible with the DBMS tools

A modern SQL DBMS has available all of the following classes of tools:

* Report writers (e.g., Crystal reports) to prepare reports for human visualization
* Business intelligence tools (e.g., Business Objects or Cognos) to enable ad-hoc querying of large data warehouses
* Data mining tools (e.g., Oracle Data Mining or IBM DB2 Intelligent Miner) to allow a user to discover structure in large data sets
* Replication tools (e.g., Golden Gate) to allow a user to replicate data from on DBMS to another
* Database design tools (e.g., Embarcadero) to assist the user in constructing a data base.


MapReduce cannot use these tools and has none of its own. Until it becomes SQL-compatible or until someone writes all of these tools, MapReduce will remain very difficult to use in an end-to-end task.

In Summary

It is exciting to see a much larger community engaged in the design and implementation of scalable query processing techniques. We, however, assert that they should not overlook the lessons of more than 40 years of database technology -- in particular the many advantages that a data model, physical and logical data independence, and a declarative query language, such as SQL, bring to the design, implementation, and maintenance of application programs. Moreover, computer science communities tend to be insular and do not read the literature of other communities. We would encourage the wider community to examine the parallel DBMS literature of the last 25 years. Last, before MapReduce can measure up to modern DBMSs, there is a large collection of unmet features and required tools that must be added.

看到规模大得多的社区加入可伸缩的查询处理技术的设计与实现,非常令人兴奋。但是,我们要强调,他们不应该忽视数据库技术40多年来的教训,尤其是数据库技术中数据模型、物理和逻辑数据独立性、像SQL这样的声明性查询语言等等,可以为应用程序的设计、实现和维护带来的诸多好处。而且,计算机科学界往往喜欢自行其是,不理会其他学科的文献。我们希望更多人来一起研究过去25年的并行DBMS文献。MapReduce要达到能够与现代 DBMS相提并论的水平,还需要开发大量特性和工具。

We fully understand that database systems are not without their problems. The database community recognizes that database systems are too "hard" to use and is working to solve this problem. The database community can also learn something valuable from the excellent fault-tolerance that MapReduce provides its applications. Finally we note that some database researchers are beginning to explore using the MapReduce framework as the basis for building scalable database systems. The Pig[10] project at Yahoo! Research is one such effort.

我们完全理解数据库系统也有自己的问题。数据库界清楚地认识到,现在数据库系统还太“难”使用,而且正在解决这一问题。数据库界也从 MapReduce为其应用程序提供的出色的容错上学到了有价值的东西。最后,我们注意到,一些数据库研究人员也开始研究使用MapReduce框架作为构建可伸缩数据库系统的基础。雅虎研究院的Pig[10]项目就是其中之一。

References

[1] "MapReduce: Simplified Data Processing on Large Clusters," Jeff Dean and Sanjay Ghemawat, Proceedings of the 2004 OSDI Conference, 2004.

[2] "The Gamma Database Machine Project," DeWitt, et. al., IEEE Transactions on Knowledge and Data Engineering, Vol. 2, No. 1, March 1990.

[4] "Gamma - A High Performance Dataflow Database Machine," DeWitt, D, R. Gerber, G. Graefe, M. Heytens, K. Kumar, and M. Muralikrishna, Proceedings of the 1986 VLDB Conference, 1986.

[5] "Prototyping Bubba, A Highly Parallel Database System," Boral, et. al., IEEE Transactions on Knowledge and Data Engineering,Vol. 2, No. 1, March 1990.

[6] "Parallel Database System: The Future of High Performance Database Systems," David J. DeWitt and Jim Gray, CACM, Vol. 35, No. 6, June 1992.

[7] "Multiprocessor Hash-Based Join Algorithms," David J. DeWitt and Robert H. Gerber, Proceedings of the 1985 VLDB Conference, 1985.

[8] "The Case for Shared-Nothing," Michael Stonebraker, Data Engineering Bulletin, Vol. 9, No. 1, 1986.

[9] "Adaptive Parallel Aggregation Algorithms," Ambuj Shatdal and Jeffrey F. Naughton, Proceedings of the 1995 SIGMOD Conference, 1995.

[10] "Pig", Chris Olston, http://research.yahoo.com/project/90

[11] "Application of Hash to Data Base Machine and Its Architecture," Masaru Kitsuregawa, Hidehiko Tanaka, Tohru Moto-Oka, New Generation Comput. 1(1): 63-74 (1983)

2009年2月10日星期二

Oracle跨版本导出EXP-00003错误的解决

作者:eygle 【转载时请以超链接形式标明文章出处和作者信息及本声明】
链接:http://www.eygle.com/archives/2009/02/oracle_exp_00003.html

当Oracle数据库中,使用不同版本的exp工具进行数据库导出时,有时候会遇到类似如下错误:

EXP-00003: no storage definition found for segment(11, 307)
EXP-00003: no storage definition found for segment(11, 523)
EXP-00003: no storage definition found for segment(11, 643)
EXP-00003: no storage definition found for segment(11, 275)

"EXP-00003: no storage definition found for segment ....."错误,和Oracle的一个小Bug相关,可以通过修改一个导出相关的View来解决。
在目标数据库使用sys用户,执行如下SQL创建新的view:

CREATE OR REPLACE VIEW exu9tne (
tsno, fileno, blockno, length) AS
SELECT ts#, segfile#, segblock#, length
FROM sys.uet$
WHERE ext# = 1
UNION ALL
SELECT * FROM SYS.EXU9TNEB
/

然后就可以正常进行导出操作,exp完成后, 最好将视图还原(Metalink建议):

CREATE OR REPLACE VIEW exu9tne (
tsno, fileno, blockno, length) AS
SELECT ts#, segfile#, segblock#, length
FROM sys.uet$
WHERE ext# = 1
/

EXU9TNE视图初始由 $ORACLE_HOME/rdbms/admin/catexp.sql 脚本创建。

-The End-

这个问题以前我也碰见过,只是没解决。感谢eygle 。

2009年2月8日星期日

JAVA使用itext为PDF添加元数据

我查了下似乎itext不支持直接添加元数据,我的方法是根据原来的PDF生成一篇同样的文件,然后删除原来的PDF,再将新生成的文件改名。

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;

import com.lowagie.text.DocumentException;
import com.lowagie.text.pdf.PdfReader;
import com.lowagie.text.pdf.PdfStamper;

public class PdfMetadataSetter {
public static boolean writeDocMetadata(String docName, Map Properties){
boolean isSuccess = false;
PdfReader reader=null;
PdfStamper stamp=null;

/*
* Infact i create a new file and write metadata to the file.
* after that i delete the old file and rename new file.
*/
try {
reader = new PdfReader(docName);
String newFileName=getNewFileName(docName);
stamp = new PdfStamper(reader,
new FileOutputStream(newFileName));

stamp.setMoreInfo((HashMap)Properties);
stamp.close();

File oldFile=new File(docName);
oldFile.delete();
File newFile=new File(newFileName);
newFile.renameTo(new File(docName));
} catch (Exception de) {
de.printStackTrace();
}finally{
if(reader!=null)
reader.close();
try {
if(stamp!=null)
stamp.close();
} catch (DocumentException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
return isSuccess;
}

private static String getNewFileName(String fileName){
String newFileName=fileName.substring(0,(fileName.lastIndexOf(".")))+"2.pdf";
return newFileName;
}
}

觉得这么做不太好,不过暂时没有更好的方法,先将就一下。

2009年2月6日星期五

介绍几本好书

一 编码的奥秘

这本书没有讲该如何编程,如何写一个hello world。它深入浅出的介绍了
计算机是如何组成的,比如二进制,比如逻辑门电路的实现,都是很浅显的。看了这本书,会对计算机有个知其所以然的了解,也就是机器如何实现的一加一等于二,如何能执行程序。《编码的奥秘》是计算机专业或者是初中高中对计算机感兴趣学生,应该一上学就读的一本书。

二 程序设计实践

绝对是每个C程序员必读的好书。

三 unix编程艺术

之所以选择这本书,是因为对于一个windows平台上的程序员来说,尽量开阔眼界是非常必须的事情。读一读这本书,体验一下不同的设计理念和不同的操作系统哲学,对于日常的编程工作都有非常大的帮助。不过,书里奉为经典的方法也许就是压断某些项目的最后一根稻草,书里很鄙视的方法没准确实某些项目的救命良药。作者在这本书里讨论了很多内容,很多软件设计和实现的方法,值得一看。最重要的是强调了kiss原则,这与windows的大而全思想有着本质上的不同。

四 effective java

这本书总结了JAVA平台的好多设计和实现的方法,值得一读。

五 ORACLE数据库编程艺术

数据库是基本每个项目都会用到的,tom的这本大作是其三本书的头一本(另外两本还没出呢)。这本书从数据库体系结构上阐明了ORACLE数据库的架构。相信读熟这本书对于掌握其他数据库和数据库系统实现也具有很大的意义。

六 lex和yacc

这本书的介绍前面写过,很容易上手。

七 正则表达式必知必会

学习正则表达式的入门书籍。

以上提到的几本书是我08年读过的几本好书,重要性依次递减。

2009年2月5日星期四

在JAVA里使用POI为word文档添加元数据

import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;

import java.util.Iterator;
import java.util.Map;

import org.apache.poi.hpsf.CustomProperties;
import org.apache.poi.hpsf.DocumentSummaryInformation;
import org.apache.poi.hpsf.MarkUnsupportedException;
import org.apache.poi.hpsf.NoPropertySetStreamException;
import org.apache.poi.hpsf.PropertySet;
import org.apache.poi.hpsf.SummaryInformation;
import org.apache.poi.hpsf.UnexpectedPropertySetTypeException;
import org.apache.poi.hpsf.WritingNotSupportedException;
import org.apache.poi.poifs.filesystem.DirectoryEntry;
import org.apache.poi.poifs.filesystem.DocumentEntry;
import org.apache.poi.poifs.filesystem.DocumentInputStream;
import org.apache.poi.poifs.filesystem.POIFSFileSystem;


public class WordMetadataSetter {
public static boolean writeDocMetadata(String path,String docName, Map Properties) throws UnexpectedPropertySetTypeException, NoPropertySetStreamException, MarkUnsupportedException, WritingNotSupportedException {
boolean isSuccess = false;
File dataDir = new File(path);
File doc = new File(dataDir, docName);

/* Read a test document doc into a POI filesystem. */
POIFSFileSystem poifs;
try {
poifs = new POIFSFileSystem(new FileInputStream(doc));
DirectoryEntry dir = poifs.getRoot();
DocumentEntry siEntry = (DocumentEntry) dir.getEntry(SummaryInformation.DEFAULT_STREAM_NAME);
DocumentEntry dsiEntry = (DocumentEntry) dir.getEntry(DocumentSummaryInformation.DEFAULT_STREAM_NAME);

/*
* Read the summary information stream and the document summary
* information stream from the POI filesystem.
*
* Please note that the result consists of SummaryInformation and
* DocumentSummaryInformation instances which are in memory only. To
* make them permanent they have to be written to a POI filesystem
* explicitly (overwriting the former contents). Then the POI filesystem
* should be saved to a file.
*/
DocumentInputStream dis = new DocumentInputStream(siEntry);
PropertySet ps = new PropertySet(dis);
SummaryInformation si = new SummaryInformation(ps);
dis = new DocumentInputStream(dsiEntry);
ps = new PropertySet(dis);
DocumentSummaryInformation dsi = new DocumentSummaryInformation(ps);

/*
* Write all properties supported by HPSF to the summary information
* (e.g. author, edit date, application name) and to the document
* summary information (e.g. company, manager).
*/
CustomProperties customProperties = dsi.getCustomProperties();
if (customProperties == null)
customProperties = new CustomProperties();
setProperties(customProperties,Properties);

dsi.setCustomProperties(customProperties);
si.write(dir, siEntry.getName());
dsi.write(dir, dsiEntry.getName());

OutputStream out = new FileOutputStream(doc);
poifs.writeFilesystem(out);
out.close();
isSuccess=true;
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}

return isSuccess;
}

private static void setProperties(CustomProperties customProperties,Map Properties){
if(customProperties==null||Properties==null)
throw new NullPointerException();
Iterator it=Properties.entrySet().iterator() ;
while(it.hasNext()){
Map.Entry entry=(Map.Entry) it.next() ;
String key=entry.getKey().toString();
String value=entry.getValue().toString();
customProperties.put(key,value);
}
}
}



import java.util.Date;
import java.util.Hashtable;
import java.util.Map;

import org.apache.poi.hpsf.MarkUnsupportedException;
import org.apache.poi.hpsf.NoPropertySetStreamException;
import org.apache.poi.hpsf.UnexpectedPropertySetTypeException;
import org.apache.poi.hpsf.WritingNotSupportedException;

import junit.framework.TestCase;

public class TestWordMetadataSetter extends TestCase {

private static final String POI_FS = "TestWriteWellKnown.doc";
private static final String POI_PATH = "C:\\temp";
private static final Map PROPERTIES = new Hashtable();

public TestWordMetadataSetter(){
PROPERTIES.put("author", "fangyuan");
PROPERTIES.put("create date", new Date());
PROPERTIES.put("num", "1");
}

public void testWriteDocMetadata() throws WritingNotSupportedException, UnexpectedPropertySetTypeException, NoPropertySetStreamException, MarkUnsupportedException{
boolean result=WordMetadataSetter.writeDocMetadata(POI_PATH, POI_FS, PROPERTIES);
assertEquals(result,true);
}

}