??xml version="1.0" encoding="utf-8" standalone="yes"?>
inode 译成中文是索引节点。每个存储设备或存储讑֤的分区(存储讑֤是硬盘、Y盘、U?... ... Q被格式化ؓ文gpȝ后,应该有两部䆾Q一部䆾是inodeQ另一部䆾是BlockQBlock是用来存储数据用的。而inode呢,是用来存储q些数据的信息,q些信息包括文g大小、属丅R归属的用户l、读写权限等。inode为每个文件进行信息烦引,所以就有了inode的数倹{操作系l根据指令,能通过inode值最快的扑ֈ相对应的文g?
做个比喻Q比如一本书Q存储设备或分区q当于q本书,Block相当于书中的每一,inode q当于q本书前面的目录Q一本书有很多的内容Q如果想查找某部份的内容Q我们可以先查目录,通过目录能最快的扑ֈ我们惌看的内容。虽然不太恰当,但还是比较Ş象?
当我们用ls 查看某个目录或文件时Q如果加?i 参数Q就可以看到inode节点了;比如我们前面所说的例子Q?
[root@localhost ~]# ls -li lsfile.sh
2408949 -rwxr-xr-x 1 root root 7 04-21 12:47 lsfile.sh
lsfile.sh 的inode值是 2408949 Q?查看一个文件或目录的inodeQ要通过ls 命o的的 -i参数?
2.10 inode 相同的文件是链接文Ӟ
在Linux 文gpȝ中,inode值相同的文g是硬链接文gQ也是_不同的文件名Qinode可能是相同的Q一个inode值可以对应多个文件。理解链接文件ƈ不难Q看看例子就会了。在Linux中,链接文g是通过ln工具来创建的?
2.11 创徏链接,链接和源文件关p;
用ln 创徏文g链接的语法Q?
# ln 源文?目标文g
下面我们举一个例子,在这个例子中Q我们要为sun.txt 创徏其硬链接sun002.txt。然后看一下sun.txt和sun002.txt的属性的变化Q?
[root@localhost ~]# ls -li sun.txt 注:查看sun.txt的属性;
2408263 -rw-r--r-- 1 root root 29 04-22 21:02 sun.txt 注:q是sun.txt的属性;
[root@localhost ~]# ln sun.txt sun002.txt 注:我们通过ln 来创建sun.txt的硬链接文gsun002.txt
[root@localhost ~]# ls -li sun* 注:我们列一下sun.txt 和sun002.txt
2408263 -rw-r--r-- 2 root root 29 04-22 21:02 sun002.txt
2408263 -rw-r--r-- 2 root root 29 04-22 21:02 sun.txt
我们可以看到sun.txt在没有创建硬链接文gsun002.txt的时候,光接个数是1Q也是-rw-r--r--后的那个数|Q创Z链接sun002.txt创徏后,q个值变成了2。也是_我们每次为sun.txt创徏一个新的硬链接文g后,其硬链接个数都会增加1?
inode值相同的文gQ他们的关系是互为硬链接的关pR当我们修改其中一个文件的内容Ӟ互ؓ链接的文g的内容也会跟着变化。如果我们删除互为硬链接关系的某个文件时Q其它的文gq不受媄响。比如我们把sun.txt删除后,我们q是一栯看到sun002.txt的内容,q且sun02.txt仍是存在的?
可以q么理解Q互为硬链接关系的文Ӟ他们好象是克隆体Q他们的属性几乎是完全一P
下面的例子,我们把sun.txt删除Q然后我们看一下sun002.txt 是不是能看到其内宏V?
[root@localhost ~]# rm -rf sun.txt
[root@localhost ~]# more sun002.txt
注意Q硬链接不能为目录创建,只有文g才能创徏链接?
2.12 软链接的创徏Q及软接与源文g的关p;
创徏软链接(也被UCؓW号链接Q的语法Q?
# ln -s 源文文g或目?目标文g或目?
软链接也叫符号链接,他和链接有所不同QY链接文g只是其源文g的一个标记。当我们删除了源文g后,链接文g不能独立存在Q虽然仍保留文g名,但我们却不能查看软链接文件的内容了?
[root@localhost ~]# ls -li linuxsir001.txt
2408274 -rw-r--r-- 1 root root 29 04-22 21:53 linuxsir001.txt
[root@localhost ~]# ln -s linuxsir001.txt linuxsir002.txt
[root@localhost ~]# ls -li linuxsir001.txt linuxsir002.txt
2408274 -rw-r--r-- 1 root root 29 04-22 21:53 linuxsir001.txt
2408795 lrwxrwxrwx 1 root root 15 04-22 21:54 linuxsir002.txt -> linuxsir001.txt
解释
上面的例子,首先我们查看 linuxsir001.txt 的属性,比如inode 、所属文件种cR创建或修改旉{?.. ...我们来对比一下:
首先 Ҏ一下节点:两个文g的节点不同;
其次 两个文g的归属的U类不同 linuxsir001.txt?Q也是普通文Ӟ而linuxsir002.txt 是lQ它是一个链接文Ӟ
W三 两个文g的读写权限不?linuxsir001.txt 是rw-r--r-- Q而linuxsir002.txt的读写权限是 rwxrwxrwx
W三 两者的链接个数相同;都是1
W四 两文件的属主和所归属的用L相同Q?
W五 修改(或访问、创建)旉不同Q?
我们q注意到了linuxsir002.txt 后面有一个标?->Q这表示linuxsir002.txt 是linuxsir001.txt的Y链接文g?
值得我们注意的是Q当我们修改链接文g的内ҎQ就意味着我们在修Ҏ文g的内宏V当然源文g的属性也会发生改变,链接文g的属性ƈ不会发生变化。当我们把源文g删除后,链接文g只存在一个文件名Q因为失M源文Ӟ所以Y链接文g也就不存在了。这一点和链接是不同的;
[root@localhost ~]# rm -rf linuxsir001.txt 注:删除linuxsir001.txt
[root@localhost ~]# ls -li linuxsir002.txt 注:查看linuxsir002 的属性;
2408795 lrwxrwxrwx 1 root root 15 04-22 21:54 linuxsir002.txt -> linuxsir001.txt
[root@localhost ~]# more linuxsir002.txt 注:查看linuxsir002.txt的内容;
linuxsir002.txt: 没有那个文g或目?注:得到提示Qlinuxsir002.txt不存在?
上面的例子告诉我们,如果一个链接文件失M源,意味着他已l不存在了;
我们可以看到软链接文Ӟ其实只是源文件的一个标讎ͼ当源文g失去Ӟ他也是存在了。Y链接文g只是占用了inode来存储Y链接文g属性等信息Q但文g存储是指向源文g的?
软g链接Q可以ؓ文g或目录都适用。无论是软链接还是硬链接Q都可以用rm来删除。rm工具是通用的?/ca>
参考资料:http://techcenter.dicder.com/2006/0908/content_185.htm
]]>
Lucene不是一个完整的全文索引应用Q而是是一个用Java写的全文索引引擎工具包,它可以方便的嵌入到各U应用中实现针对应用的全文烦?索功能?/p>
Lucene的作者:Lucene的A献?a >Doug Cutting是一位资深全文烦?索专Ӟ曄是V-Twin搜烦引擎(Apple的Copland操作pȝ的成׃一)的主要开发者,后在Excite担Q高pȝ架构设计师,目前从事于一些INTERNET底层架构的研I。他贡献出的Lucene的目标是为各U中型应用E序加入全文索功能?/p>
Lucene的发展历E:早先发布在作者自qwww.lucene.comQ后来发布在SourceForgeQ?001q年底成为APACHE基金会jakarta的一个子目Q?a >http://jakarta.apache.org/lucene/
已经有很多Java目都用了Lucene作ؓ其后台的全文索引引擎Q比较著名的有:
Eclipse:ZJava的开攑ּ发^収ͼ帮助部分的全文烦引用了Lucene
对于中文用户来说Q最兛_的问题是其是否支持中文的全文索。但通过后面对于Lucene的结构的介绍Q你会了解到׃Lucene良好架构设计Q对中文的支持只需对其语言词法分析接口q行扩展p实现对中文检索的支持?/p>
Lucene的API接口设计的比较通用Q输入输出结构都很像数据库的?=>记录==>字段Q所以很多传l的应用的文件、数据库{都可以比较方便的映到Lucene的存储结?接口中。M上看Q可以先?b>Lucene当成一个支持全文烦引的数据库系l?/b>?/p>
比较一下Lucene和数据库Q?/p>
Lucene | 数据?/td> |
索引数据源:doc(field1,field2...) doc(field1,field2...) |
索引数据源:record(field1,field2...) record(field1..) |
DocumentQ一个需要进行烦引的“单元?br />一个Document由多个字D늻?/td> | RecordQ记录,包含多个字段 |
FieldQ字D?/td> | FieldQ字D?/td> |
HitsQ查询结果集Q由匚w的Documentl成 | RecordSetQ查询结果集Q由多个Recordl成 |
全文??like "%keyword%"
通常比较厚的书籍后面常常附关键词索引表(比如Q北京:12, 34,上vQ?,77……)Q它能够帮助读者比较快地找到相兛_容的늠。而数据库索引能够大大提高查询的速度原理也是一P惛_一下通过书后面的索引查找的速度要比一一地d定w多少倍……而烦引之所以效率高Q另外一个原因是它是排好序的?b>对于索系l来说核心是一个排序问?/b>?/p>
׃数据库烦引不是ؓ全文索引设计的,因此Q?b>使用like "%keyword%"Ӟ数据库烦引是不v作用?/b>Q在使用like查询Ӟ搜烦q程又变成类g一页M的遍历过E了Q所以对于含有模p查询的数据库服务来_LIKEҎ能的危x极大的。如果是需要对多个关键词进行模p匹配:like"%keyword1%" and like "%keyword2%" ...其效率也可惌知了?/p>
所以徏立一个高效检索系l的关键是徏立一个类gU技索引一L反向索引机制Q将数据源(比如多篇文章Q排序顺序存储的同时Q有另外一个排好序的关键词列表Q用于存储关键词==>文章映射关系Q利用这L映射关系索引Q[关键?=>出现关键词的文章~号Q出现次敎ͼ甚至包括位置Qv始偏U量Q结束偏U量Q,出现频率]Q检索过E就是把模糊查询变成多个可以利用索引的精查询的逻辑l合的过E?/b>。从而大大提高了多关键词查询的效率,所以,全文索问题归l到最后是一个排序问题?/p>
由此可以看出模糊查询相对数据库的_查询是一个非怸定的问题,q也是大部分数据库对全文索支持有限的原因。Lucene最核心的特征是通过Ҏ的烦引结构实C传统数据库不擅长的全文烦引机Ӟq提供了扩展接口Q以方便针对不同应用的定制?/p>
可以通过一下表格对比一下数据库的模p查询:
Lucene全文索引引擎
数据?/td>
索引
数据源中的数据都通过全文索引一一建立反向索引
对于LIKE查询来说Q数据传l的索引是根本用不上的。数据需要逐个便利记录q行GREP式的模糊匚wQ比有烦引的搜烦速度要有多个数量U的下降?/td>
匚w效果
通过词元(term)q行匚wQ通过语言分析接口的实玎ͼ可以实现对中文等非英语的支持?/td>
使用Qlike "%net%" 会把netherlands也匹配出来,
多个关键词的模糊匚wQ用like "%com%net%"Q就不能匚w词序颠倒的xxx.net..xxx.com
匚w?/td>
有匹配度法Q将匚wE度Q相似度Q比较高的结果排在前面?/td>
没有匚wE度的控Ӟ比如有记录中net出现5词和出现1ơ的Q结果是一L?/td>
l果输出
通过特别的算法,最匚w度最高的?00条结果输出,l果集是~冲式的批量读取的?/td>
q回所有的l果集,在匹配条目非常多的时候(比如上万条)需要大量的内存存放q些临时l果集?/td>
可定制?/td>
通过不同的语a分析接口实现Q可以方便的定制出符合应用需要的索引规则Q包括对中文的支持)
没有接口或接口复杂,无法定制
l论
高负载的模糊查询应用Q需要负责的模糊查询的规则,索引的资料量比较?/td>
使用率低Q模p匹配规则简单或者需要模p查询的资料量少
全文索和数据库应用最大的不同在于Q让
最相关?/span>
?00条结果满?8%以上用户的需?br />
Lucene的创C处:
大部分的搜烦Q数据库Q引擎都是用B树结构来l护索引Q烦引的更新会导致大量的IO操作QLucene在实CQ对此稍微有所改进Q不是维护一个烦引文Ӟ而是在扩展烦引的时候不断创建新的烦引文Ӟ然后定期的把q些新的烦引文件合q到原先的大索引中(针对不同的更新策略,Ҏ的大可以调_Q这样在不媄响检索的效率的前提下Q提高了索引的效率?/p>
Lucene和其他一些全文检索系l?应用的比较:
Lucene | 其他开源全文检索系l?/td> | |
增量索引和批量烦?/td> | 可以q行增量的烦?Append)Q可以对于大量数据进行批量烦引,q且接口设计用于优化扚w索引和小扚w的增量烦引?/td> | 很多pȝ只支持批量的索引Q有时数据源有一点增加也需要重建烦引?/td> |
数据?/td> | Lucene没有定义具体的数据源Q而是一个文档的l构Q因此可以非常灵zȝ适应各种应用Q只要前端有合适的转换器把数据源{换成相应l构Q, | 很多pȝ只针对网,~Z其他格式文档的灵zL?/td> |
索引内容抓取 | Lucene的文档是由多个字D늻成的Q甚臛_以控刉些字D需要进行烦引,那些字段不需要烦引,q一步烦引的字段也分为需要分词和不需要分词的cdQ?br /> 需要进行分词的索引Q比如:标题Q文章内容字D?br /> 不需要进行分词的索引Q比如:作?日期字段 | ~Z通用性,往往文档整个烦引了 |
语言分析 | 通过语言分析器的不同扩展实现Q?br />可以qo掉不需要的词:an the of {, 西文语法分析Q将jumps jumped jumper都归l成jumpq行索引/?br />非英文支持:对亚z语aQ阿拉伯语言的烦引支?/td> | ~Z通用接口实现 |
查询分析 | 通过查询分析接口的实玎ͼ可以定制自己的查询语法规则: 比如Q?多个关键词之间的 + - and or关系{?/td> | |
q发讉K | 能够支持多用L使用 |
对于中文来说Q全文烦引首先还要解决一个语a分析的问题,对于英文来说Q语句中单词之间是天焉过I格分开的,但亚z语a的中日韩文语句中的字是一个字挨一个,所有,首先要把语句中按“词”进行烦引的话,q个词如何切分出来就是一个很大的问题?/p>
首先Q肯定不能用单个字符?si-gram)为烦引单元,否则查“上”时Q不能让含有“v上”也匚w?/p>
但一句话Q“北京天安门”,计算机如何按照中文的语言习惯q行切分呢?
“北?天安门?q是“北 ?天安门”?让计机能够按照语言习惯q行切分Q往往需要机器有一个比较丰富的词库才能够比较准的识别句中的单词?/p>
另外一个解决的办法是采用自动切分算法:单词按?元语?bigram)方式切分出来Q比如:
"北京天安? ==> "北京 京天 天安 安门"?/p>
q样Q在查询的时候,无论是查?北京" q是查询"天安?Q将查询词组按同L规则q行切分Q?北京"Q?天安安门"Q多个关键词之间按与"and"的关pȝ合,同样能够正确地映到相应的烦引中。这U方式对于其他亚z语aQ韩文,日文都是通用的?/p>
Z自动切分的最大优Ҏ没有词表l护成本Q实现简单,~点是烦引效率低Q但对于中小型应用来_Z2元语法的切分q是够用的。基?元切分后的烦引一般大和源文件差不多Q而对于英文,索引文g一般只有原文g?0%-40%不同Q?/p>
|
自动切分 | 词表切分 |
实现 | 实现非常?/td> | 实现复杂 |
查询 | 增加了查询分析的复杂E度Q?/td> | 适于实现比较复杂的查询语法规?/td> |
存储效率 | 索引冗余大,索引几乎和原文一样大 | 索引效率高,为原文大的30Q左?/td> |
l护成本 | 无词表维护成?/td> | 词表l护成本非常高:中日韩等语言需要分别维护?br />q需要包括词频统计等内容 |
适用领域 | 嵌入式系l:q行环境资源有限 分布式系l:无词表同步问?br />多语a环境Q无词表l护成本 |
Ҏ询和存储效率要求高的专业搜烦引擎 |
目前比较大的搜烦引擎的语a分析法一般是Z以上2个机制的l合。关于中文的语言分析法Q大家可以在Google查关键词"wordsegment search"能找到更多相关的资料?/p>
下蝲Q?a >http://jakarta.apache.org/lucene/
注意QLucene中的一些比较复杂的词法分析是用JavaCC生成的(JavaCCQJavaCompilerCompilerQ纯Java的词法分析生成器Q,所以如果从源代码编译或需要修改其中的QueryParser、定制自q词法分析器,q需要从https://javacc.dev.java.net/下蝲javacc?/p>
lucene的组成结构:对于外部应用来说索引模块(index)和检索模?search)是主要的外部应用入口
org.apache.Lucene.search/ | 搜烦入口 |
org.apache.Lucene.index/ | 索引入口 |
org.apache.Lucene.analysis/ | 语言分析?/td> |
org.apache.Lucene.queryParser/ | 查询分析?/td> |
org.apache.Lucene.document/ | 存储l构 |
org.apache.Lucene.store/ | 底层IO/存储l构 |
org.apache.Lucene.util/ | 一些公用的数据l构 |
单的例子演示一下Lucene的用方法:
索引q程Q从命o行读取文件名Q多个)Q将文g分\?path字段)和内?body字段)2个字D进行存储,q对内容q行全文索引Q烦引的单位是Document对象Q每个Document对象包含多个字段Field对象Q针对不同的字段属性和数据输出的需求,对字D还可以选择不同的烦?存储字段规则Q列表如下:Ҏ | 切词 | 索引 | 存储 | 用?/th> |
---|---|---|---|---|
Field.Text(String name, String value) | Yes | Yes | Yes | 切分词烦引ƈ存储Q比如:标题Q内容字D?/td> |
Field.Text(String name, Reader value) | Yes | Yes | No | 切分词烦引不存储Q比如:META信息Q?br />不用于返回显C,但需要进行检索内?/td> |
Field.Keyword(String name, String value) | No | Yes | Yes | 不切分烦引ƈ存储Q比如:日期字段 |
Field.UnIndexed(String name, String value) | No | No | Yes | 不烦引,只存储,比如Q文件\?/td> |
Field.UnStored(String name, String value) | Yes | Yes | No | 只全文烦引,不存?/td> |
public class IndexFiles {
//使用ҎQ? IndexFiles [索引输出目录] [索引的文件列表] ...
public static void main(String[] args) throws Exception {
String indexPath = args[0];
IndexWriter writer;
//用指定的语言分析器构造一个新的写索引器(W?个参数表C是否ؓq加索引Q?br /> writer = new IndexWriter(indexPath, new SimpleAnalyzer(), false);
for (int i=1; i<args.length; i++) {
System.out.println("Indexing file " + args[i]);
InputStream is = new FileInputStream(args[i]);
//构造包?个字DField的Document对象
//一个是路径path字段Q不索引Q只存储
//一个是内容body字段Q进行全文烦引,q存?br /> Document doc = new Document();
doc.add(Field.UnIndexed("path", args[i]));
doc.add(Field.Text("body", (Reader) new InputStreamReader(is)));
//文档写入烦?br /> writer.addDocument(doc);
is.close();
};
//关闭写烦引器
writer.close();
}
}
索引q程中可以看刎ͼ
索过E和l果昄Q?/p>
搜烦l果q回的是Hits对象Q可以通过它再讉KDocument==>Field中的内容?/p>
假设Ҏbody字段q行全文索,可以查询结果的path字段和相应查询的匚w?score)打印出来Q?/p>
public class Search {在整个检索过E中Q语a分析器,查询分析器,甚至搜烦器(SearcherQ都是提供了抽象的接口,可以Ҏ需要进行定制?
public static void main(String[] args) throws Exception {
String indexPath = args[0], queryString = args[1];
//指向索引目录的搜索器
Searcher searcher = new IndexSearcher(indexPath);
//查询解析器:使用和烦引同L语言分析?br /> Query query = QueryParser.parse(queryString, "body",
new SimpleAnalyzer());
//搜烦l果使用Hits存储
Hits hits = searcher.search(query);
//通过hits可以讉K到相应字D늚数据和查询的匚w?br /> for (int i=0; i<hits.length(); i++) {
System.out.println(hits.doc(i).get("path") + "; Score: " +
hits.score(i));
};
}
}
化的查询分析?/b>
个h感觉lucene成ؓJAKARTA目后,d了太多的旉用于调试日趋复杂QueryParserQ而其中大部分是大多数用户q不很熟悉的Q目前LUCENE支持的语法:
Query ::= ( Clause )*
Clause ::= ["+", "-"] [<TERM> ":"] ( <TERM> | "(" Query ")")
中间的逻辑包括Qand or + - &&||{符P而且q有"短语查询"和针对西文的前缀/模糊查询{,个h感觉对于一般应用来_q些功能有一些华而不实,其实能够实现目前cM于Google的查询语句分析功能其实对于大多数用户来说已经够了。所以,Lucene早期版本的QueryParser仍是比较好的选择?/p>
d修改删除指定记录QDocumentQ?/b>
Lucene提供了烦引的扩展机制Q因此烦引的动态扩展应该是没有问题的,而指定记录的修改也似乎只能通过记录的删除,然后重新加入实现。如何删除指定的记录呢?删除的方法也很简单,只是需要在索引时根据数据源中的记录ID专门另徏索引Q然后利用IndexReader.delete(Termterm)Ҏ通过q个记录ID删除相应的Document?/p>
Ҏ某个字段值的排序功能
lucene~省是按照自q相关度算法(scoreQ进行结果排序的Q但能够Ҏ其他字段q行l果排序是一个在LUCENE的开发邮件列表中l常提到的问题,很多原先Z数据库应用都需要除了基于匹配度QscoreQ以外的排序功能。而从全文索的原理我们可以了解刎ͼM不基于烦引的搜烦q程效率都会D效率非常的低Q如果基于其他字D늚排序需要在搜烦q程中访问存储字D,速度回大大降低,因此非常是不可取的?/p>
但这里也有一个折中的解决ҎQ在搜烦q程中能够媄响排序结果的只有索引中已l存储的docID和scoreq?个参敎ͼ所以,Zscore以外的排序,其实可以通过数据源预先排好序,然后ҎdocIDq行排序来实现。这样就避免了在LUCENE搜烦l果外对l果再次q行排序和在搜烦q程中访问不在烦引中的某个字D倹{?/p>
q里需要修改的是IndexSearcher中的HitCollectorq程Q?/p>
...
scorer.score(new HitCollector() {
private float minScore = 0.0f;
public final void collect(int doc, float score) {
if (score > 0.0f && // ignore zeroed buckets
(bits==null || bits.get(doc))) { // skip docs not in bits
totalHits[0]++;
if (score >= minScore) {
/* 原先QLucenedocID和相应的匚w度score例入l果命中列表中:
* hq.put(new ScoreDoc(doc, score)); // update hit queue
* 如果用doc ?1/doc 代替 scoreQ就实现了根据docID排或逆排
* 假设数据源烦引时已经按照某个字段排好了序Q而结果根据docID排序也就实现?br /> * 针对某个字段的排序,甚至可以实现更复杂的score和docID的拟合?br /> */
hq.put(new ScoreDoc(doc, (float) 1/doc ));
if (hq.size() > nDocs) { // if hit queue overfull
hq.pop(); // remove lowest in hit queue
minScore = ((ScoreDoc)hq.top()).score; // reset minScore
}
}
}
}
}, reader.maxDoc());
更通用的输入输出接?/b>
虽然lucene没有定义一个确定的输入文档格式Q但来多的h惛_使用一个标准的中间格式作ؓLucene的数据导入接口,然后其他数据Q比如PDF只需要通过解析器{换成标准的中间格式就可以q行数据索引了。这个中间格式主要以XMLZQ类似实现已l不?Q?个:
数据? WORD PDF HTML DB other
\ | | | /
XML中间格式
|
Lucene INDEX
目前q没有针对MSWord文档的解析器Q因为Word文档和基于ASCII的RTF文档不同Q需要用COM对象机制解析。这个是我在Google上查的相兌料:http://www.intrinsyc.com/products/enterprise_applications.asp
另外一个办法就是把Word文档转换成textQ?a >http://www.winfield.demon.nl/index.html
索引q程优化
索引一般分2U情况,一U是批量的索引扩展Q一U是大批量的索引重徏。在索引q程中,q不是每ơ新的DOC加入q去索引都重新进行一ơ烦引文件的写入操作Q文件I/O是一仉常消耗资源的事情Q?/p>
Lucene先在内存中进行烦引操作,q根据一定的扚wq行文g的写入。这个批ơ的间隔大Q文件的写入ơ数少Q但占用内存会很多。反之占用内存少Q但文gIO操作频繁Q烦引速度会很慢。在IndexWriter中有一个MERGE_FACTOR参数可以帮助你在构造烦引器后根据应用环境的情况充分利用内存减少文g的操作。根据我的用经验:~省Indexer是每20条记录烦引后写入一ơ,每将MERGE_FACTOR增加50倍,索引速度可以提高1倍左叟?br />
搜烦q程优化
lucene支持内存索引Q这L搜烦比基于文件的I/O有数量的速度提升?br />http://www.onjava.com/lpt/a/3273
而尽可能减少IndexSearcher的创建和Ҏ索结果的前台的缓存也是必要的?br />
Lucene面向全文索的优化在于首次索引索后Qƈ不把所有的记录QDocumentQ具体内容读取出来,而v只将所有结果中匚w度最高的?00条结果(TopDocsQ的ID攑ֈl果集缓存中q返回,q里可以比较一下数据库索:如果是一?0,000条的数据库检索结果集Q数据库是一定要把所有记录内定w取得以后再开始返回给应用l果集的。所以即使检索匹配L很多QLucene的结果集占用的内存空间也不会很多。对于一般的模糊索应用是用不到这么多的结果的Q头100条已l可以满?0%以上的检索需求?br />
如果首批~存l果数用完后q要d更后面的l果时Searcher会再ơ检索ƈ生成一个上ơ的搜烦~存数大1倍的~存Qƈ再重新向后抓取。所以如果构造一个SearcherL1Q?20条结果,Searcher其实是进行了2ơ搜索过E:?00条取完后Q缓存结果用完,Searcher重新索再构造一?00条的l果~存Q依此类推,400条缓存,800条缓存。由于每ơSearcher对象消失后,q些~存也访问那不到了,你有可能惛_l果记录~存下来Q缓存数量保证?00以下以充分利用首ơ的l果~存Q不让Lucene费多次索,而且可以分q行l果~存?br />
Lucene的另外一个特Ҏ在收集结果的q程中将匚w度低的结果自动过滤掉了。这也是和数据库应用需要将搜烦的结果全部返回不同之处?/p>
我的一些尝?/a>Q?/p> Luene的确是一个面对对象设计的典范 q些优点都是非常值得在以后的开发中学习借鉴的。作Z个通用工具包,Lunece的确l予了需要将全文索功能嵌入到应用中的开发者很多的便利?/p> 此外Q通过对Lucene的学习和使用Q我也更深刻地理解了Z么很多数据库优化设计中要求,比如Q?/p> 参考资料: Apache: Lucene Project The Lucene search engine: Powerful, flexible, and free Lucene Tutorial Notes on distributed searching with Lucene 中文语言的切分词 搜烦引擎工具介绍 Lucene作者Cutting的几论文和专利 Lucene?NET实现QdotLucene Lucene作者Cutting的另外一个项目:ZJava的搜索引擎Nutch 关于Z词表和N-Gram的切分词比较
http://jakarta.apache.org/lucene/
Lucene开?用户邮g列表归档
Lucene-dev@jakarta.apache.org
Lucene-user@jakarta.apache.org
http://www.javaworld.com/javaworld/jw-09-2000/jw-0915-Lucene_p.html
http://www.darksleep.com/puff/lucene/lucene.html
http://home.clara.net/markharwood/lucene/
http://www.google.com/search?sourceid=navclient&hl=zh-CN&q=chinese+word+segment
http://searchtools.com/
http://lucene.sourceforge.net/publications.html
http://sourceforge.net/projects/dotlucene/
http://www.nutch.org/ http://sourceforge.net/projects/nutch/
http://china.nikkeibp.co.jp/cgi-bin/china/news/int/int200302100112.html
2005-01-08 Cutting在Pisa大学做的关于Lucene的讲座:非常详细的Lucene架构解说
]]>
Microsoft公司Z么还要添加这个技术呢?
那么存储q程与一般的SQL语句有什么区别呢?
存储q程的优点:
1.存储q程只在创造时q行~译Q以后每ơ执行存储过E都不需再重新编译,而一般SQL语句每执行一ơ就~译一?所以用存储过E可提高数据库执行速度?br /> 2.当对数据库进行复杂操作时(如对多个表进行Update,Insert,Query,DeleteӞQ可此复杂操作用存储过E封装v来与数据库提供的事务处理l合一起用?br /> 3.存储q程可以重复使用,可减数据库开发h员的工作?br /> 4.安全性高,可设定只有某此用hhҎ定存储过E的使用?br /> 存储q程的种c:
1.pȝ存储q程Q以sp_开?用来q行pȝ的各设?取得信息.相关理工作,
如 ?sp_help是取得指定对象的相关信?br /> 2.扩展存储q程 以XP_开?用来调用操作pȝ提供的功?br /> exec master..xp_cmdshell 'ping 10.8.16.1'
3.用户自定义的存储q程,q是我们所指的存储q程
常用格式
Create procedure procedue_name
[@parameter data_type][output]
[with]{recompile|encryption}
as
sql_statement
解释:
outputQ表C此参数是可传回?br /> with {recompile|encryption}
recompile:表示每次执行此存储过E时都重新编译一?br /> encryption:所创徏的存储过E的内容会被加密
?
表book的内容如?br /> ~号 书名 h
001 C语言入门 $30
002 PowerBuilder报表开发 ?$52
实例1:查询表Book的内容的存储q程
create proc query_book
as
select * from book
go
exec query_book
实例2:加入一W记录到表book,q查询此表中所有书c的总金?br /> Create proc insert_book
@param1 char(10),@param2 varchar(20),@param3 money,@param4 money output
with encryption ---------加密
as
insert book(~号,书名Qh| Values(@param1,@param2,@param3)
select @param4=sum(h) from book
go
执行例子:
declare @total_price money
exec insert_book '003','Delphi 控g开发指?,$100,@total_price
print '总金额ؓ'+convert(varchar,@total_price)
go
存储q程?U传回?
1.以Return传回整数
2.以output格式传回参数
3.Recordset
传回值的区别:
output和return都可在批ơ程式中用变量接?而recordset则传回到执行Ҏ的客L中 ?
实例3Q设有两个表为Product,Order,其表内容如下Q?br /> Product
产品~号 产品名称 客户订数
001 钢笔 30
002 毛笔 50
003 铅笔 100
Order
产品~号 客户名 ?客户订金
001 南山区 ?$30
002 |湖区 ?$50
003 宝安区 ?$4
请实现按~号接条?两个表q接成一个时表,该表只含~号.产品?客户?订金.总金?
总金?订金*订数,临时表放在存储过E中
代码如下:
Create proc temp_sale
as
select a.产品~号,a.产品名称,b.客户?b.客户订金,a.客户订数* b.客户订金 as总金?br /> into #temptable from Product a inner join Order b on a.产品~号=b.产品~号
if @@error=0
print 'Good'
else
&n bsp; print 'Fail'
go
存储q程介绍
一、先介绍一下什么是存储q程
存储q程是利用SQL Server所提供的Tranact-SQL语言所~写的程序。Tranact-SQL语言是SQL Server提供专ؓ设计数据库应用程序的语言Q它是应用程序和SQL Server数据库间的主要程序式设计界面。它好比Oracle数据库系l中的Pro-SQL和Informix的数据库pȝ能够中的Informix-4GL语言一栗这c语a主要提供以下功能Q让用户可以设计出符合引用需求的E序Q ?
1)、变量说明 ?
2)、ANSI兼容的SQL命o(如Select,Update?)
3)、一般流E控制命?if…else…、while?)
4)、内部函敊W?
二、存储过E的书写格 ?
CREATE PROCEDURE [拥有?]存储q程名[;E序~号]
[(参数#1,…参?1024)]
[WITH
{RECOMPILE | ENCRYPTION | RECOMPILE, ENCRYPTION}
]
[FOR REPLICATION]
AS E序行 ?
其中存储q程名不能超q?28个字。每个存储过E中最多设?024个参敊W?
(SQL Server 7.0以上版本),参数的用方法如?
@参数名 ?数据cd [VARYING] [=内定值] [OUTPUT]
每个参数名前要有一个“@”符?每一个存储过E的参数仅ؓ该程序内部?参数的类型除了IMAGE外,其他SQL Server所支持的数据类型都可用。 ?
[=内定值]相当于我们在建立数据库时讑֮一个字D늚默认|q里是ؓq个参数讑֮默认倹{[OUTPUT]是用来指定该参数是既有输入又有输出值的Q也是在调用了q个存储q程Ӟ如果所指定的参数值是我们需要输入的参数Q同时也需要在l果中输出的Q则该项必须为OUTPUTQ而如果只是做输出参数用,可以用CURSORQ同时在使用该参数时Q必L定VARYING和OUTPUTq两个语句。 ?
例子:
CREATE PROCEDURE order_tot_amt @o_id int,@p_tot int output AS
SELECT @p_tot = sum(Unitprice*Quantity)
FROM orderdetails
WHERE ordered=@o_id
例子说明:
该例子是建立一个简单的存储q程order_tot_amt,q个存储q程Ҏ用户输入的定单IDL(@o_id),由定单明l表(orderdetails)中计该定单销售总额[单h(Unitprice)*数量(Quantity)],q一金额通过@p_totq一参数输出l调用这一存储q程的程序 ?
三、在SQL Server中执行存储过E ?
在SQL Server的查询分析器中,输入以下代码:
declare @tot_amt int
execute order_tot_amt 1,@tot_amt output
select @tot_amt
以上代码是执行order_tot_amtq一存储q程Q以计算出定单编号ؓ1的定单销售金额,我们定义@tot_amt出参敎ͼ用来承接我们所要的l果
把sql语句输出作了以下的实验,发现是时间多了一?0
后来的办法是先把q个旉转成to_charQ再转成to_date
SQL> select to_date('2005-10-01 12:01:01.0','yyyy-mm-dd hh24:mi:ss') from dual;
select to_date('2005-10-01 12:01:01.0','yyyy-mm-dd hh24:mi:ss') from dual
ORA-01830: 日期格式囄在{换整个输入字W串之前l束
SQL> select to_date('2005-10-01 12:01:01.0','yyyy-mm-dd hh24:mi:sssss') from dual;
select to_date('2005-10-01 12:01:01.0','yyyy-mm-dd hh24:mi:sssss') from dual
ORA-01836: 时与日中的U发生冲H?/font>
SQL> select to_date('2005-10-01 12:01:01.0','yyyy-mm-dd hh24:mi:ss.sssss') from dual;
select to_date('2005-10-01 12:01:01.0','yyyy-mm-dd hh24:mi:ss.sssss') from dual
ORA-01836: 时与日中的U发生冲H?/font>
SQL> select to_date('2005-10-01 12:01:01.0','yyyy-mm-dd hh24:mi:ff') from dual;
select to_date('2005-10-01 12:01:01.0','yyyy-mm-dd hh24:mi:ff') from dual
ORA-01821: 日期格式无法识别
------------------------------------------------------------------
必须保证传入的字W串和要转换的格式精匹?/font>
SQL> SELECT TO_DATE('11-10-1996-13:51:21','DD/MM/YYYY-HH24') A FROM dual;
ERROR:
ORA-01830: date format picture ends before converting entire input string.
SQL> SELECT TO_DATE('11-10-1996-13:51:21','DD/MM/YYYY-HH24:MI:SS') B FROM dual;
Q-Q-Q-Q-Q-Q-Q-Q-Q-Q-Q-Q-Q-Q-Q-Q-Q-Q-Q-
以上是{载的
后来我是q么做的哈:
SELECT中将其他表的日期TO_CHAR下,然后再将值在INSERT时TO_DATE!
具体的做法如下:
SELECT TO_CHAR(parameter,'YYYY-MM-DD HH24:MI:SS') AS TIME
FROM TABLE_NAME_1;
...
...
INSERT INTO TABLE_NAME_2
(COLUME_NAME_1)
VALUE (TO_DATE('"+TIME+"','YYYY-MM-DD HH24:MI:SS'));
然后OK了,呵呵Q看来要学的q真多!
public class EasyConnection implements java.sql.Connection{
private Connection m_delegate = null;
public EasyConnection(){
m_delegate = getConnectionFromPool();
}
public void close(){
putConnectionBackToPool(m_delegate);
}
public PreparedStatement prepareStatement(String sql) throws SQLException{
m_delegate.prepareStatement(sql);
}
//...... other method
}
import java.sql.*;
import java.lang.reflect.*;
import java.util.*;
import java.io.*;
public class SimpleConnetionPool {
private static LinkedList m_notUsedConnection = new LinkedList();
private static HashSet m_usedUsedConnection = new HashSet();
private static String m_url = "";
private static String m_user = "";
private static String m_password = "";
static final boolean DEBUG = true;
static private long m_lastClearClosedConnection = System.currentTimeMillis();
public static long CHECK_CLOSED_CONNECTION_TIME = 4 * 60 * 60 * 1000; //4 hours
static {
initDriver();
}
private SimpleConnetionPool() {
}
private static void initDriver() {
Driver driver = null;
//load mysql driver
try {
driver = (Driver) Class.forName("com.mysql.jdbc.Driver").newInstance();
installDriver(driver);
} catch (Exception e) {
}
//load postgresql driver
try {
driver = (Driver) Class.forName("org.postgresql.Driver").newInstance();
installDriver(driver);
} catch (Exception e) {
}
}
public static void installDriver(Driver driver) {
try {
DriverManager.registerDriver(driver);
} catch (Exception e) {
e.printStackTrace();
}
}
public static synchronized Connection getConnection() {
clearClosedConnection();
while (m_notUsedConnection.size() > 0) {
try {
ConnectionWrapper wrapper = (ConnectionWrapper) m_notUsedConnection.removeFirst();
if (wrapper.connection.isClosed()) {
continue;
}
m_usedUsedConnection.add(wrapper);
if (DEBUG) {
wrapper.debugInfo = new Throwable("Connection initial statement");
}
return wrapper.connection;
} catch (Exception e) {
}
}
int newCount = getIncreasingConnectionCount();
LinkedList list = new LinkedList();
ConnectionWrapper wrapper = null;
for (int i = 0; i < newCount; i++) {
wrapper = getNewConnection();
if (wrapper != null) {
list.add(wrapper);
}
}
if (list.size() == 0) {
return null;
}
wrapper = (ConnectionWrapper) list.removeFirst();
m_usedUsedConnection.add(wrapper);
m_notUsedConnection.addAll(list);
list.clear();
return wrapper.connection;
}
private static ConnectionWrapper getNewConnection() {
try {
Connection con = DriverManager.getConnection(m_url, m_user, m_password);
ConnectionWrapper wrapper = new ConnectionWrapper(con);
return wrapper;
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
static synchronized void pushConnectionBackToPool(ConnectionWrapper con) {
boolean exist = m_usedUsedConnection.remove(con);
if (exist) {
m_notUsedConnection.addLast(con);
}
}
public static int close() {
int count = 0;
Iterator iterator = m_notUsedConnection.iterator();
while (iterator.hasNext()) {
try {
( (ConnectionWrapper) iterator.next()).close();
count++;
} catch (Exception e) {
}
}
m_notUsedConnection.clear();
iterator = m_usedUsedConnection.iterator();
while (iterator.hasNext()) {
try {
ConnectionWrapper wrapper = (ConnectionWrapper) iterator.next();
wrapper.close();
if (DEBUG) {
wrapper.debugInfo.printStackTrace();
}
count++;
} catch (Exception e) {
}
}
m_usedUsedConnection.clear();
return count;
}
private static void clearClosedConnection() {
long time = System.currentTimeMillis();
//sometimes user change system time,just return
if (time < m_lastClearClosedConnection) {
time = m_lastClearClosedConnection;
return;
}
//no need check very often
if (time - m_lastClearClosedConnection < CHECK_CLOSED_CONNECTION_TIME) {
return;
}
m_lastClearClosedConnection = time;
//begin check
Iterator iterator = m_notUsedConnection.iterator();
while (iterator.hasNext()) {
ConnectionWrapper wrapper = (ConnectionWrapper) iterator.next();
try {
if (wrapper.connection.isClosed()) {
iterator.remove();
}
} catch (Exception e) {
iterator.remove();
if (DEBUG) {
System.out.println("connection is closed, this connection initial StackTrace");
wrapper.debugInfo.printStackTrace();
}
}
}
//make connection pool size smaller if too big
int decrease = getDecreasingConnectionCount();
if (m_notUsedConnection.size() < decrease) {
return;
}
while (decrease-- > 0) {
ConnectionWrapper wrapper = (ConnectionWrapper) m_notUsedConnection.removeFirst();
try {
wrapper.connection.close();
} catch (Exception e) {
}
}
}
/**
* get increasing connection count, not just add 1 connection
* @return count
*/
public static int getIncreasingConnectionCount() {
int count = 1;
int current = getConnectionCount();
count = current / 4;
if (count < 1) {
count = 1;
}
return count;
}
/**
* get decreasing connection count, not just remove 1 connection
* @return count
*/
public static int getDecreasingConnectionCount() {
int count = 0;
int current = getConnectionCount();
if (current < 10) {
return 0;
}
return current / 3;
}
public synchronized static void printDebugMsg() {
printDebugMsg(System.out);
}
public synchronized static void printDebugMsg(PrintStream out) {
if (DEBUG == false) {
return;
}
StringBuffer msg = new StringBuffer();
msg.append("debug message in " + SimpleConnetionPool.class.getName());
msg.append("\r\n");
msg.append("total count is connection pool: " + getConnectionCount());
msg.append("\r\n");
msg.append("not used connection count: " + getNotUsedConnectionCount());
msg.append("\r\n");
msg.append("used connection, count: " + getUsedConnectionCount());
out.println(msg);
Iterator iterator = m_usedUsedConnection.iterator();
while (iterator.hasNext()) {
ConnectionWrapper wrapper = (ConnectionWrapper) iterator.next();
wrapper.debugInfo.printStackTrace(out);
}
out.println();
}
public static synchronized int getNotUsedConnectionCount() {
return m_notUsedConnection.size();
}
public static synchronized int getUsedConnectionCount() {
return m_usedUsedConnection.size();
}
public static synchronized int getConnectionCount() {
return m_notUsedConnection.size() + m_usedUsedConnection.size();
}
public static String getUrl() {
return m_url;
}
public static void setUrl(String url) {
if (url == null) {
return;
}
m_url = url.trim();
}
public static String getUser() {
return m_user;
}
public static void setUser(String user) {
if (user == null) {
return;
}
m_user = user.trim();
}
public static String getPassword() {
return m_password;
}
public static void setPassword(String password) {
if (password == null) {
return;
}
m_password = password.trim();
}
}
class ConnectionWrapper implements InvocationHandler {
private final static String CLOSE_METHOD_NAME = "close";
public Connection connection = null;
private Connection m_originConnection = null;
public long lastAccessTime = System.currentTimeMillis();
Throwable debugInfo = new Throwable("Connection initial statement");
ConnectionWrapper(Connection con) {
Class[] interfaces = {java.sql.Connection.class};
this.connection = (Connection) Proxy.newProxyInstance(
con.getClass().getClassLoader(),
interfaces, this);
m_originConnection = con;
}
void close() throws SQLException {
m_originConnection.close();
}
public Object invoke(Object proxy, Method m, Object[] args) throws Throwable {
Object obj = null;
if (CLOSE_METHOD_NAME.equals(m.getName())) {
SimpleConnetionPool.pushConnectionBackToPool(this);
}
else {
obj = m.invoke(m_originConnection, args);
}
lastAccessTime = System.currentTimeMillis();
return obj;
}
}
public class TestConnectionPool{
public static void main(String[] args) {
SimpleConnetionPool.setUrl(DBTools.getDatabaseUrl());
SimpleConnetionPool.setUser(DBTools.getDatabaseUserName());
SimpleConnetionPool.setPassword(DBTools.getDatabasePassword());
Connection con = SimpleConnetionPool.getConnection();
Connection con1 = SimpleConnetionPool.getConnection();
Connection con2 = SimpleConnetionPool.getConnection();
//do something with con ...
try {
con.close();
} catch (Exception e) {}
try {
con1.close();
} catch (Exception e) {}
try {
con2.close();
} catch (Exception e) {}
con = SimpleConnetionPool.getConnection();
con1 = SimpleConnetionPool.getConnection();
try {
con1.close();
} catch (Exception e) {}
con2 = SimpleConnetionPool.getConnection();
SimpleConnetionPool.printDebugMsg();
}
}