﻿<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:trackback="http://madskills.com/public/xml/rss/module/trackback/" xmlns:wfw="http://wellformedweb.org/CommentAPI/" xmlns:slash="http://purl.org/rss/1.0/modules/slash/"><channel><title>BlogJava-weidagang2046的专栏-文章分类-Database</title><link>http://www.blogjava.net/weidagang2046/category/1067.html</link><description>物格而后知致</description><language>zh-cn</language><lastBuildDate>Fri, 02 Mar 2007 07:33:11 GMT</lastBuildDate><pubDate>Fri, 02 Mar 2007 07:33:11 GMT</pubDate><ttl>60</ttl><item><title>在应用中加入全文检索功能——基于Java的全文索引引擎Lucene简介</title><link>http://www.blogjava.net/weidagang2046/articles/87684.html</link><dc:creator>weidagang2046</dc:creator><author>weidagang2046</author><pubDate>Thu, 14 Dec 2006 05:05:00 GMT</pubDate><guid>http://www.blogjava.net/weidagang2046/articles/87684.html</guid><wfw:comment>http://www.blogjava.net/weidagang2046/comments/87684.html</wfw:comment><comments>http://www.blogjava.net/weidagang2046/articles/87684.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/weidagang2046/comments/commentRss/87684.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/weidagang2046/services/trackbacks/87684.html</trackback:ping><description><![CDATA[
		<p>作者： 车东 Email: chedongATbigfoot.com/chedongATchedong.com</p>
		<p>写于：2002/08 最后更新：
<script language="Javascript" src="http://www.chedong.com/referer.js"></script>
 11/29/2006 17:23:30<br /><a href="http://www.chedong.com/tech/comments.php">Feed Back &gt;&gt;</a> (<a href="http://www.linuxforum.net/doc/smartq-grand.html">Read this before you ask question</a>)<a href="http://creativecommons.org/licenses/by-sa/2.5/"><br /><img alt="Creative Commons License" src="http://www.creativecommons.cn/images/public/somerights.gif" border="0" /></a>&lt;广告&gt;</p>
		<p>版权声明：可以任意转载，转载时请务必以超链接形式标明文章原始出处和作者信息及本声明<br /><a href="http://www.chedong.com/tech/lucene.html">http://www.chedong.com/tech/lucene.html</a></p>
		<p>关键词：Lucene java full-text search engine Chinese word segment</p>
		<p>
				<span style="FONT-WEIGHT: bold">
				</span>内容摘要：</p>
		<p>Lucene是一个基于Java的全文索引工具包。</p>
		<ol>
				<li>
						<a href="http://www.chedong.com/tech/lucene.html#intro">基于Java的全文索引引擎Lucene简介：关于作者和Lucene的历史</a>
				</li>
				<li>
						<a href="http://www.chedong.com/tech/lucene.html#compare">全文检索的实现：Luene全文索引和数据库索引的比较</a>
				</li>
				<li>
						<a href="http://www.chedong.com/tech/lucene.html#segment">中文切分词机制简介：基于词库和自动切分词算法的比较</a>
				</li>
				<li>
						<a href="http://www.chedong.com/tech/lucene.html#demo">具体的安装和使用简介：系统结构介绍和演示</a>
				</li>
				<li>
						<a href="http://www.chedong.com/tech/lucene.html#hacking">Hacking Lucene：简化的查询分析器，删除的实现，定制的排序，应用接口的扩展</a>
				</li>
				<li>
						<a href="http://www.chedong.com/tech/lucene.html#learn">从Lucene我们还可以学到什么</a>
				</li>
		</ol>
		<p>
				<a name="intro">
						<b>基于Java的全文索引/检索引擎——Lucene</b>
				</a>
		</p>
		<p>Lucene不是一个完整的全文索引应用，而是是一个用Java写的全文索引引擎工具包，它可以方便的嵌入到各种应用中实现针对应用的全文索引/检索功能。</p>
		<p>Lucene的作者：Lucene的贡献者<a href="http://www.nutch.org/blog/cutting.html">Doug Cutting</a>是一位资深全文索引/检索专家，曾经是V-Twin搜索引擎(Apple的Copland操作系统的成就之一)的主要开发者，后在Excite担任高级系统架构设计师，目前从事于一些INTERNET底层架构的研究。他贡献出的Lucene的目标是为各种中小型应用程序加入全文检索功能。</p>
		<p>Lucene的发展历程：早先发布在作者自己的<a href="http://www.lucene.com/">www.lucene.com</a>，后来发布在<a href="http://sourceforge.net/projects/lucene/">SourceForge</a>，2001年年底成为APACHE基金会jakarta的一个子项目：<a href="http://jakarta.apache.org/lucene/">http://jakarta.apache.org/lucene/</a></p>
		<p>已经有很多Java项目都使用了Lucene作为其后台的全文索引引擎，比较著名的有：</p>
		<ul>
				<li>
						<a href="http://www.jivesoftware.com/">J</a>
						<a href="http://www.jivesoftware.com/">ive</a>：WEB论坛系统； 
</li>
				<li>
						<a href="http://eyebrowse.tigris.org/">Eyebrows</a>：邮件列表HTML归档/浏览/查询系统，本文的主要参考文档“<a href="http://www.javaworld.com/javaworld/jw-09-2000/jw-0915-lucene_p.html">TheLucene search engine: Powerful, flexible, and free</a>”作者就是EyeBrows系统的主要开发者之一，而EyeBrows已经成为目前APACHE项目的主要邮件列表归档系统。 
</li>
				<li>
						<a href="http://xml.apache.org/cocoon/index.html">Cocoon</a>:基于XML的web发布框架，全文检索部分使用了Lucene 
</li>
				<li>
						<p align="left">
								<a href="http://www.eclipse.org/">Eclipse</a>:基于Java的开放开发平台，帮助部分的全文索引使用了Lucene</p>
				</li>
		</ul>
		<p>对于中文用户来说，最关心的问题是其是否支持中文的全文检索。但通过后面对于Lucene的结构的介绍，你会了解到由于Lucene良好架构设计，对中文的支持只需对其语言词法分析接口进行扩展就能实现对中文检索的支持。</p>
		<p>
				<b>
						<a name="compare">全文检索的实现机制</a>
				</b>
		</p>
		<p>Lucene的API接口设计的比较通用，输入输出结构都很像数据库的表==&gt;记录==&gt;字段，所以很多传统的应用的文件、数据库等都可以比较方便的映射到Lucene的存储结构/接口中。总体上看：可以先把<b>Lucene当成一个支持全文索引的数据库系统</b>。</p>
		<p>比较一下Lucene和数据库：</p>
		<table width="100%" border="1">
				<tbody>
						<tr>
								<td align="middle" width="50%">Lucene</td>
								<td align="middle" width="50%">数据库</td>
						</tr>
						<tr>
								<td width="50%">
										<pre>索引数据源：doc(field1,field2...) doc(field1,field2...)<br />                  \  indexer /<br />                 _____________<br />                | Lucene Index|<br />                --------------<br />                 / searcher \<br /> 结果输出：Hits(doc(field1,field2) doc(field1...))</pre>
								</td>
								<td width="50%">
										<pre> 索引数据源：record(field1,field2...) record(field1..)<br />              \  SQL: insert/<br />               _____________<br />              | DB  Index   |<br />               -------------<br />              / SQL: select \<br />结果输出：results(record(field1,field2..) record(field1...))</pre>
								</td>
						</tr>
						<tr>
								<td width="50%">Document：一个需要进行索引的“单元”<br />一个Document由多个字段组成</td>
								<td width="50%">Record：记录，包含多个字段</td>
						</tr>
						<tr>
								<td width="50%">Field：字段</td>
								<td width="50%">Field：字段</td>
						</tr>
						<tr>
								<td width="50%">Hits：查询结果集，由匹配的Document组成</td>
								<td width="50%">RecordSet：查询结果集，由多个Record组成</td>
						</tr>
				</tbody>
		</table>
		<p>
				<b>全文检索 ≠ like "%keyword%"</b>
		</p>
		<p>通常比较厚的书籍后面常常附关键词索引表（比如：北京：12, 34页，上海：3,77页……），它能够帮助读者比较快地找到相关内容的页码。而数据库索引能够大大提高查询的速度原理也是一样，想像一下通过书后面的索引查找的速度要比一页一页地翻内容高多少倍……而索引之所以效率高，另外一个原因是它是排好序的。<b>对于检索系统来说核心是一个排序问题</b>。</p>
		<p align="left">由于数据库索引不是为全文索引设计的，因此，<b>使用like "%keyword%"时，数据库索引是不起作用的</b>，在使用like查询时，搜索过程又变成类似于一页页翻书的遍历过程了，所以对于含有模糊查询的数据库服务来说，LIKE对性能的危害是极大的。如果是需要对多个关键词进行模糊匹配：like"%keyword1%" and like "%keyword2%" ...其效率也就可想而知了。</p>
		<p>所以建立一个高效检索系统的关键是建立一个类似于科技索引一样的反向索引机制，将数据源（比如多篇文章）排序顺序存储的同时，有另外一个排好序的关键词列表，用于存储关键词==&gt;文章映射关系，利用这样的映射关系索引：[关键词==&gt;出现关键词的文章编号，出现次数（甚至包括位置：起始偏移量，结束偏移量），出现频率]，检索过程就是把<b>模糊查询变成多个可以利用索引的精确查询的逻辑组合的过程</b>。从而大大提高了多关键词查询的效率，所以，全文检索问题归结到最后是一个排序问题。</p>
		<p>由此可以看出模糊查询相对数据库的精确查询是一个非常不确定的问题，这也是大部分数据库对全文检索支持有限的原因。Lucene最核心的特征是通过特殊的索引结构实现了传统数据库不擅长的全文索引机制，并提供了扩展接口，以方便针对不同应用的定制。</p>
		<p>可以通过一下表格对比一下数据库的模糊查询：</p>
		<table height="283" width="100%" border="1">
				<tbody>
						<tr>
								<td align="middle" width="9%" height="16">　</td>
								<td align="middle" width="47%" height="16">Lucene全文索引引擎</td>
								<td align="middle" width="40%" height="16">数据库</td>
						</tr>
						<tr>
								<td width="9%" height="48">索引</td>
								<td width="47%" height="48">将数据源中的数据都通过全文索引一一建立反向索引</td>
								<td width="40%" height="48">对于LIKE查询来说，数据传统的索引是根本用不上的。数据需要逐个便利记录进行GREP式的模糊匹配，比有索引的搜索速度要有多个数量级的下降。</td>
						</tr>
						<tr>
								<td width="9%" height="49">匹配效果</td>
								<td width="47%" height="49">通过词元(term)进行匹配，通过语言分析接口的实现，可以实现对中文等非英语的支持。</td>
								<td width="40%" height="49">使用：like "%net%" 会把netherlands也匹配出来，<br />多个关键词的模糊匹配：使用like "%com%net%"：就不能匹配词序颠倒的xxx.net..xxx.com</td>
						</tr>
						<tr>
								<td width="9%" height="32">匹配度</td>
								<td width="47%" height="32">有匹配度算法，将匹配程度（相似度）比较高的结果排在前面。</td>
								<td width="40%" height="32">没有匹配程度的控制：比如有记录中net出现5词和出现1次的，结果是一样的。</td>
						</tr>
						<tr>
								<td width="9%" height="32">结果输出</td>
								<td width="47%" height="32">通过特别的算法，将最匹配度最高的头100条结果输出，结果集是缓冲式的小批量读取的。</td>
								<td width="40%" height="32">返回所有的结果集，在匹配条目非常多的时候（比如上万条）需要大量的内存存放这些临时结果集。</td>
						</tr>
						<tr>
								<td width="9%" height="32">可定制性</td>
								<td width="47%" height="32">通过不同的语言分析接口实现，可以方便的定制出符合应用需要的索引规则（包括对中文的支持）</td>
								<td width="40%" height="32">没有接口或接口复杂，无法定制</td>
						</tr>
						<tr>
								<td width="9%" height="32">结论</td>
								<td width="47%" height="32">高负载的模糊查询应用，需要负责的模糊查询的规则，索引的资料量比较大</td>
								<td width="40%" height="32">使用率低，模糊匹配规则简单或者需要模糊查询的资料量少</td>
						</tr>
				</tbody>
		</table>
		<p>
				<span style="FONT-WEIGHT: bold">全文检索和数据库应用最大的不同在于：让</span>
				<span style="FONT-WEIGHT: bold">最相关的</span>
				<span style="FONT-WEIGHT: bold">头100条结果满足98%以上用户的需求<br /></span>
				<br />Lucene的创新之处：</p>
		<p>大部分的搜索（数据库）引擎都是用B树结构来维护索引，索引的更新会导致大量的IO操作，Lucene在实现中，对此稍微有所改进：不是维护一个索引文件，而是在扩展索引的时候不断创建新的索引文件，然后定期的把这些新的小索引文件合并到原先的大索引中（针对不同的更新策略，批次的大小可以调整），这样在不影响检索的效率的前提下，提高了索引的效率。</p>
		<p>Lucene和其他一些全文检索系统/应用的比较：</p>
		<table width="100%" border="1">
				<tbody>
						<tr>
								<td align="middle" width="18%">　</td>
								<td align="middle" width="45%">Lucene</td>
								<td align="middle" width="37%">其他开源全文检索系统</td>
						</tr>
						<tr>
								<td width="18%">增量索引和批量索引</td>
								<td width="45%">可以进行增量的索引(Append)，可以对于大量数据进行批量索引，并且接口设计用于优化批量索引和小批量的增量索引。</td>
								<td width="37%">很多系统只支持批量的索引，有时数据源有一点增加也需要重建索引。</td>
						</tr>
						<tr>
								<td width="18%">数据源</td>
								<td width="45%">Lucene没有定义具体的数据源，而是一个文档的结构，因此可以非常灵活的适应各种应用（只要前端有合适的转换器把数据源转换成相应结构），</td>
								<td width="37%">很多系统只针对网页，缺乏其他格式文档的灵活性。</td>
						</tr>
						<tr>
								<td width="18%">索引内容抓取</td>
								<td width="45%">Lucene的文档是由多个字段组成的，甚至可以控制那些字段需要进行索引，那些字段不需要索引，近一步索引的字段也分为需要分词和不需要分词的类型：<br />   需要进行分词的索引，比如：标题，文章内容字段<br />   不需要进行分词的索引，比如：作者/日期字段</td>
								<td width="37%">缺乏通用性，往往将文档整个索引了</td>
						</tr>
						<tr>
								<td width="18%">语言分析</td>
								<td width="45%">通过语言分析器的不同扩展实现：<br />可以过滤掉不需要的词：an the of 等，<br />西文语法分析：将jumps jumped jumper都归结成jump进行索引/检索<br />非英文支持：对亚洲语言，阿拉伯语言的索引支持</td>
								<td width="37%">缺乏通用接口实现</td>
						</tr>
						<tr>
								<td width="18%">查询分析</td>
								<td width="45%">通过查询分析接口的实现，可以定制自己的查询语法规则：<br />比如： 多个关键词之间的 + - and or关系等</td>
								<td width="37%">　</td>
						</tr>
						<tr>
								<td width="18%">并发访问</td>
								<td width="45%">能够支持多用户的使用</td>
								<td width="37%">　</td>
						</tr>
				</tbody>
		</table>
		<p>　</p>
		<p>
				<b>
						<a name="segment">关于亚洲语言的的切分词问题(Word Segment)</a>
				</b>
		</p>
		<p>对于中文来说，全文索引首先还要解决一个语言分析的问题，对于英文来说，语句中单词之间是天然通过空格分开的，但亚洲语言的中日韩文语句中的字是一个字挨一个，所有，首先要把语句中按“词”进行索引的话，这个词如何切分出来就是一个很大的问题。</p>
		<p>首先，肯定不能用单个字符作(si-gram)为索引单元，否则查“上海”时，不能让含有“海上”也匹配。</p>
		<p>但一句话：“北京天安门”，计算机如何按照中文的语言习惯进行切分呢？<br />“北京 天安门” 还是“北 京 天安门”？让计算机能够按照语言习惯进行切分，往往需要机器有一个比较丰富的词库才能够比较准确的识别出语句中的单词。</p>
		<p>另外一个解决的办法是采用自动切分算法：将单词按照2元语法(bigram)方式切分出来，比如：<br />"北京天安门" ==&gt; "北京 京天 天安 安门"。</p>
		<p>这样，在查询的时候，无论是查询"北京" 还是查询"天安门"，将查询词组按同样的规则进行切分："北京"，"天安安门"，多个关键词之间按与"and"的关系组合，同样能够正确地映射到相应的索引中。这种方式对于其他亚洲语言：韩文，日文都是通用的。</p>
		<p>基于自动切分的最大优点是没有词表维护成本，实现简单，缺点是索引效率低，但对于中小型应用来说，基于2元语法的切分还是够用的。基于2元切分后的索引一般大小和源文件差不多，而对于英文，索引文件一般只有原文件的30%-40%不同，</p>
		<table height="68" width="100%" border="1">
				<tbody>
						<tr>
								<td align="middle" width="11%" height="18">
										<br />
								</td>
								<td align="middle" width="39%" height="18">自动切分</td>
								<td align="middle" width="50%" height="18">词表切分</td>
						</tr>
						<tr>
								<td width="11%" height="16">实现</td>
								<td width="39%" height="16">实现非常简单</td>
								<td width="50%" height="16">实现复杂</td>
						</tr>
						<tr>
								<td width="11%" height="16">查询</td>
								<td width="39%" height="16">增加了查询分析的复杂程度，</td>
								<td width="50%" height="16">适于实现比较复杂的查询语法规则</td>
						</tr>
						<tr>
								<td width="11%" height="16">存储效率</td>
								<td width="39%" height="16">索引冗余大，索引几乎和原文一样大</td>
								<td width="50%" height="16">索引效率高，为原文大小的30％左右</td>
						</tr>
						<tr>
								<td width="11%" height="16">维护成本</td>
								<td width="39%" height="16">无词表维护成本</td>
								<td width="50%" height="16">词表维护成本非常高：中日韩等语言需要分别维护。<br />还需要包括词频统计等内容</td>
						</tr>
						<tr>
								<td width="11%" height="16">适用领域</td>
								<td width="39%" height="16">嵌入式系统：运行环境资源有限<br />分布式系统：无词表同步问题<br />多语言环境：无词表维护成本</td>
								<td width="50%" height="16">对查询和存储效率要求高的专业搜索引擎<br /></td>
						</tr>
				</tbody>
		</table>
		<p>目前比较大的搜索引擎的语言分析算法一般是基于以上2个机制的结合。关于中文的语言分析算法，大家可以在Google查关键词"wordsegment search"能找到更多相关的资料。</p>
		<p>
				<a name="demo">
						<b>安装和使用</b>
				</a>
		</p>
		<p>下载：<a href="http://jakarta.apache.org/lucene/">http://jakarta.apache.org/lucene/</a></p>
		<p>注意：Lucene中的一些比较复杂的词法分析是用JavaCC生成的（JavaCC：JavaCompilerCompiler，纯Java的词法分析生成器），所以如果从源代码编译或需要修改其中的QueryParser、定制自己的词法分析器，还需要从<a href="https://javacc.dev.java.net/">https://javacc.dev.java.net/</a>下载javacc。</p>
		<p>lucene的组成结构：对于外部应用来说索引模块(index)和检索模块(search)是主要的外部应用入口</p>
		<table width="100%" border="1">
				<tbody>
						<tr>
								<td width="27%">org.apache.Lucene.search/</td>
								<td width="73%">搜索入口</td>
						</tr>
						<tr>
								<td width="27%">org.apache.Lucene.index/</td>
								<td width="73%">索引入口</td>
						</tr>
						<tr>
								<td width="27%">org.apache.Lucene.analysis/</td>
								<td width="73%">语言分析器</td>
						</tr>
						<tr>
								<td width="27%">org.apache.Lucene.queryParser/</td>
								<td width="73%">查询分析器</td>
						</tr>
						<tr>
								<td width="27%">org.apache.Lucene.document/</td>
								<td width="73%">存储结构</td>
						</tr>
						<tr>
								<td width="27%">org.apache.Lucene.store/ </td>
								<td width="73%">底层IO/存储结构</td>
						</tr>
						<tr>
								<td width="27%">org.apache.Lucene.util/</td>
								<td width="73%">一些公用的数据结构</td>
						</tr>
				</tbody>
		</table>
		<p>简单的例子演示一下Lucene的使用方法：</p>索引过程：从命令行读取文件名（多个），将文件分路径(path字段)和内容(body字段)2个字段进行存储，并对内容进行全文索引：索引的单位是Document对象，每个Document对象包含多个字段Field对象，针对不同的字段属性和数据输出的需求，对字段还可以选择不同的索引/存储字段规则，列表如下： 
<table border="1"><tbody><tr><th>方法</th><th>切词</th><th>索引</th><th>存储</th><th>用途</th></tr><tr><td>Field.Text(String name, String value)</td><td>Yes</td><td>Yes</td><td>Yes</td><td valign="top">切分词索引并存储，比如：标题，内容字段</td></tr><tr><td>Field.Text(String name, Reader value)</td><td>Yes</td><td>Yes</td><td>No</td><td valign="top">切分词索引不存储，比如：META信息，<br />不用于返回显示，但需要进行检索内容</td></tr><tr><td>Field.Keyword(String name, String value)</td><td>No</td><td>Yes</td><td>Yes</td><td valign="top">不切分索引并存储，比如：日期字段</td></tr><tr><td>Field.UnIndexed(String name, String value)</td><td>No</td><td>No</td><td>Yes</td><td valign="top">不索引，只存储，比如：文件路径</td></tr><tr><td>Field.UnStored(String name, String value)</td><td>Yes</td><td>Yes</td><td>No</td><td valign="top">只全文索引，不存储</td></tr></tbody></table><pre>public class IndexFiles { <br />  //使用方法：: IndexFiles [索引输出目录] [索引的文件列表] ... <br />  public static void main(String[] args) throws Exception {<br />    String indexPath = args[0];<br />    IndexWriter writer;<br />    //用指定的语言分析器构造一个新的写索引器（第3个参数表示是否为追加索引）<br />    writer = new IndexWriter(indexPath, new SimpleAnalyzer(), false);<br /><br />    for (int i=1; i&lt;args.length; i++) {<br />      System.out.println("Indexing file " + args[i]);<br />      InputStream is = new FileInputStream(args[i]);<br /><br />      //构造包含2个字段Field的Document对象<br />      //一个是路径path字段，不索引，只存储<br />      //一个是内容body字段，进行全文索引，并存储<br />      Document doc = new Document();<br />      doc.add(Field.UnIndexed("path", args[i]));<br />      doc.add(Field.Text("body", (Reader) new InputStreamReader(is)));<br />      //将文档写入索引<br />      writer.addDocument(doc);<br />      is.close();<br />    };<br />    //关闭写索引器<br />    writer.close();<br />  }<br />}<br />　</pre><p>索引过程中可以看到：</p><ul><li>语言分析器提供了抽象的接口，因此语言分析(Analyser)是可以定制的，虽然lucene缺省提供了2个比较通用的分析器SimpleAnalyser和StandardAnalyser，这2个分析器缺省都不支持中文，所以要加入对中文语言的切分规则，需要修改这2个分析器。 
</li><li>Lucene并没有规定数据源的格式，而只提供了一个通用的结构（Document对象）来接受索引的输入，因此输入的数据源可以是：数据库，WORD文档，PDF文档，HTML文档……只要能够设计相应的解析转换器将数据源构造成成Docuement对象即可进行索引。 
</li><li>对于大批量的数据索引，还可以通过调整IndexerWrite的文件合并频率属性（mergeFactor）来提高批量索引的效率。 </li></ul><p>检索过程和结果显示：</p><p>搜索结果返回的是Hits对象，可以通过它再访问Document==&gt;Field中的内容。</p><p>假设根据body字段进行全文检索，可以将查询结果的path字段和相应查询的匹配度(score)打印出来，</p><pre>public class Search { <br />  public static void main(String[] args) throws Exception {<br />    String indexPath = args[0], queryString = args[1];<br />    //指向索引目录的搜索器<br />    Searcher searcher = new IndexSearcher(indexPath);<br />    //查询解析器：使用和索引同样的语言分析器<br />    Query query = QueryParser.parse(queryString, "body", <br />                              new SimpleAnalyzer());<br />    //搜索结果使用Hits存储<br />    Hits hits = searcher.search(query);<br />    //通过hits可以访问到相应字段的数据和查询的匹配度<br />    for (int i=0; i&lt;hits.length(); i++) {<br />      System.out.println(hits.doc(i).get("path") + "; Score: " + <br />                         hits.score(i));<br />    };<br />  }<br />}</pre>在整个检索过程中，语言分析器，查询分析器，甚至搜索器（Searcher）都是提供了抽象的接口，可以根据需要进行定制。 
<p><b><a name="hacking">Hacking Lucene</a></b></p><p><b>简化的查询分析器</b></p><p>个人感觉lucene成为JAKARTA项目后，画在了太多的时间用于调试日趋复杂QueryParser，而其中大部分是大多数用户并不很熟悉的，目前LUCENE支持的语法：</p><p>Query ::= ( Clause )*<br />Clause ::= ["+", "-"] [&lt;TERM&gt; ":"] ( &lt;TERM&gt; | "(" Query ")")</p><p>中间的逻辑包括：and or + - &amp;&amp;||等符号，而且还有"短语查询"和针对西文的前缀/模糊查询等，个人感觉对于一般应用来说，这些功能有一些华而不实，其实能够实现目前类似于Google的查询语句分析功能其实对于大多数用户来说已经够了。所以，Lucene早期版本的QueryParser仍是比较好的选择。</p><p><b>添加修改删除指定记录（Document）</b></p><p>Lucene提供了索引的扩展机制，因此索引的动态扩展应该是没有问题的，而指定记录的修改也似乎只能通过记录的删除，然后重新加入实现。如何删除指定的记录呢？删除的方法也很简单，只是需要在索引时根据数据源中的记录ID专门另建索引，然后利用IndexReader.delete(Termterm)方法通过这个记录ID删除相应的Document。</p><p><b>根据某个字段值的排序功能</b></p><p>lucene缺省是按照自己的相关度算法（score）进行结果排序的，但能够根据其他字段进行结果排序是一个在LUCENE的开发邮件列表中经常提到的问题，很多原先基于数据库应用都需要除了基于匹配度（score）以外的排序功能。而从全文检索的原理我们可以了解到，任何不基于索引的搜索过程效率都会导致效率非常的低，如果基于其他字段的排序需要在搜索过程中访问存储字段，速度回大大降低，因此非常是不可取的。</p><p>但这里也有一个折中的解决方法：在搜索过程中能够影响排序结果的只有索引中已经存储的docID和score这2个参数，所以，基于score以外的排序，其实可以通过将数据源预先排好序，然后根据docID进行排序来实现。这样就避免了在LUCENE搜索结果外对结果再次进行排序和在搜索过程中访问不在索引中的某个字段值。</p><p>这里需要修改的是IndexSearcher中的HitCollector过程：</p><pre>...<br />　scorer.score(new HitCollector() {<br />	private float minScore = 0.0f;<br />	public final void collect(int doc, float score) {<br />	  if (score &gt; 0.0f &amp;&amp;			  // ignore zeroed buckets<br />	      (bits==null || bits.get(doc))) {	  // skip docs not in bits<br />	    totalHits[0]++;<br />	    if (score &gt;= minScore) {<br />              /* 原先：Lucene将docID和相应的匹配度score例入结果命中列表中：<br />	       * hq.put(new ScoreDoc(doc, score));	  // update hit queue<br />               * 如果用doc 或 1/doc 代替 score，就实现了根据docID顺排或逆排<br />               * 假设数据源索引时已经按照某个字段排好了序，而结果根据docID排序也就实现了<br />               * 针对某个字段的排序，甚至可以实现更复杂的score和docID的拟合。<br />               */<br />              hq.put(new ScoreDoc(doc, (float) 1/doc )); <br />	      if (hq.size() &gt; nDocs) {		  // if hit queue overfull<br />		hq.pop();			  // remove lowest in hit queue<br />		minScore = ((ScoreDoc)hq.top()).score; // reset minScore<br />	      }<br />	    }<br />	  }<br />	}<br />      }, reader.maxDoc());</pre><p><b>更通用的输入输出接口</b></p><p>虽然lucene没有定义一个确定的输入文档格式，但越来越多的人想到使用一个标准的中间格式作为Lucene的数据导入接口，然后其他数据，比如PDF只需要通过解析器转换成标准的中间格式就可以进行数据索引了。这个中间格式主要以XML为主，类似实现已经不下4，5个：</p><pre>数据源: WORD       PDF     HTML    DB       other<br />         \          |       |      |         /<br />                       XML中间格式<br />                            |<br />                     Lucene INDEX</pre><p>目前还没有针对MSWord文档的解析器，因为Word文档和基于ASCII的RTF文档不同，需要使用COM对象机制解析。这个是我在Google上查的相关资料：<a href="http://www.intrinsyc.com/products/enterprise_applications.asp">http://www.intrinsyc.com/products/enterprise_applications.asp</a><br />另外一个办法就是把Word文档转换成text：<a href="http://www.winfield.demon.nl/index.html">http://www.winfield.demon.nl/index.html</a><br /></p><p><br /><b>索引过程优化</b></p><p>索引一般分2种情况，一种是小批量的索引扩展，一种是大批量的索引重建。在索引过程中，并不是每次新的DOC加入进去索引都重新进行一次索引文件的写入操作（文件I/O是一件非常消耗资源的事情）。</p><p>Lucene先在内存中进行索引操作，并根据一定的批量进行文件的写入。这个批次的间隔越大，文件的写入次数越少，但占用内存会很多。反之占用内存少，但文件IO操作频繁，索引速度会很慢。在IndexWriter中有一个MERGE_FACTOR参数可以帮助你在构造索引器后根据应用环境的情况充分利用内存减少文件的操作。根据我的使用经验：缺省Indexer是每20条记录索引后写入一次，每将MERGE_FACTOR增加50倍，索引速度可以提高1倍左右。<br /></p><p><span style="FONT-WEIGHT: bold">搜索过程优化<br /></span></p><p><span style="FONT-WEIGHT: bold"></span>lucene支持内存索引：这样的搜索比基于文件的I/O有数量级的速度提升。<br /><a href="http://www.onjava.com/lpt/a/3273">http://www.onjava.com/lpt/a/3273</a><br />而尽可能减少IndexSearcher的创建和对搜索结果的前台的缓存也是必要的。<br /><span style="FONT-WEIGHT: bold"></span></p><p><b></b></p><p>Lucene面向全文检索的优化在于首次索引检索后，并不把所有的记录（Document）具体内容读取出来，而起只将所有结果中匹配度最高的头100条结果（TopDocs）的ID放到结果集缓存中并返回，这里可以比较一下数据库检索：如果是一个10,000条的数据库检索结果集，数据库是一定要把所有记录内容都取得以后再开始返回给应用结果集的。所以即使检索匹配总数很多，Lucene的结果集占用的内存空间也不会很多。对于一般的模糊检索应用是用不到这么多的结果的，头100条已经可以满足90%以上的检索需求。<br /></p><p>如果首批缓存结果数用完后还要读取更后面的结果时Searcher会再次检索并生成一个上次的搜索缓存数大1倍的缓存，并再重新向后抓取。所以如果构造一个Searcher去查1－120条结果，Searcher其实是进行了2次搜索过程：头100条取完后，缓存结果用完，Searcher重新检索再构造一个200条的结果缓存，依此类推，400条缓存，800条缓存。由于每次Searcher对象消失后，这些缓存也访问那不到了，你有可能想将结果记录缓存下来，缓存数尽量保证在100以下以充分利用首次的结果缓存，不让Lucene浪费多次检索，而且可以分级进行结果缓存。<br /></p><p>Lucene的另外一个特点是在收集结果的过程中将匹配度低的结果自动过滤掉了。这也是和数据库应用需要将搜索的结果全部返回不同之处。</p><p><a href="http://sourceforge.net/projects/weblucene/">我的一些尝试</a>：</p><ul><li>支持中文的Tokenizer：这里有2个版本，一个是通过JavaCC生成的，对CJK部分按一个字符一个TOKEN索引，另外一个是从SimpleTokenizer改写的，对英文支持数字和字母TOKEN，对中文按迭代索引。 
</li><li>基于XML数据源的索引器：XMLIndexer，因此所有数据源只要能够按照DTD转换成指定的XML，就可以用XMLIndxer进行索引了。 
</li><li>根据某个字段排序：按记录索引顺序排序结果的搜索器：IndexOrderSearcher，因此如果需要让搜索结果根据某个字段排序，可以让数据源先按某个字段排好序（比如：PriceField），这样索引后，然后在利用这个按记录的ID顺序检索的搜索器，结果就是相当于是那个字段排序的结果了。 </li></ul><p><a name="learn"><b>从Lucene学到更多</b></a></p><p>Luene的确是一个面对对象设计的典范</p><ul><li>所有的问题都通过一个额外抽象层来方便以后的扩展和重用：你可以通过重新实现来达到自己的目的，而对其他模块而不需要； 
</li><li>简单的应用入口Searcher, Indexer，并调用底层一系列组件协同的完成搜索任务； 
</li><li>所有的对象的任务都非常专一：比如搜索过程：QueryParser分析将查询语句转换成一系列的精确查询的组合(Query),通过底层的索引读取结构IndexReader进行索引的读取，并用相应的打分器给搜索结果进行打分/排序等。所有的功能模块原子化程度非常高，因此可以通过重新实现而不需要修改其他模块。  
</li><li>除了灵活的应用接口设计，Lucene还提供了一些适合大多数应用的语言分析器实现（SimpleAnalyser,StandardAnalyser），这也是新用户能够很快上手的重要原因之一。 </li></ul><p>这些优点都是非常值得在以后的开发中学习借鉴的。作为一个通用工具包，Lunece的确给予了需要将全文检索功能嵌入到应用中的开发者很多的便利。</p><p>此外，通过对Lucene的学习和使用，我也更深刻地理解了为什么很多数据库优化设计中要求，比如：</p><ul><li>尽可能对字段进行索引来提高查询速度，但过多的索引会对数据库表的更新操作变慢，而对结果过多的排序条件，实际上往往也是性能的杀手之一。 
</li><li>很多商业数据库对大批量的数据插入操作会提供一些优化参数，这个作用和索引器的merge_factor的作用是类似的， 
</li><li>20%/80%原则：查的结果多并不等于质量好，尤其对于返回结果集很大，如何优化这头几十条结果的质量往往才是最重要的。 
</li><li>尽可能让应用从数据库中获得比较小的结果集，因为即使对于大型数据库，对结果集的随机访问也是一个非常消耗资源的操作。<br /></li></ul><p>参考资料：</p><p>Apache: Lucene Project<br /><a href="http://jakarta.apache.org/lucene/">http://jakarta.apache.org/lucene/<br /></a>Lucene开发/用户邮件列表归档<br /><a href="http://www.mail-archive.com/lucene-dev@jakarta.apache.org/">Lucene-dev@jakarta.apache.org</a><br /><a href="http://www.mail-archive.com/lucene-user@jakarta.apache.org/">Lucene-user@jakarta.apache.org</a></p><p>The Lucene search engine: Powerful, flexible, and free<br /><a href="http://www.javaworld.com/javaworld/jw-09-2000/jw-0915-lucene_p.html">http://www.javaworld.com/javaworld/jw-09-2000/jw-0915-Lucene_p.html</a></p><p>Lucene Tutorial<br /><a href="http://www.darksleep.com/puff/lucene/lucene.html">http://www.darksleep.com/puff/lucene/lucene.html</a></p><p>Notes on distributed searching with Lucene<br /><a href="http://home.clara.net/markharwood/lucene/">http://home.clara.net/markharwood/lucene/</a></p><p>中文语言的切分词<br /><a href="http://www.google.com/search?sourceid=navclient&amp;hl=zh-CN&amp;q=chinese+word+segment">http://www.google.com/search?sourceid=navclient&amp;hl=zh-CN&amp;q=chinese+word+segment</a></p><p>搜索引擎工具介绍<a href="http://searchtools.com/"><br />http://searchtools.com/</a></p><p>Lucene作者Cutting的几篇论文和专利<br /><a href="http://lucene.sourceforge.net/publications.html">http://lucene.sourceforge.net/publications.html</a> </p><p>Lucene的.NET实现：dotLucene<br /><a href="http://sourceforge.net/projects/dotlucene/">http://sourceforge.net/projects/dotlucene/<br /></a></p><p>Lucene作者Cutting的另外一个项目：基于Java的搜索引擎Nutch<br /><a href="http://www.nutch.org/">http://www.nutch.org/</a>   <a href="http://sourceforge.net/projects/nutch/">http://sourceforge.net/projects/nutch/<br /></a></p><p>关于基于词表和N-Gram的切分词比较<br /><a href="http://china.nikkeibp.co.jp/cgi-bin/china/news/int/int200302100112.html">http://china.nikkeibp.co.jp/cgi-bin/china/news/int/int200302100112.html</a><br /><br />2005-01-08 <a href="http://lucene.sourceforge.net/talks/pisa/">Cutting在Pisa大学做的关于Lucene的讲座：非常详细的Lucene架构解说</a></p><p>特别感谢：<br /><a href="http://www.google.com/search?q=%22Jack+Xu%22+Excite">前网易CTO许良杰(Jack Xu)</a>给我的指导：是您将我带入了搜索引擎这个行业。</p>原文出处：&lt;ahref="http://www.chedong.com/tech/lucene.html"&gt;http://www.chedong.com/tech/lucene.html&lt;/a&gt;<br /><br />from: <img src ="http://www.blogjava.net/weidagang2046/aggbug/87684.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/weidagang2046/" target="_blank">weidagang2046</a> 2006-12-14 13:05 <a href="http://www.blogjava.net/weidagang2046/articles/87684.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>深入浅出理解索引结构 </title><link>http://www.blogjava.net/weidagang2046/articles/86464.html</link><dc:creator>weidagang2046</dc:creator><author>weidagang2046</author><pubDate>Fri, 08 Dec 2006 14:01:00 GMT</pubDate><guid>http://www.blogjava.net/weidagang2046/articles/86464.html</guid><wfw:comment>http://www.blogjava.net/weidagang2046/comments/86464.html</wfw:comment><comments>http://www.blogjava.net/weidagang2046/articles/86464.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/weidagang2046/comments/commentRss/86464.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/weidagang2046/services/trackbacks/86464.html</trackback:ping><description><![CDATA[&nbsp;&nbsp;&nbsp;&nbsp; 摘要: （一）深入浅出理解索引结构																								        实际上，您可以把索引理解为一种特殊的目录。微软的						SQL SERVER						提供了两种索引：聚集索引（						clustered index						，也称聚类索引、簇集索引）和非聚集索引（						nonclustered index	...&nbsp;&nbsp;<a href='http://www.blogjava.net/weidagang2046/articles/86464.html'>阅读全文</a><img src ="http://www.blogjava.net/weidagang2046/aggbug/86464.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/weidagang2046/" target="_blank">weidagang2046</a> 2006-12-08 22:01 <a href="http://www.blogjava.net/weidagang2046/articles/86464.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>提高查询速度方法总结 </title><link>http://www.blogjava.net/weidagang2046/articles/86461.html</link><dc:creator>weidagang2046</dc:creator><author>weidagang2046</author><pubDate>Fri, 08 Dec 2006 13:55:00 GMT</pubDate><guid>http://www.blogjava.net/weidagang2046/articles/86461.html</guid><wfw:comment>http://www.blogjava.net/weidagang2046/comments/86461.html</wfw:comment><comments>http://www.blogjava.net/weidagang2046/articles/86461.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/weidagang2046/comments/commentRss/86461.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/weidagang2046/services/trackbacks/86461.html</trackback:ping><description><![CDATA[
		<p>这个帖子主要总结提高查询速度的方法，涉及到减少连接数据库次数、建立索引、优化语句等方面。</p>
		<p>关于索引，推荐转载的这篇文章<br /><a href="http://blog.csdn.net/dutguoyi/archive/2006/01/10/575617.aspx">http://blog.csdn.net/dutguoyi/archive/2006/01/10/575617.aspx</a></p>
		<p>改善SQL语句的效率<br /><a href="http://community.csdn.net/Expert/topic/5087/5087396.xml?temp=.345669">http://community.csdn.net/Expert/topic/5087/5087396.xml?temp=.345669</a><br />数据量很大怎样加快索检速度<br /><a href="http://community.csdn.net/Expert/topic/5058/5058320.xml?temp=.1229517">http://community.csdn.net/Expert/topic/5058/5058320.xml?temp=.1229517</a><br />索引建立方法的区别<br /><a href="http://community.csdn.net/Expert/topic/5068/5068154.xml?temp=.3010218">http://community.csdn.net/Expert/topic/5068/5068154.xml?temp=.3010218</a><br />频繁插入删除数据需要更新索引<br /><a href="http://community.csdn.net/Expert/topic/4937/4937910.xml?temp=.8428614">http://community.csdn.net/Expert/topic/4937/4937910.xml?temp=.8428614</a><br />测试了一下sql server 2005 全文检索<br /><a href="http://community.csdn.net/Expert/topic/4878/4878430.xml?temp=.6049311">http://community.csdn.net/Expert/topic/4878/4878430.xml?temp=.6049311</a></p>
		<p>其他关于效率的高频问题</p>
		<p>判断一个表的数据不在另一个表中最优秀方法？<br /><a href="http://community.csdn.net/Expert/topic/5038/5038742.xml?temp=.4704553">http://community.csdn.net/Expert/topic/5038/5038742.xml?temp=.4704553</a><br />删除千万级表中重复记录的办法<br /><a href="http://community.csdn.net/Expert/topic/5089/5089261.xml?temp=.7907068">http://community.csdn.net/Expert/topic/5089/5089261.xml?temp=.7907068</a></p>
		<p>数据库数据查询变得不正常类型问题</p>
		<p>大数据量，稳定运行一段时候以后无法得到查询结果。<br /><a href="http://community.csdn.net/Expert/topic/4810/4810464.xml?temp=9.014529E-02">http://community.csdn.net/Expert/topic/4810/4810464.xml?temp=9.014529E-02</a><br /><br />from: <a href="/zqli/archive/2006/12/08/86391.html">http://www.blogjava.net/zqli/archive/2006/12/08/86391.html</a></p>
<img src ="http://www.blogjava.net/weidagang2046/aggbug/86461.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/weidagang2046/" target="_blank">weidagang2046</a> 2006-12-08 21:55 <a href="http://www.blogjava.net/weidagang2046/articles/86461.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>编写高性能Web应用程序的10个入门技巧</title><link>http://www.blogjava.net/weidagang2046/articles/85980.html</link><dc:creator>weidagang2046</dc:creator><author>weidagang2046</author><pubDate>Wed, 06 Dec 2006 15:57:00 GMT</pubDate><guid>http://www.blogjava.net/weidagang2046/articles/85980.html</guid><wfw:comment>http://www.blogjava.net/weidagang2046/comments/85980.html</wfw:comment><comments>http://www.blogjava.net/weidagang2046/articles/85980.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/weidagang2046/comments/commentRss/85980.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/weidagang2046/services/trackbacks/85980.html</trackback:ping><description><![CDATA[
		<p>编写高性能Web应用程序的10个入门技巧<br /><br />  数据层性能 <br />  技巧 1 — 返回多个结果集 <br />  技巧 2 — 分页的数据访问 <br />  技巧 3 — 连接池 <br />  技巧 4 — ASP.NET 缓存 API <br />  技巧 5 — 每请求缓存 <br />  技巧 6 — 后台处理 <br />  技巧 7 — 页输出缓存和代理<a href="http://www.chinahtml.com/systems/" target="_blank">服务器</a><br />  技巧 8 — 运行 IIS 6.0（只要用于内核缓存） <br />  技巧 9 — 使用 Gzip 压缩 <br />  技巧 10 — <a href="http://www.chinahtml.com/systems/" target="_blank">服务器</a>控件视图状态 <br />  <br /><br />使用 ASP.NET 编写 Web 应用程序的简单程度令人不敢相信。正因为如此简单，所以很多<br />开发人员就不会花时间来设计其应用程序的结构，以获得更好的性能了。在本文中，我将<br />讲述 10 个用于编写高性能 Web 应用程序的技巧。但是我并不会将这些建议仅局限于 <br />ASP.NET 应用程序，因为这些应用程序只是 Web 应用程序的一部分。本文不作为对 Web <br />应用程序进行性能调整的权威性指南 — 一整本书恐怕都无法轻松讲清楚这个问题。请将<br />本文视作一个很好的起点。 <br /><br />成为工作狂之前，我原来喜欢攀岩。在进行任何大型攀岩活动之前，我都会首先仔细查看<br />指南中的路线，阅读以前游客提出的建议。但是，无论指南怎么好，您都需要真正的攀岩<br />体验，然后才能尝试一个特别具有挑战性的攀登。与之相似，当您面临修复性能问题或者<br />运行一个高吞吐量站点的问题时，您只能学习如何编写高性能 Web 应用程序。<br /><br />我的个人体验来自在 Microsoft 的 ASP.NET 部门作为基础架构程序经理的经验，在此期<br />间我运行和管理 www.ASP.NET，帮助设计社区<a href="http://www.chinahtml.com/systems/" target="_blank">服务器</a>的结构，社区<a href="http://www.chinahtml.com/systems/" target="_blank">服务器</a>是几个著名 <br />ASP.NET 应用程序（组合到一个平台的 ASP.NET Forums、.Text 和 nGallery）。我确信<br />有些曾经帮助过我的技巧对您肯定也会有所帮助。<br /><br />您应该考虑将应用程序分为几个逻辑层。您可能听说过 3 层（或者 n 层）物理体系结构<br />一词。这些通常都是规定好的体系结构方式，将功能在进程和/或硬件之间进行了物理分离<br />。当系统需要扩大时，可以很轻松地添加更多的硬件。但是会出现一个与进程和机器跳跃<br />相关的性能下降，因此应该避免。所以，如果可能的话，请尽量在同一个应用程序中一起<br />运行 ASP.NET 页及其相关组件。<br /><br />因为代码分离以及层之间的边界，所以使用 Web 服务或远程处理将会使得性能下降 20% <br />甚至更多。<br /><br />数据层有点与众不同，因为通常情况下，最好具有专用于<a href="http://www.chinahtml.com/databases/" target="_blank">数据库</a>的硬件。然而进程跳跃到<br /><a href="http://www.chinahtml.com/databases/" target="_blank">数据库</a>的成本依然很高，因此数据层的性能是您在优化代码时首先要考虑的问题。<br /><br />在深入应用程序的性能修复问题之前，请首先确保对应用程序进行剖析，以便找出具体的<br />问题所在。主要性能计数器（如表示执行垃圾回收所需时间百分比的计数器）对于找出应<br />用程序在哪些位置花费了其主要时间也非常有用。然而花费时间的位置通常非常不直观。<br /><br />本文讲述了两种类型的性能改善：大型优化（如使用 ASP.NET 缓存），和进行自身重复的<br />小型优化。这些小型优化有时特别有意思。您对代码进行一点小小的更改，就会获得很多<br />很多时间。使用大型优化，您可能会看到整体性能的较大飞跃。而使用小型优化时，对于<br />某个特定请求可能只会节省几毫秒的时间，但是每天所有请求加起来，则可能会产生巨大<br />的改善。<br /><br />数据层性能<br /><br /><br />谈到应用程序的性能调整，有一个试纸性的测试可用来对工作进行优先级划分：代码是否<br />访问<a href="http://www.chinahtml.com/databases/" target="_blank">数据库</a>？如果是，频率是怎样的？请注意，这一相同测试也可应用于使用 Web 服务或<br />远程处理的代码，但是本文对这些内容未做讲述。<br /><br />如果某个特定的代码路径中必需进行<a href="http://www.chinahtml.com/databases/" target="_blank">数据库</a>请求，并且您认为要首先优化其他领域（如字<br />符串操作），则请停止，然后执行这个试纸性测试。如果您的性能问题不是非常严重的话<br />，最好花一些时间来优化一下与<a href="http://www.chinahtml.com/databases/" target="_blank">数据库</a>、返回的数据量、进出<a href="http://www.chinahtml.com/databases/" target="_blank">数据库</a>的往返频率相关的花<br />费时间。<br /><br />了解这些常规信息之后，我们来看一下可能会有助于提高应用程序性能的十个技巧。首先<br />，我要讲述可能会引起最大改观的更改。<br /><br /><br />技巧 1 — 返回多个结果集<br /><br /><br />仔细查看您的<a href="http://www.chinahtml.com/databases/" target="_blank">数据库</a>代码，看是否存在多次进入<a href="http://www.chinahtml.com/databases/" target="_blank">数据库</a>的请求路径。每个这样的往返都会<br />降低应用程序可以提供的每秒请求数量。通过在一个<a href="http://www.chinahtml.com/databases/" target="_blank">数据库</a>请求中返回多个结果集，可以<br />节省与<a href="http://www.chinahtml.com/databases/" target="_blank">数据库</a>进行通信所需的总时间长度。同时因为减少了<a href="http://www.chinahtml.com/databases/" target="_blank">数据库</a><a href="http://www.chinahtml.com/systems/" target="_blank">服务器</a>管理请求的工作<br />，还会使得系统伸缩性更强。<br /><br />虽然可以使用动态 SQL 返回多个结果集，但是我首选使用存储过程。关于业务逻辑是否应<br />该驻留于存储过程的问题还存在一些争议，但是我认为，如果存储过程中的逻辑可以约束<br />返回数据的话（缩小数据集的大小、缩短网络上所花费时间，不必筛选逻辑层的数据），<br />则应赞成这样做。<br /><br />使用 SqlCommand 实例及其 ExecuteReader 方法填充强类型的业务类时，可以通过调用 <br />NextResult 将结果集指针向前移动。图 1 显示了使用类型类填充几个 ArrayList 的示例<br />会话。只从<a href="http://www.chinahtml.com/databases/" target="_blank">数据库</a>返回您需要的数据将进一步减少<a href="http://www.chinahtml.com/systems/" target="_blank">服务器</a>上的内存分配。 <br /><br />Figure 1 Extracting Multiple Resultsets from a DataReader<br />// read the first resultset<br />reader = command.ExecuteReader();<br /><br />// read the data from that resultset<br />while (reader.Read()) {<br />    suppliers.Add(PopulateSupplierFromIDataReader( reader ));<br />}<br /><br />// read the next resultset<br />reader.NextResult();<br /><br />// read the data from that second resultset<br />while (reader.Read()) {<br />    products.Add(PopulateProductFromIDataReader( reader ));<br />}<br /><br /><br />技巧 2 — 分页的数据访问<br /><br /><br />ASP.NET DataGrid 具有一个很好的功能：数据分页支持。在 DataGrid 中启用分页时，一<br />次会显示固定数量的记录。另外，在 DataGrid 的底部还会显示分页 UI，以便在记录之间<br />进行导航。该分页 UI 使您能够在所显示的数据之间向前和向后导航，并且一次显示固定<br />数量的记录。<br /><br />还有一个小小的波折。使用 DataGrid 的分页需要所有数据均与网格进行绑定。例如，您<br />的数据层需要返回所有数据，那么 DataGrid 就会基于当前页筛选显示的所有记录。如果<br />通过 DataGrid 进行分页时返回了 100,000 个记录，那么针对每个请求会放弃 99,975 个<br />记录（假设每页大小为 25 个记录）。当记录的数量不断增加时，应用程序的性能就会受<br />到影响，因为针对每个请求必须发送越来越多的数据。<br /><br />要编写性能更好的分页代码，一个极佳的方式是使用存储过程。图 2 显示了针对 <br />Northwind <a href="http://www.chinahtml.com/databases/" target="_blank">数据库</a>中的 Orders 表进行分页的一个示例存储过程。简而言之，您此时要做<br />的只是传递页索引和页大小。然后就会计算合适的结果集，并将其返回。 <br /><br />Figure 2 Paging Through the Orders Table<br />CREATE PROCEDURE northwind_OrdersPaged<br />(<br />    @PageIndex int, <br />    @PageSize int<br />)<br />AS<br />BEGIN<br />DECLARE @PageLowerBound int<br />DECLARE @PageUpperBound int<br />DECLARE @RowsToReturn int<br /><br />-- First set the rowcount<br />SET @RowsToReturn = @PageSize * (@PageIndex + 1)<br />SET ROWCOUNT @RowsToReturn<br /><br />-- Set the page bounds<br />SET @PageLowerBound = @PageSize * @PageIndex<br />SET @PageUpperBound = @PageLowerBound + @PageSize + 1<br /><br />-- Create a temp table to store the select results<br />CREATE TABLE #PageIndex <br />(<br />    IndexId int IDENTITY (1, 1) NOT NULL,<br />    OrderID int<br />)<br /><br />-- Insert into the temp table<br />INSERT INTO #PageIndex (OrderID)<br />SELECT <br />    OrderID<br />FROM <br />    Orders<br />ORDER BY <br />    OrderID DESC<br /><br />-- Return total count<br />SELECT COUNT(OrderID) FROM Orders<br /><br />-- Return paged results<br />SELECT <br />    O.*<br />FROM <br />    Orders O,<br />    #PageIndex PageIndex<br />WHERE <br />    O.OrderID = PageIndex.OrderID AND<br />    PageIndex.IndexID &gt; @PageLowerBound AND<br />    PageIndex.IndexID &lt; @PageUpperBound<br />ORDER BY <br />    PageIndex.IndexID<br /><br />END<br /><br /><br /><br />在社区<a href="http://www.chinahtml.com/systems/" target="_blank">服务器</a>中，我们编写了一个分页<a href="http://www.chinahtml.com/systems/" target="_blank">服务器</a>控件，以完成所有的数据分页。您将会看到<br />，我使用的就是技巧 1 中讨论的理念，从一个存储过程返回两个结果集：记录的总数和请<br />求的数据。<br /><br />返回记录的总数可能会根据所执行查询的不同而有所变化。例如，WHERE 子句可用来约束<br />返回的数据。为了计算在分页 UI 中显示的总页数，必须了解要返回记录的总数。例如，<br />如果总共有 1,000,000 条记录，并且要使用一个 WHERE 子句将其筛选为 1000 条记录，<br />那么分页逻辑就需要了解记录的总数才能正确呈现分页 UI。<br /><br /><br />技巧 3 — 连接池<br /><br /><br />在 Web 应用程序和 <a href="http://www.chinahtml.com/databases/2/" target="_blank">SQL Server</a>? 之间设置 TCP 连接可能是一个非常消耗资源的操作。Mi<br />crosoft 的开发人员到目前为止能够使用连接池已经有一段时间了，这使得他们能够重用<br /><a href="http://www.chinahtml.com/databases/" target="_blank">数据库</a>连接。他们不是针对每个请求都设置一个新的 TCP 连接，而是只在连接池中没有任<br />何连接时才设置新连接。当连接关闭时，它会返回连接池，在其中它会保持与<a href="http://www.chinahtml.com/databases/" target="_blank">数据库</a>的连<br />接，而不是完全破坏该 TCP 连接。<br /><br />当然，您需要小心是否会出现泄漏连接。当您完成使用连接时，请一定要关闭这些连接。<br />再重复一遍：无论任何人对 Microsoft?.NET Framework 中的垃圾回收有什么评论，请一<br />定要在完成使用连接时针对该连接显式调用 Close 或 Dispose。不要相信公共语言运行库<br />(CLR) 会在预先确定的时间为您清除和关闭连接。尽管 CLR 最终会破坏该类，并强制连<br />接关闭，但是当针对对象的垃圾回收真正发生时，并不能保证。 <br /><br />要以最优化的方式使用连接池，需要遵守一些规则。首先打开连接，执行操作，然后关闭<br />该连接。如果您必须如此的话，可以针对每个请求多次打开和关闭连接（最好应用技巧 1<br />），但是不要一直将连接保持打开状态并使用各种不同的方法对其进行进出传递。第二，<br />使用相同的连接字符串（如果使用集成身份验证的话，还要使用相同的线程标识）。如果<br />不使用相同的连接字符串，例如根据登录的用户自定义连接字符串，那么您将无法得到连<br />接池提供的同一个优化值。如果您使用集成身份验证，同时还要模拟大量用户，连接池的<br />效率也会大大下降。尝试跟踪与连接池相关的任何性能问题时，.NET CLR 数据性能计数器<br />可能非常有用。 <br /><br />每当应用程序连接资源时，如在另一个进程中运行的<a href="http://www.chinahtml.com/databases/" target="_blank">数据库</a>，您都应该重点考虑连接该资<br />源所花时间、发送或检索数据所花时间，以及往返的数量，从而进行优化。优化应用程序<br />中任何种类的进程跳跃都是获得更佳性能的首要一点。<br /><br />应用层包含了连接数据层、将数据转换为有意义类实例和业务流程的逻辑。例如社区服务<br />器，您要在其中填充Forums 或 Threads集合，应用业务规则（如权限）；最重要的是要在<br />其中执行缓存逻辑。<br /><br /><br />技巧 4 — ASP.NET 缓存 API<br /><br /><br />编写应用程序代码行之前，一个首要完成的操作是设计应用层的结构，以便最大化利用 <br />ASP.NET 缓存功能。<br /><br />如果您的组件要在 ASP.NET 应用程序中运行，则只需在该应用程序项目中包括一个 <br />System.Web.dll 引用。当您需要访问该缓存时，请使用 HttpRuntime.Cache 属性（通过 <br />Page.Cache 和 HttpContext.Cache 也可访问这个对象）。<br /><br />对于缓存数据，有几个规则。首先，如果数据可能会多次使用时，则这是使用缓存的一个<br />很好的备选情况。第二，如果数据是通用的，而不特定于某个具体的请求或用户时，则也<br />是使用缓存的一个很好的备选情况。如果数据是特定于用户或请求的，但是寿命较长的话<br />，仍然可以对其进行缓存，但是这种情况可能并不经常使用。第三，一个经常被忽略的规<br />则是，有时可能您缓存得太多。通常在一个 x86 计算机上，为了减少内存不足错误出现的<br />机会，您会想使用不高于 800MB 的专用字节运行进程。因此缓存应该有个限度。换句话说<br />，您可能能够重用某个计算结果，但是如果该计算采用 10 个参数的话，您可能要尝试缓<br />存 10 个排列，这样有可能给您带来麻烦。一个要求 ASP.NET 的最常见支持是由于过度缓<br />存引起的内存不足错误，尤其是对于大型数据集。<br /><br /><br />缓存有几个极佳的功能，您需要对它们有所了解。首先，缓存会实现最近最少使用的算法<br />，使得 ASP.NET 能够在内存运行效率较低的情况下强制缓存清除 － 从缓存自动删除未使<br />用过的项目。第二，缓存支持可以强制失效的过期依赖项。这些依赖项包括时间、密钥和<br />文件。时间经常会用到，但是对于 ASP.NET 2.0，引入了一个功能更强的新失效类型：数<br />据库缓存失效。它指的是当<a href="http://www.chinahtml.com/databases/" target="_blank">数据库</a>中的数据发生变化时自动删除缓存中的项。有关<a href="http://www.chinahtml.com/databases/" target="_blank">数据库</a><br />缓存失效的详细信息，请参阅 MSDN?Magazine 2004 年 7 月的 Dino Esposito Cutting <br />Edge 专栏。要了解缓存的体系结构，请参阅图 3。<br /><br />技巧 5 — 每请求缓存<br /><br />在本文前面部分，我提到了经常遍历代码路径的一些小改善可能会导致较大的整体性能收<br />益。对于这些小改善，其中有一个绝对是我的最爱，我将其称之为"每请求缓存"。<br /><br />缓存 API 的设计目的是为了将数据缓存较长的一段时间，或者缓存至满足某些条件时，但<br />每请求缓存则意味着只将数据缓存为该请求的持续时间。对于每个请求，要经常访问某个<br />特定的代码路径，但是数据却只需提取、应用、修改或更新一次。这听起来有些理论化，<br />那么我们来举一个具体的示例。<br /><br />在社区<a href="http://www.chinahtml.com/systems/" target="_blank">服务器</a>的论坛应用程序中，页面上使用的每个<a href="http://www.chinahtml.com/systems/" target="_blank">服务器</a>控件都需要个性化的数据来确<br />定使用什么外观、使用什么样式表，以及其他个性化数据。这些数据中有些可以长期缓存<br />，但是有些数据却只针对每个请求提取一次，然后在执行该请求期间对其重用多次，如要<br />用于控件的外观。<br /><br />为了达到每请求缓存，请使用 ASP.NET HttpContext。对于每个请求，都会创建一个 <br />HttpContext 实例，在该请求期间从 HttpContext.Current 属性的任何位置都可访问该实<br />例。该 HttpContext 类具有一个特殊的 Items 集合属性；添加到此 Items 集合的对象和<br />数据只在该请求持续期间内进行缓存。正如您可以使用缓存来存储经常访问的数据一样，<br />您也可以使用 HttpContext.Items 来存储只基于每个请求使用的数据。它背后的逻辑非常<br />简单：数据在它不存在的时候添加到 HttpContext.Items 集合，在后来的查找中，只是返<br />回 HttpContext.Items 中的数据。<br /><br /><br />技巧 6 — 后台处理<br /><br /><br />通往代码的路径应该尽可能快速，是吗？可能有时您会觉得针对每个请求执行的或者每 <br />n 个请求执行一次的任务所需资源非常多。发送电子邮件或者分析和验证传入数据就是这<br />样的一些例子。<br /><br />剖析 ASP.NET Forums 1.0 并重新构建组成社区<a href="http://www.chinahtml.com/systems/" target="_blank">服务器</a>的内容时，我们发现添加新张贴的<br />代码路径非常慢。每次添加新张贴时，应用程序首先需要确保没有重复的张贴，然后必须<br />使用"坏词"筛选器分析该张贴，分析张贴的字符图释，对张贴添加标记并进行索引，请求<br />时将张贴添加到合适的队列，验证附件，最终张贴之后，立即向所有订阅者发出电子邮件<br />通知。很清楚，这涉及很多操作。<br /><br />经研究发现，大多数时间都花在了索引逻辑和发送电子邮件上。对张贴进行索引是一个非<br />常耗时的操作，人们发现内置的 System.Web.Mail 功能要连接 SMYP <a href="http://www.chinahtml.com/systems/" target="_blank">服务器</a>，然后连续发<br />送电子邮件。当某个特定张贴或主题领域的订阅者数量增加时，执行 AddPost 功能所需的<br />时间也越来越长。<br /><br />并不需要针对每个请求都进行电子邮件索引。理想情况下，我们想要将此操作进行批处理<br />，一次索引 25 个张贴或者每五分钟发送一次所有电子邮件。我们决定使用以前用于对数<br />据缓存失效进行原型设计的代码，这个失效是用于最终进入 Visual Studio? 2005 的内容<br />的。<br /><br />System.Threading 命名空间中的 Timer 类非常有用，但是在 .NET Framework 中不是很<br />有名，至少对于 Web 开发人员来说是这样。创建之后，这个 Timer 类将以一个可配置的<br />间隔针对 ThreadPool 中的某个线程调用指定的回调。这就表示，您可以对代码进行设置<br />，使其能够在没有对 ASP.NET 应用程序进行传入请求的情况下得以执行，这是后台处理的<br />理想情况。您还可以在此后台进程中执行如索引或发送电子邮件之类的操作。 <br /><br />但是，这一技术有几个问题。如果应用程序域卸载，该计时器实例将停止触发其事件。另<br />外，因为 CLR 对于每个进程的线程数量具有一个硬性标准，所以可能会出现这样的情形：<br /><a href="http://www.chinahtml.com/systems/" target="_blank">服务器</a>负载很重，其中计时器可能没有可在其基础上得以完成的线程，在某种程度上可能<br />会造成延迟。ASP.NET 通过在进程中保留一定数量的可用线程，并且仅使用总线程的一部<br />分用于请求处理，试图将上述情况发生的机会降到最低。但是，如果您具有很多异步操作<br />时，这可能就是一个问题了。 <br /><br />这里没有足够的空间来放置该代码，但是您可以下载一个可以看懂的示例，网址是 <br />www.rob-howard.net。请了解一下 Blackbelt TechEd 2004 演示中的幻灯片和演示。<br /><br /><br />技巧 7 — 页输出缓存和代理<a href="http://www.chinahtml.com/systems/" target="_blank">服务器</a><br /><br /><br />ASP.NET 是您的表示层（或者说应该是您的表示层）；它由页、用户控件、<a href="http://www.chinahtml.com/systems/" target="_blank">服务器</a>控件（H<br />ttpHandlers 和 HttpModules）以及它们生成的内容组成。如果您具有一个 ASP.NET 页，<br />它会生成输出（HTML、XML、图像或任何其他数据），并且您针对每个请求运行此代码时，<br />它都会生成相同的输出，那么您就拥有一个可用于页输出缓存的绝佳备选内容。 <br /><br />将此行内容添加页的最上端 <br /><br />&lt;%@ Page OutputCache VaryByParams="none" Duration="60" %&gt; <br /><br />就可以高效地为此页生成一次输出，然后对它进行多次重用，时间最长为 60 秒，此时该<br />页将重新执行，输出也将再一次添加到 ASP.NET 缓存。通过使用一些低级程序化 API 也<br />可以完成此行为。对于输出缓存有几个可配置的设置，如刚刚讲到的 VaryByParams 属性<br />。VaryByParams 刚好被请求到，但还允许您指定 HTTP GET 或 HTTP POST 参数来更改缓<br />存项。例如，只需设置 VaryByParam="Report" 即可对 default.aspx?Report=1 或 <br />default.aspx?Report=2 进行输出缓存。通过指定一个以分号分隔的列表，还可以指定其<br />他参数。 <br /><br />很多人都不知道何时使用输出缓存，ASP.NET 页还会生成一些位于缓存<a href="http://www.chinahtml.com/systems/" target="_blank">服务器</a>下游的 <br />HTTP 标头，如 Microsoft Internet Security and Acceleration Server 或 Akamai 使<br />用的标头。设置了 HTTP 缓存标头之后，可以在这些网络资源上对文档进行缓存，客户端<br />请求也可在不必返回原始<a href="http://www.chinahtml.com/systems/" target="_blank">服务器</a>的情况下得以满足。<br /><br />因此，使用页输出缓存不会使得您的应用程序效率更高，但是它可能会减少<a href="http://www.chinahtml.com/systems/" target="_blank">服务器</a>上的负<br />载，因为下游缓存技术会缓存文档。当然，这可能只是匿名内容；一旦它成为下游之后，<br />您就再也不会看到这些请求，并且再也无法执行身份验证以阻止对它的访问了。<br /><br /><br />技巧 8 — 运行 IIS 6.0（只要用于内核缓存）<br /><br /><br />如果您未运行 IIS 6.0 (<a href="http://www.chinahtml.com/systems/2/" target="_blank">Windows</a> Server? 2003)，那么您就错过了 Microsoft Web 服务<br />器中的一些很好的性能增强。在技巧 7 中，我讨论了输出缓存。在 IIS 5.0 中，请求是<br />通过 IIS 然后进入 ASP.NET 的。涉及到缓存时，ASP.NET 中的 HttpModule 会接收该请<br />求，并返回缓存中的内容。<br /><br />如果您正在使用 IIS 6.0，就会发现一个很好的小功能，称为内核缓存，它不需要对 <br />ASP.NET 进行任何代码更改。当请求由 ASP.NET 进行输出缓存时，IIS 内核缓存会接收缓<br />存数据的一个副本。当请求来自网络驱动程序时，内核级别的驱动程序（无上下文切换到<br />用户模式）就会接收该请求，如果经过了缓存，则会将缓存的数据刷新到响应，然后完成<br />执行。这就表示，当您将内核模式缓存与 IIS 和 ASP.NET 输出缓存一起使用时，就会看<br />到令人不敢相信的性能结果。在 ASP.NET 的 Visual Studio 2005 开发过程中，我一度是<br />负责 ASP.NET 性能的程序经理。开发人员完成具体工作，但是我要看到每天进行的所有报<br />告。内核模式缓存结果总是最有意思的。最常见的特征是网络充满了请求/响应，而 IIS <br />运行时的 CPU 使用率只有大约 5%。这太令人震惊了！当然使用 IIS 6.0 还有一些其他原<br />因，但是内核模式缓存是其中最明显的一个。<br /><br /><br />技巧 9 — 使用 Gzip 压缩<br /><br /><br />虽然使用 gzip 并不一定是<a href="http://www.chinahtml.com/systems/" target="_blank">服务器</a>性能技巧（因为您可能会看到 CPU 使用率的提高），但<br />是使用 gzip 压缩可以减少<a href="http://www.chinahtml.com/systems/" target="_blank">服务器</a>发送的字节数量。这就使人们觉得页速度加快了，并且<br />还减少了带宽的用量。根据所发送数据、可以压缩的程度以及客户端浏览器是否支持（IIS<br />只会向支持 gzip 压缩的客户端发送经过 gzip 压缩的内容，如 Internet Explorer <br />6.0 和 Firefox），您的<a href="http://www.chinahtml.com/systems/" target="_blank">服务器</a>每秒可以服务于更多的请求。实际上，几乎每当您减少所<br />返回数据的数量时，都会增加每秒请求数。 <br /><br />Gzip 压缩已经内置到 IIS 6.0 中，并且其性能比 IIS 5.0 中使用的 gzip 压缩要好的多<br />，这是好消息。但不幸的是，当尝试在 IIS 6.0 中打开 gzip 压缩时，您可能无法在 <br />IIS 的属性对话中找到该设置。IIS 小组在该<a href="http://www.chinahtml.com/systems/" target="_blank">服务器</a>中置入了卓越的 gzip 功能，但是忘<br />了包括一个用于启用该功能的管理 UI。要启用 gzip 压缩，您必须深入到 IIS 6.0 的 <br />XML 配置设置内部（这样不会引起心脏虚弱）。顺便提一句，这归功于 OrcsWeb 的 <br />Scott Forsyth，他帮助我提出了在 OrcsWeb 上宿主的 www.asp.net <a href="http://www.chinahtml.com/systems/" target="_blank">服务器</a>的这个问题。<br /><br /><br />本文就不讲述步骤了，请阅读 Brad Wilson 的文章，网址是 IIS6 Compression。还有一<br />篇有关为 ASPX 启用压缩的知识库文章，网址是 Enable ASPX Compression in IIS。但是<br />您应该注意，由于一些实施细节，IIS 6.0 中不能同时存在动态压缩和内核缓存。<br /><br /><br />技巧 10 — <a href="http://www.chinahtml.com/systems/" target="_blank">服务器</a>控件视图状态<br /><br /><br />视图状态是一个有趣的名称，用于表示在所生成页的隐藏输出字段中存储一些状态数据的 <br />ASP.NET。当该页张贴回<a href="http://www.chinahtml.com/systems/" target="_blank">服务器</a>时，<a href="http://www.chinahtml.com/systems/" target="_blank">服务器</a>可以分析、验证、并将此视图状态数据应用回该<br />页的控件树。视图状态是一个非常强大的功能，因为它允许状态与客户端一起保持，并且<br />它不需要 cookie 或<a href="http://www.chinahtml.com/systems/" target="_blank">服务器</a>内存即可保存此状态。很多 ASP.NET <a href="http://www.chinahtml.com/systems/" target="_blank">服务器</a>控件都使用视图状<br />态来保持在与页元素进行交互期间创建的设置，例如保存对数据进行分页时显示的当前页<br />。 <br /><br />然而使用视图状态也有一些缺点。首先，服务或请求页时，它都会增加页的总负载。对张<br />贴回<a href="http://www.chinahtml.com/systems/" target="_blank">服务器</a>的视图状态数据进行序列化或取消序列化时，也会发生额外的开销。最后，视<br />图状态会增加<a href="http://www.chinahtml.com/systems/" target="_blank">服务器</a>上的内存分配。<br /><br />几个<a href="http://www.chinahtml.com/systems/" target="_blank">服务器</a>控件有着过度使用视图状态的趋势，即使在并不需要的情况下也要使用它，其<br />中最著名的是 DataGrid。ViewState 属性的默认行为是启用，但是如果您不需要，则可以<br />在控件或页级别关闭。在控件内，只需将 EnableViewState 属性设置为 false，或者在页<br />中使用下列设置即可对其进行全局设置： <br /><br />&lt;%@ Page EnableViewState="false" %&gt;<br /><br />如果您不回发页，或者总是针对每个请求重新生成页上的控件，则应该在页级别禁用视图<br />状态。 <br /><br /><br />我为您讲述了一些我认为在编写高性能 ASP.NET 应用程序时有所帮助的技巧。正如我在本<br />文前面部分提到的那样，这是一个初步指南，并不是 ASP.NET 性能的最后结果。（有关改<br />善 ASP.NET 应用程序性能的信息，请参阅 Improving ASP.NET Performance。）只有通过<br />自己的亲身体验才能找出解决具体性能问题的最好方法。但是，在您的旅程中，这些技巧<br />应该会为您提供一些好的指南。在软件开发中，几乎没有绝对的东西；每个应用程序都是<br />唯一的。<br /><br />from: <a href="http://www.chinahtml.com/programming/8/2006/11622676777784_2.shtml">http://www.chinahtml.com/programming/8/2006/11622676777784_2.shtml</a><br /></p>
<img src ="http://www.blogjava.net/weidagang2046/aggbug/85980.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/weidagang2046/" target="_blank">weidagang2046</a> 2006-12-06 23:57 <a href="http://www.blogjava.net/weidagang2046/articles/85980.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>linux下postgresql数据库中汉字的插入</title><link>http://www.blogjava.net/weidagang2046/articles/85213.html</link><dc:creator>weidagang2046</dc:creator><author>weidagang2046</author><pubDate>Sun, 03 Dec 2006 11:55:00 GMT</pubDate><guid>http://www.blogjava.net/weidagang2046/articles/85213.html</guid><wfw:comment>http://www.blogjava.net/weidagang2046/comments/85213.html</wfw:comment><comments>http://www.blogjava.net/weidagang2046/articles/85213.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/weidagang2046/comments/commentRss/85213.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/weidagang2046/services/trackbacks/85213.html</trackback:ping><description><![CDATA[首先用postgresql的用户登陆,(su - postgres) ,然后进入数据库,假设数据库名称为“house”,命令为 <br /><br />[postgres@ITC-S postgres]$ psql house<br />Welcome to psql 8.0.1, the PostgreSQL interactive terminal.<br /><p>Type:  copyright for distribution terms<br />　       h for help with SQL commands<br />　       ? for help with psql commands<br />　       g or terminate with semicolon to execute query<br />　       q to quit<br />house=# <br />输入encoding GBK<br />house=#set encoding GBK<br />然后就可以插入汉字了。<br /><br />from: <a href="http://publish.it168.com/2006/0219/20060219173801.shtml?positioncode=1547">http://publish.it168.com/2006/0219/20060219173801.shtml?positioncode=1547</a></p><img src ="http://www.blogjava.net/weidagang2046/aggbug/85213.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/weidagang2046/" target="_blank">weidagang2046</a> 2006-12-03 19:55 <a href="http://www.blogjava.net/weidagang2046/articles/85213.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>一劳永逸的数据库编码解决方案</title><link>http://www.blogjava.net/weidagang2046/articles/85178.html</link><dc:creator>weidagang2046</dc:creator><author>weidagang2046</author><pubDate>Sun, 03 Dec 2006 05:22:00 GMT</pubDate><guid>http://www.blogjava.net/weidagang2046/articles/85178.html</guid><wfw:comment>http://www.blogjava.net/weidagang2046/comments/85178.html</wfw:comment><comments>http://www.blogjava.net/weidagang2046/articles/85178.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/weidagang2046/comments/commentRss/85178.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/weidagang2046/services/trackbacks/85178.html</trackback:ping><description><![CDATA[
		<p>　　<strong>问题提出</strong></p>
		<p>　　现在几乎所有的应用系统都无法避免使用数据库系统。在JAVA世界里访问数据库是一件非常轻松的事情，JDBC为JAVA应用程序访问数据库提供了一个统一的接口，通过使用JDBC接口开发者无需关心系统最终采用哪种数据库，因为JDBC仅仅是定义了访问几个JAVA的接口类，具体的实现是由数据库厂商提供的，这种做法其实与其他数据库连接方式例如ODBC是类似的。但是在实际的应用过程中，开发者发现离JDBC设计的初衷还是有一定距离，就比如说在存储字符串时的编码问题，我想很多开发者都会遇见这个问题，倒不是因为说解决它有什么技术方面的难度，而是它的的确确非常繁琐。我们必须在每次写入或者读出字符串的时候进行编码和反编码处理；或者说我们可以写一个方法可以进行编码处理的，但又必须在每次数据库操作的时候调用，虽然调用很简单，可是我非得这样吗？要是忘了编码那又要DEBUG了。当然你可能觉得这并没有什么，或者你可能很勤快，喜欢写大量重复的代码，可是你难道没有觉得这种繁琐的工作正在浪费你过于宝贵的青春吗？停止你的键盘输入，让我们来解决这个问题吧！</p>
		<p>　　<strong>解决思路</strong></p>
		<p>　　在传统的应用程序中数据库操作部分我们可以想象成两层，如图所示：一个是数据库的"连接池"，另外一个业务数据操作层。在这里数据库的连接池是广义的，你可以把JDBC中的DriverManager也当成是连接池，具体的意思就是我们可以通过这层来获取到指定数据库的连接而不去关心它是怎么获取的。如果这个时候数据库系统（有如Informix，SQL Server）要求对字符串进行转码才能存储（例如最常见的GBK-&gt;ISO8859_1转码），那我们就必须在业务数据操作层来进行，这样有多少业务数据操作我们就要做多少编码转码的工作，太麻烦了，代码中充斥中大量重复的内容。本文提出的解决方案就是利用对获取到的数据库连接实例进行二次封装，也就是在数据库连接池与业务数据操作层之间加入了连接封装层，当然了，我们也完全可以直接将连接封装集成到数据库连接池内部。关于连接池的实现请参照我的另外一篇文章《使用JAVA动态代理实现数据库连接池》</p>
		<p align="center">
				<img height="173" src="http://www.javafan.net/uploadfiles/20041212111952100.gif" width="247" border="0" />
				<br />图一</p>
		<p>　　我们知道进行编码和转码工作都是集中在JDBC的两个接口PreparedStatement和ResultSet上进行的，主要涉及PreparedStatement的setString方法以及ResultSet的getString方法。前面我们讲过需要加入一个连接封装层来对数据库连接实例进行二次封装，但是怎么通过这个封装来改变PreparedStatement和ResultSet这两个接口的行为呢？这个问题其实也很简单，因为PreparedStatement接口必须通过Connection接口来获取实例，而ResultSet接口又必须从Statement或者PreparedStatement接口来获取实例，有了这样的级联关系，问题也就迎刃而解了。还是利用我在文章《使用JAVA动态代理实现数据库连接池》中使用的动态接口代理技术。首先我们设计Connection接口的代理类_Connection，这个代理类接管了Connection接口中所有可能获取到Statement或者PreparedStatement接口实例的方法，例如：prepareStatement和createStatement。改变这两个方法使之返回的是经过接管后的Statement或者PreparedStatement实例。通过对于Statement接口也有相应的代理类_Statement，这个代理类接管用于获取ResultSet接口实例的所有方法，包括对setString方法的接管以决定是否对字符串进行编码处理。对于接口ResultSet的接管类_ResultSet就相应的比较简单，它只需要处理getString方法即可。</p>
		<p>　　<strong>关键代码</strong></p>
		<p>　　前面我们大概介绍了这个解决方案的思路，下面我们给出关键的实现代码包括Connection的代理类，Statement的代理类，ResultSet的代理类。这些代码是在原来关于数据库连接池实现的基础上进行扩充使之增加对自动编码处理的功能。有需要源码打包的可以通过电子邮件跟我联系。</p>
		<p>_Connection.java</p>
		<p style="BACKGROUND: #eeeeee">/* <br /> * Created on 2003-10-23 by Liudong <br /> */<br />package lius.pool;<br />import java.sql.*;<br />import java.lang.reflect.*;<br /><br />/*<br /> * <br /> * 数据库连接的代理类 <br /> * @author Liudong <br /> */<br /> class _Connection implements InvocationHandler{<br /> private Connection conn = null;<br /> private boolean coding = false;<br /> //指定是否进行字符串转码操作<br /> _Connection(Connection conn, boolean coding){<br />  this.conn = conn;<br />  this.coding = coding;<br />  initConnectionParam(this.conn);<br /> <br /> }<br /> <br /> /**  <br />  * Returns the conn.  <br />  * @return Connection  <br />  */<br />  <br /> public Connection getConnection() {<br />  Class[] interfaces = conn.getClass().getInterfaces();<br />  if(interfaces==null||interfaces.length==0){<br />   interfaces = new Class[1];<br />   interfaces[0] = Connection.class;<br />  <br />  }<br /> <br />  Connection conn2 = (Connection)Proxy.newProxyInstance( conn.getClass().getClassLoader(), interfaces,this);<br />  return conn2;<br /> <br /> }<br /> <br /> /**  <br />  * @see java.lang.reflect.InvocationHandler#invoke  <br />  */<br /> public Object invoke(Object proxy, Method m, Object[] args) throws Throwable { <br />  String method = m.getName();<br />  //调用相应的操作<br />  Object obj = null;<br />  try{<br />   obj = m.invoke(conn, args);<br />   //接管用于获取语句句柄实例的方法<br />   if((CS.equals(method)||PS.equals(method))&amp;&amp;coding) <br />    return new _Statement((Statement)obj,true).getStatement();<br />  <br />  } catch(InvocationTargetException e) {<br />   throw e.getTargetException();<br />  }<br />  return obj;<br /> }<br /> <br /> private final static String PS = "prepareStatement";<br /> private final static String CS = "createStatement";<br />}</p>
		<p>
				<br />_Statement.java</p>
		<p style="BACKGROUND: #eeeeee">/* <br /> * Created on 2003-10-23 by Liudong <br /> */<br /> <br />package lius.pool;<br />import java.sql.*;<br />import java.lang.reflect.*;<br /><br />/** <br /> * 数据库语句对象实例的代理类 <br /> * @author Liudong <br /> */<br />class _Statement implements InvocationHandler{ <br /> private Statement statement ; //保存所接管对象的实例 <br /> private boolean decode = false; //指定是否进行字符串转码 <br /><br /> public _Statement(Statement stmt,boolean decode) { <br />  this.statement = stmt;<br />  this.decode = decode;<br /> }<br /> <br /> /**  <br />  * 获取一个接管后的对象实例  <br />  * @return  <br />  */<br /> public Statement getStatement() {<br />  Class[] interfaces = statement.getClass().getInterfaces();<br />  if(interfaces==null||interfaces.length==0){ <br />   interfaces = new Class[1];<br />   interfaces[0] = Statement.class;<br />  } <br />  Statement stmt = (Statement)Proxy.newProxyInstance(   <br />   statement.getClass().getClassLoader(), <br />   interfaces,this);<br />  return stmt;<br /> <br /> }<br /> <br /> /**  <br />  * 方法接管  <br />  */<br /> public Object invoke(Object proxy, Method m, Object[] args) throws Throwable {<br />  String method = m.getName(); //接管setString方法 <br />  if(decode &amp;&amp; SETSTRING.equals(method)) {<br />   try{<br />    String param = (String)args[1];<br />    if(param!=null)<br />     param = new String(param.getBytes(),"8859_1");<br />    return m.invoke(statement,new Object[]{args[0],param});<br />   } catch(InvocationTargetException e){<br />    throw e.getTargetException();<br />        <br />   }  <br />  }<br />  <br />  //接管executeQuery方法<br />  <br />  if(decode &amp;&amp; EXECUTEQUERY.equals(method)){<br />   try{<br />    ResultSet rs = (ResultSet)m.invoke(statement,args);<br />    return new _ResultSet(rs,decode).getResultSet();<br />   <br />   }catch(InvocationTargetException e){<br />    throw e.getTargetException();<br />    }  <br />  }<br />  <br />  try{<br />   return m.invoke(statement, args);<br />  } catch(InvocationTargetException e) {<br />   throw e.getTargetException();<br />   } <br /> }<br /> //两个要接管的方法名<br /> <br /> private final static String SETSTRING = "setString";<br /> private final static String EXECUTEQUERY = "executeQuery";<br />}</p>
		<p>
				<br />_ResultSet.java</p>
		<p style="BACKGROUND: #eeeeee">/* <br /> * Created on 2003-10-23 by Liudong <br /> */<br /> <br />package lius.pool;<br />import java.sql.ResultSet;<br />import java.lang.reflect.InvocationHandler;<br />import java.lang.reflect.InvocationTargetException;<br />import java.lang.reflect.Method;<br />import java.lang.reflect.Proxy;<br /><br />/** <br /> * 数据库结果集的代理类 <br /> * @author Liudong <br /> */<br /> class _ResultSet implements InvocationHandler{ <br /> private ResultSet rs = null;<br /> private boolean decode = false;<br /> <br /> public _ResultSet(ResultSet rs,boolean decode) {<br />  this.rs = rs;<br />  this.decode = decode;<br /> }<br /> <br /> public ResultSet getResultSet(){ <br />  Class[] interfaces = rs.getClass().getInterfaces();<br />  if(interfaces==null||interfaces.length==0){<br />   interfaces = new Class[1];<br />   interfaces[0] = ResultSet.class;  <br />  }<br /> <br />  ResultSet rs2 = (ResultSet)Proxy.newProxyInstance(rs.getClass().getClassLoader(),interfaces,this);<br />  return rs2;<br /> <br /> }<br /><br /> /**  <br />  * 结果getString方法  <br />  */<br /> public Object invoke(Object proxy, Method m, Object[] args) throws Throwable { <br />  String method = m.getName();<br />  if(decode &amp;&amp; GETSTRING.equals(method)){<br />   try{<br />    String result = (String)m.invoke(rs,args);<br />    if(result!=null)     <br />     return new String(result.getBytes("8859_1"));<br />    return null;<br />   <br />   }catch(InvocationTargetException e){<br />    throw e.getTargetException();<br />    }<br />   <br />  } <br />  <br />  try{<br />   return m.invoke(rs, args);<br />  }catch(InvocationTargetException e){<br />   throw e.getTargetException();<br />  }<br /> }<br /> <br /> private final static String GETSTRING = "getString";<br /><br />}</p>
		<p>　　现在我们已经把三个接口的代理类做好了，下一步就是怎么来使用这三个类。其实对于使用者来讲并不需要关心三个类，只需要了解_Connection就可以了，因为另外两个是_Connection直接调用的。为了使用_Connection我们必须传入两个参数，第一个是数据库实际的数据库连接实例，另外一个是布尔值代表是否进行转码处理。我们必须先通过实际的情况获取到数据库连接后再传入_Connection的构造函数作为参数，下面例子告诉你如何来使用_Connection这个类：</p>
		<p style="BACKGROUND: #eeeeee">　　Connection conn = getConnection(); //获取数据库连接<br />　　boolean coding = false; //从配置或者其他地方读取是否进行转码的配置 <br />　　//接管数据库连接实例 <br />　　_Connection _conn = new _Connection(conn,coding);<br />　　//获得接管后的数据库连接实例，以后直接使用conn2而不是conn <br />　　Connection conn2 = _conn.getConnection();</p>
		<p>　　因为对一个应用系统来讲，数据库连接的获取必然有统一的方法，在这个方法中加入对连接的接管就可以一劳永逸的解决数据库的编码问题。</p>
		<p>　　<strong>性能比较</strong></p>
		<p>　　功能没有问题了，开发者接下来就会关心性能的问题，因为在进行一些对响应速度要求很高或者大数据量的处理情况下性能就成为一个非常突出的问题。由于JAVA中的动态接口代理采用的是反射（Reflection）机制，同时又加入我们自己的一些代码例如方法名判断，字符串转码等操作因此在性能上肯定比不上直接使用没有经过接管的数据库连接。但是这点性能上的差别是不是我们可以忍受的呢，为此我做了一个试验对二者进行了比较：</p>
		<p>　　测试环境简单描述：</p>
		<p>　　使用ACCESS数据库，建两张结构一样的表，计算从获取连接后到插入数据完毕后的时间差，两个程序（直连数据库和使用连接接管）都进行的字符串的转码操作。</p>
		<p>　　测试结果：</p>
		<table cellspacing="1" cellpadding="1" width="500" align="center" bgcolor="#999999" border="0">
				<tbody>
						<tr bgcolor="#ffffff">
								<td align="middle" height="25">插入记录数</td>
								<td align="middle">直连数据库程序耗时 单位：ms</td>
								<td align="middle">使用连接接管程序耗时</td>
								<td align="middle">性能比较</td>
						</tr>
						<tr bgcolor="#ffffff">
								<td height="25">1000</td>
								<td>2063</td>
								<td>2250</td>
								<td>9.0%</td>
						</tr>
						<tr bgcolor="#ffffff">
								<td height="25">5000</td>
								<td>8594</td>
								<td>8359</td>
								<td>-2.7%</td>
						</tr>
						<tr bgcolor="#ffffff">
								<td height="25">10000</td>
								<td>16750</td>
								<td>17219</td>
								<td>2.8%</td>
						</tr>
						<tr bgcolor="#ffffff">
								<td height="25">15000</td>
								<td>22187</td>
								<td>23000</td>
								<td>3.6%</td>
						</tr>
						<tr bgcolor="#ffffff">
								<td height="25">20000</td>
								<td>27031</td>
								<td>27813</td>
								<td>2.9%</td>
						</tr>
				</tbody>
		</table>
		<p>　　从上面这张测试结果表中来看，二者的性能的差别非常小，尽管在两万条数据的批量插入的时候时间差别也不会多于一秒钟，这样的结果应该说还是令人满意的，毕竟为了程序良好的结构有时候牺牲一点点性能还是值得的。</p>
		<p>　　本文算是我之前文章《使用JAVA动态代理实现数据库连接池》中提出的数据库连接池实现的进一步完善，同样使用动态接口代理的技术来解决数据库编码的问题。JAVA的这个高级技术可以用来解决许多实际中非常棘手的问题，就像本文提到的编码问题的处理以及数据库连接池的实现，同时在WEB开发框架的实现上也有非常大的作为。欢迎对这方面感兴趣的朋友来信共同来研究。<br /><br />from: <a href="http://www.javafan.net/article/20041212111952983.html">http://www.javafan.net/article/20041212111952983.html</a></p>
<img src ="http://www.blogjava.net/weidagang2046/aggbug/85178.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/weidagang2046/" target="_blank">weidagang2046</a> 2006-12-03 13:22 <a href="http://www.blogjava.net/weidagang2046/articles/85178.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>JDBC性能优化技巧</title><link>http://www.blogjava.net/weidagang2046/articles/84051.html</link><dc:creator>weidagang2046</dc:creator><author>weidagang2046</author><pubDate>Tue, 28 Nov 2006 06:59:00 GMT</pubDate><guid>http://www.blogjava.net/weidagang2046/articles/84051.html</guid><wfw:comment>http://www.blogjava.net/weidagang2046/comments/84051.html</wfw:comment><comments>http://www.blogjava.net/weidagang2046/articles/84051.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/weidagang2046/comments/commentRss/84051.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/weidagang2046/services/trackbacks/84051.html</trackback:ping><description><![CDATA[ 
<p class="MsoNormal" style="MARGIN-LEFT: 21pt; TEXT-INDENT: -21pt; mso-margin-top-alt: auto; mso-margin-bottom-alt: auto; mso-list: l77 level1 lfo11; tab-stops: list 21.0pt"><span lang="EN-US" style="FONT-FAMILY: Wingdings">l<span style="FONT: 7pt 'Times New Roman'">         </span></span><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">如果可能，避免访问数据库</span></p><p class="MsoNormal" style="MARGIN-LEFT: 21pt; TEXT-INDENT: -21pt; mso-margin-top-alt: auto; mso-margin-bottom-alt: auto; mso-list: l159 level1 lfo12; tab-stops: list 21.0pt"><span lang="EN-US" style="FONT-FAMILY: Wingdings; mso-bidi-font-weight: bold">l<span style="FONT: 7pt 'Times New Roman'">         </span></span><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">为应用选择最好最快的 JDBC </span><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">驱动 ,参考<a href="http://www.flyjava.com/performance/index.htm"><font color="#0000ff">本站文章</font></a> 。 JDBC3.0</span><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">提供了新的特性来提高性能，诸如连接池， statemente池的改进</span><b><span lang="EN-US"><o:p>   </o:p></span></b></p><p class="MsoNormal" style="MARGIN-LEFT: 21pt; TEXT-INDENT: -21pt; mso-margin-top-alt: auto; mso-margin-bottom-alt: auto; mso-list: l159 level1 lfo12; tab-stops: list 21.0pt"><span lang="EN-US" style="FONT-FAMILY: Wingdings; mso-bidi-font-weight: bold">l<span style="FONT: 7pt 'Times New Roman'">        </span></span><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">对数据库使用连接池并且重用连接，而不要重复打开和关闭连接。最佳的连接池大小是当连接池大到足够使服务请求不等待</span><b><span lang="EN-US"><o:p></o:p></span></b></p><p class="MsoNormal" style="MARGIN-LEFT: 21pt; TEXT-INDENT: -21pt; mso-margin-top-alt: auto; mso-margin-bottom-alt: auto; mso-list: l159 level1 lfo12; tab-stops: list 21.0pt"><span lang="EN-US" style="FONT-FAMILY: Wingdings">l<span style="FONT: 7pt 'Times New Roman'">         </span></span><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">尽量使用支持 JDBC3.0 的驱动，因为 JDBC3.0 支持包括 DataSource 对象，连接池，分布式事务支持， RowSets 和 prepared statement 池等性能增强特性</span></p><p class="MsoNormal" style="MARGIN-LEFT: 21pt; TEXT-INDENT: -21pt; mso-margin-top-alt: auto; mso-margin-bottom-alt: auto; mso-list: l159 level1 lfo12; tab-stops: list 21.0pt"><span lang="EN-US" style="FONT-FAMILY: Wingdings">l<span style="FONT: 7pt 'Times New Roman'">         </span></span><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">Prepared statement 池（自从 JDBC3.0 开始有）高速缓存已经预先优化并运行了的 SQL 查询，这样，他们被再次请求的时候，不必经历再次的优化预处理（避免最优化步骤，诸如检查语法，验证地址，优化访问路径和执行计划）。 Statement 池是一个很好的，重要的性能优化方法</span></p><p class="MsoNormal" style="MARGIN-LEFT: 21pt; TEXT-INDENT: -21pt; mso-list: l172 level1 lfo14; tab-stops: list 21.0pt"><span lang="EN-US" style="FONT-FAMILY: Wingdings">l<span style="FONT: 7pt 'Times New Roman'">         </span></span><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">JDBC3.0 中的 Statement 池和连接池能合作共享 statement 池，这样，能使用一个已高速缓存的 statement （该 statement 来自另外一个连接）的连接，在由任一连接执行的 一些SQL 首次被执行时，产生的 statement 准备开销仅一次</span></p><p class="MsoNormal" style="MARGIN-LEFT: 21pt; TEXT-INDENT: -21pt; mso-margin-top-alt: auto; mso-margin-bottom-alt: auto; mso-list: l159 level1 lfo12; tab-stops: list 21.0pt"><span lang="EN-US" style="FONT-FAMILY: Wingdings">l<span style="FONT: 7pt 'Times New Roman'">         </span></span><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">RowSet对象与 ResultSet 对象相似，但是能提供当断开连接的时候对数据库数据的访问。这允许数据以它最简单的形式被高效的高速缓存</span></p><p class="MsoNormal" style="MARGIN-LEFT: 21pt; TEXT-INDENT: -21pt; mso-list: l172 level1 lfo14; tab-stops: list 21.0pt"><span lang="EN-US" style="FONT-FAMILY: Wingdings">l<span style="FONT: 7pt 'Times New Roman'">         </span></span><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">用同一个连接执行多个 statements</span></p><p class="MsoNormal" style="MARGIN-LEFT: 21pt; TEXT-INDENT: -21pt; mso-margin-top-alt: auto; mso-margin-bottom-alt: auto; mso-list: l159 level1 lfo12; tab-stops: list 21.0pt"><span lang="EN-US" style="FONT-FAMILY: Wingdings">l<span style="FONT: 7pt 'Times New Roman'">         </span></span><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">关闭 autocommit ，但不要让事务打开太久</span></p><p class="MsoNormal" style="MARGIN-LEFT: 21pt; TEXT-INDENT: -21pt; mso-margin-top-alt: auto; mso-margin-bottom-alt: auto; mso-list: l159 level1 lfo12; tab-stops: list 21.0pt"><span lang="EN-US" style="FONT-FAMILY: Wingdings">l<span style="FONT: 7pt 'Times New Roman'">         </span></span><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">避免将事务分布开（事务跨越多个连接）</span></p><p class="MsoNormal" style="MARGIN-LEFT: 21pt; TEXT-INDENT: -21pt; mso-margin-top-alt: auto; mso-margin-bottom-alt: auto; mso-list: l159 level1 lfo12; tab-stops: list 21.0pt"><span lang="EN-US" style="FONT-FAMILY: Wingdings">l<span style="FONT: 7pt 'Times New Roman'">         </span></span><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">最小化数据库的行和列数据获取。使用 setMaxRows, setMaxFieldSize,</span><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">和 SetFetchSize</span></p><p class="MsoNormal" style="MARGIN-LEFT: 21pt; TEXT-INDENT: -21pt; mso-margin-top-alt: auto; mso-margin-bottom-alt: auto; mso-list: l159 level1 lfo12; tab-stops: list 21.0pt"><span lang="EN-US" style="FONT-FAMILY: Wingdings; mso-bidi-font-weight: bold">l<span style="FONT: 7pt 'Times New Roman'">         </span></span><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">使用最高效的数据类型：字符串比整数型快，整数型比浮点类型和时间戳类型都要高效（是否不太理解^&amp;^，这是针对DB2数据库处理来说的，处理character类型最快，而处理integer类型通常需要一些转换或者字节排序）</span></p><p class="MsoNormal" style="MARGIN-LEFT: 21pt; TEXT-INDENT: -21pt; mso-margin-top-alt: auto; mso-margin-bottom-alt: auto; mso-list: l159 level1 lfo12; tab-stops: list 21.0pt"><span lang="EN-US" style="FONT-FAMILY: Wingdings">l<span style="FONT: 7pt 'Times New Roman'">         </span></span><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">使用 updateXXX()</span>方法<span style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">更新： updateXXX() 在可更新的结果集上调用。结果集已经定位到了一行 , 因此当使用一个 UPDATE statement 时，可以消除通常的查找要更新的数据行的开销</span></p><p class="MsoNormal" style="MARGIN-LEFT: 21pt; TEXT-INDENT: -21pt; mso-margin-top-alt: auto; mso-margin-bottom-alt: auto; mso-list: l159 level1 lfo12; tab-stops: list 21.0pt"><span lang="EN-US" style="FONT-FAMILY: Wingdings">l<span style="FONT: 7pt 'Times New Roman'">         </span></span><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">Cache任何请求的元数据（ metadata ）并尽可能少的使用元数据 方法，其慢的程度一用便知</span></p><p class="MsoNormal" style="MARGIN-LEFT: 21pt; TEXT-INDENT: -21pt; mso-margin-top-alt: auto; mso-margin-bottom-alt: auto; mso-list: l159 level1 lfo12; tab-stops: list 21.0pt"><span lang="EN-US" style="FONT-FAMILY: Wingdings">l<span style="FONT: 7pt 'Times New Roman'">         </span></span><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">避免在元数据 查询中使用 null 参数</span></p><p class="MsoNormal" style="MARGIN-LEFT: 21pt; TEXT-INDENT: -21pt; mso-margin-top-alt: auto; mso-margin-bottom-alt: auto; mso-list: l159 level1 lfo12; tab-stops: list 21.0pt"><span lang="EN-US" style="FONT-FAMILY: Wingdings">l<span style="FONT: 7pt 'Times New Roman'">         </span></span><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">使用虚拟查询获得一行的元数据，不要使用getcolumns()（假如应用允许用户使用列数据，应用是使用getColumns来返回列的信息给用户还是准备一个虚拟查询而后调用getMetadata呢？</span></p><p class="MsoNormal" style="MARGIN-LEFT: 21pt; TEXT-INDENT: -21pt; mso-margin-top-alt: auto; mso-margin-bottom-alt: auto; mso-list: l159 level1 lfo12; tab-stops: list 21.0pt"><span lang="EN-US" style="FONT-FAMILY: Wingdings">l<span style="FONT: 7pt 'Times New Roman'">         </span></span><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">使用存储过程，避免多余的网络传输</span></p><p class="MsoNormal" style="MARGIN-LEFT: 21pt; TEXT-INDENT: -21pt; mso-margin-top-alt: auto; mso-margin-bottom-alt: auto; mso-list: l159 level1 lfo12; tab-stops: list 21.0pt"><span lang="EN-US" style="FONT-FAMILY: Wingdings">l<span style="FONT: 7pt 'Times New Roman'">         </span></span><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">在存储过程中使用参量，不要将数据挨个地放在statement中，最小化解析开销。此条针对DB2来说，其它数据库未必适用。SQL总是以字符串形式发送给DB2数据库，例如：</span><br /><font color="#ff0000">CallableStatement cstmt = conn.prepareCall ("call getCustName (12345)");<br />ResultSet rs = cstmt.executeQuery ();</font><br />DB2服务器必须解析该SQL，验证参量类型，并将参量转化为正确的数据类型。</p><p class="MsoNormal" style="MARGIN-LEFT: 21pt; TEXT-INDENT: -21pt; mso-margin-top-alt: auto; mso-margin-bottom-alt: auto; mso-list: l159 level1 lfo12; tab-stops: list 21.0pt"><span lang="EN-US" style="FONT-FAMILY: Wingdings">l<span style="FONT: 7pt 'Times New Roman'">         </span></span><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">对需要重复执行的statement使用预处理statement（PreparedStatement）</span></p><p class="MsoNormal" style="MARGIN-LEFT: 21pt; TEXT-INDENT: -21pt; mso-margin-top-alt: auto; mso-margin-bottom-alt: auto; mso-list: l159 level1 lfo12; tab-stops: list 21.0pt"><span lang="EN-US" style="FONT-FAMILY: Wingdings">l<span style="FONT: 7pt 'Times New Roman'">         </span></span><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">选择使用最佳游标：对连续读取使用游标；对双向滚动使用游标。对仅返回一行的查询避免使用游标。</span></p><p class="MsoNormal" style="MARGIN-LEFT: 21pt; TEXT-INDENT: -21pt; mso-margin-top-alt: auto; mso-margin-bottom-alt: auto; mso-list: l77 level1 lfo11; tab-stops: list 21.0pt"><span lang="EN-US" style="FONT-FAMILY: Wingdings">l<span style="FONT: 7pt 'Times New Roman'">         </span></span><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">在JVM中Cache频繁请求的数据，避免不必要的数据库请求</span></p><p class="MsoNormal" style="MARGIN-LEFT: 21pt; TEXT-INDENT: -21pt; mso-margin-top-alt: auto; mso-margin-bottom-alt: auto; mso-list: l77 level1 lfo11; tab-stops: list 21.0pt"><span lang="EN-US" style="FONT-FAMILY: Wingdings">l<span style="FONT: 7pt 'Times New Roman'">         </span></span><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">采用预读取机制， 批量取行，而不要一次一行 。调整批大小和预取行的数量。避免使用预取 BLOB 数据。</span></p><p class="MsoNormal" style="MARGIN-LEFT: 21pt; TEXT-INDENT: -21pt; mso-margin-top-alt: auto; mso-margin-bottom-alt: auto; mso-list: l77 level1 lfo11; tab-stops: list 21.0pt"><span lang="EN-US" style="FONT-FAMILY: Wingdings">l<span style="FONT: 7pt 'Times New Roman'">         </span></span><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">除非绝对需要，否则避免移动数据</span></p><p class="MsoNormal" style="MARGIN-LEFT: 21pt; TEXT-INDENT: -21pt; mso-margin-top-alt: auto; mso-margin-bottom-alt: auto; mso-list: l77 level1 lfo11; tab-stops: list 21.0pt"><span lang="EN-US" style="FONT-FAMILY: Wingdings">l<span style="FONT: 7pt 'Times New Roman'">         </span></span><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">在数据穿过网络之前要使流化数据（ Streamline data ）</span></p><p class="MsoNormal" style="MARGIN-LEFT: 21pt; TEXT-INDENT: -21pt; mso-margin-top-alt: auto; mso-margin-bottom-alt: auto; mso-list: l77 level1 lfo11; tab-stops: list 21.0pt"><span lang="EN-US" style="FONT-FAMILY: Wingdings">l<span style="FONT: 7pt 'Times New Roman'">         </span></span><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">避免每次处理一行，尽可能一起处理多行。</span></p><p class="MsoNormal" style="MARGIN-LEFT: 21pt; TEXT-INDENT: -21pt; mso-margin-top-alt: auto; mso-margin-bottom-alt: auto; mso-list: l77 level1 lfo11; tab-stops: list 21.0pt"><span lang="EN-US" style="FONT-FAMILY: Wingdings">l<span style="FONT: 7pt 'Times New Roman'">         </span></span><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">在表中统计个数（例如：使用 select count(*) from myTable,yourTable where …</span><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">）属于资源密集型的。试试首先选入临时表，仅返回该计数（count），然后发送精确的二次查询获得临时表中的行的子集。</span></p><p class="MsoNormal" style="MARGIN-LEFT: 21pt; TEXT-INDENT: -21pt; mso-margin-top-alt: auto; mso-margin-bottom-alt: auto; mso-list: l77 level1 lfo11; tab-stops: list 21.0pt"><span lang="EN-US" style="FONT-FAMILY: Wingdings">l<span style="FONT: 7pt 'Times New Roman'">         </span></span><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: Times New Roman; mso-hansi-font-family: Times New Roman">恰</span><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">当的使用 SQL 能减少资源请求。使用返回所需数据的最小值的查询：避免 select * 查询。一个返回小的数据子集的复杂查询，比一个简单的，返回超过所需的大量数据的简单查询更高效。</span></p><p class="MsoNormal" style="MARGIN-LEFT: 21pt; TEXT-INDENT: -21pt; mso-margin-top-alt: auto; mso-margin-bottom-alt: auto; mso-list: l77 level1 lfo11; tab-stops: list 21.0pt"><span lang="EN-US" style="FONT-FAMILY: Wingdings">l<span style="FONT: 7pt 'Times New Roman'">         </span></span><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">使你的查询尽可能精巧，例如：尽可能精确地最小化要传输的数据，使其是所需的子集</span></p><p class="MsoNormal" style="MARGIN-LEFT: 21pt; TEXT-INDENT: -21pt; mso-margin-top-alt: auto; mso-margin-bottom-alt: auto; mso-list: l77 level1 lfo11; tab-stops: list 21.0pt"><span lang="EN-US" style="FONT-FAMILY: Wingdings">l<span style="FONT: 7pt 'Times New Roman'">         </span></span><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">努力批量更新：将 statement 收集到一起，然后在一个事务里面一起执行。如果可能，使用有条件的逻辑和临时变量来达到 statement 批处理</span></p><p class="MsoNormal" style="MARGIN-LEFT: 21pt; TEXT-INDENT: -21pt; mso-margin-top-alt: auto; mso-margin-bottom-alt: auto; mso-list: l77 level1 lfo11; tab-stops: list 21.0pt"><span lang="EN-US" style="FONT-FAMILY: Wingdings">l<span style="FONT: 7pt 'Times New Roman'">         </span></span><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">永远不要让 DBMS 事务跨越用户输入</span></p><p class="MsoNormal" style="MARGIN-LEFT: 21pt; TEXT-INDENT: -21pt; mso-margin-top-alt: auto; mso-margin-bottom-alt: auto; mso-list: l77 level1 lfo11; tab-stops: list 21.0pt"><span lang="EN-US" style="FONT-FAMILY: Wingdings">l<span style="FONT: 7pt 'Times New Roman'">         </span></span><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">考虑使用乐观锁。乐观锁使用时间戳验证数据是否还没有被其他用户改变，否则事务失败</span></p><p class="MsoNormal" style="MARGIN-LEFT: 21pt; TEXT-INDENT: -21pt; mso-margin-top-alt: auto; mso-margin-bottom-alt: auto; mso-list: l77 level1 lfo11; tab-stops: list 21.0pt"><span lang="EN-US" style="FONT-FAMILY: Wingdings">l<span style="FONT: 7pt 'Times New Roman'">         </span></span><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: Times New Roman; mso-hansi-font-family: Times New Roman"><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">使用 恰当的更新，例如：更新行</span>/<span style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">表中已经存在的数据，而不要添加或者删除行</span>/<span style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">表。在适当的位置更新数据要比移动数据快得多，如果更新需要的空间比表设计能提供的更多，这可能是需要的。如果你设计的行需要空间初始化，更新将会更快。交易是你的表可能需要更多的磁盘空间，但可能速度更快。由于磁盘空间是便宜的，使用一点点能提高性能</span>，这应该说是非常有价值的投资</span></p><p class="MsoNormal" style="MARGIN-LEFT: 21pt; TEXT-INDENT: -21pt; mso-margin-top-alt: auto; mso-margin-bottom-alt: auto; mso-list: l77 level1 lfo11; tab-stops: list 21.0pt"><span lang="EN-US" style="FONT-FAMILY: Wingdings">l<span style="FONT: 7pt 'Times New Roman'">         </span></span><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">分开存储正在操作的数据和历史数据（更一般的情况是将频繁使用的数据和不常使用的数据分开存储）</span></p><p class="MsoNormal" style="MARGIN-LEFT: 21pt; TEXT-INDENT: -21pt; mso-margin-top-alt: auto; mso-margin-bottom-alt: auto; mso-list: l77 level1 lfo11; tab-stops: list 21.0pt"><span lang="EN-US" style="FONT-FAMILY: Wingdings">l<span style="FONT: 7pt 'Times New Roman'">         </span></span><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">尽可能小的保留你的操作数据集，避免必须读那些不相关的数据</span></p><p class="MsoNormal" style="MARGIN-LEFT: 21pt; TEXT-INDENT: -21pt; mso-margin-top-alt: auto; mso-margin-bottom-alt: auto; mso-list: l77 level1 lfo11; tab-stops: list 21.0pt"><span lang="EN-US" style="FONT-FAMILY: Wingdings">l<span style="FONT: 7pt 'Times New Roman'">         </span></span><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">DBMS可以很好的并行运转，尽量将应用设计成当和 DBMS交互时应用能做其他事情。</span></p><p class="MsoNormal" style="MARGIN-LEFT: 21pt; TEXT-INDENT: -21pt; mso-margin-top-alt: auto; mso-margin-bottom-alt: auto; mso-list: l77 level1 lfo11; tab-stops: list 21.0pt"><span lang="EN-US" style="FONT-FAMILY: Wingdings">l<span style="FONT: 7pt 'Times New Roman'">         </span></span><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">使用流水线操作和并行操作。 将应用设计成支持大量并行进程， 使应用运行更快。如果要处理多步，努力设计好应用，以使后来的步骤能够在任何优先的进程已经完成的数据部分上开始工作，而不是必须等到优先进程完成</span></p><p class="MsoNormal" style="MARGIN-LEFT: 21pt; TEXT-INDENT: -21pt; mso-margin-top-alt: auto; mso-margin-bottom-alt: auto; mso-list: l77 level1 lfo11; tab-stops: list 21.0pt"><span lang="EN-US" style="FONT-FAMILY: Wingdings">l<span style="FONT: 7pt 'Times New Roman'">      </span></span> <span style="FONT-FAMILY: 宋体; mso-ascii-font-family: Times New Roman; mso-hansi-font-family: Times New Roman">事物的保护级别越高，性能损失就越大。事物级别按增长的顺序为： TRANSACTION_NONE, TRANSACTION_READ_UNCOMMITTED, TRANSACTION_READ_COMMITTED, TRANSACTION_REPEATABLE_READ, TRANSACTION_SERIALIZABLE。使用Connection.setTransactionIsolation() 设置你想要的事物级别</span></p><p class="MsoNormal" style="MARGIN-LEFT: 21pt; TEXT-INDENT: -21pt; mso-margin-top-alt: auto; mso-margin-bottom-alt: auto; mso-list: l77 level1 lfo11; tab-stops: list 21.0pt"><span lang="EN-US" style="FONT-FAMILY: Wingdings">l<span style="FONT: 7pt 'Times New Roman'">      </span></span><span lang="EN-US" style="FONT-WEIGHT: normal; FONT-SIZE: 7pt; FONT-STYLE: normal; FONT-FAMILY: Wingdings; FONT-VARIANT: normal"></span><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: Times New Roman; mso-hansi-font-family: Times New Roman">默认的自动提交模式由于使每一个数据库命令都成为一个单独的事务，这会严重影响性能，关闭自动提交（Connection.setAutoCommit(false) ），明确声明事务</span></p><p class="MsoNormal" style="MARGIN-LEFT: 21pt; TEXT-INDENT: -21pt; mso-margin-top-alt: auto; mso-margin-bottom-alt: auto; mso-list: l77 level1 lfo11; tab-stops: list 21.0pt"><span lang="EN-US" style="FONT-FAMILY: Wingdings">l<span style="FONT: 7pt 'Times New Roman'">         </span></span><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: Times New Roman; mso-hansi-font-family: Times New Roman">通过整合多个事务为一个的批量操作，并在一个statement中使用Statement.addBatch() 和Statement.executeBatch()</span></p><p class="MsoNormal" style="MARGIN-LEFT: 21pt; TEXT-INDENT: -21pt; mso-margin-top-alt: auto; mso-margin-bottom-alt: auto; mso-list: l77 level1 lfo11; tab-stops: list 21.0pt"><span lang="EN-US" style="FONT-FAMILY: Wingdings">l<span style="FONT: 7pt 'Times New Roman'">     </span></span><span style="FONT: 7pt 'Times New Roman'"> </span><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: Times New Roman; mso-hansi-font-family: Times New Roman"> Savepoints (from JDBC3.0)需要昂贵的资源。一旦不再需要，就立刻使用Connection.releaseSavepoint()释放掉Savepoints</span></p><p class="MsoNormal" style="MARGIN-LEFT: 21pt; TEXT-INDENT: -21pt; mso-margin-top-alt: auto; mso-margin-bottom-alt: auto; mso-list: l77 level1 lfo11; tab-stops: list 21.0pt"><span lang="EN-US" style="FONT-FAMILY: Wingdings">l<span style="FONT: 7pt 'Times New Roman'">         </span></span><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: Times New Roman; mso-hansi-font-family: Times New Roman">ConnectionPoolDataSource (from JDBC3.0)和PooledConnection接口为连接池提供了built-in支持</span></p><p class="MsoNormal" style="MARGIN-LEFT: 21pt; TEXT-INDENT: -21pt; mso-margin-top-alt: auto; mso-margin-bottom-alt: auto; mso-list: l77 level1 lfo11; tab-stops: list 21.0pt"><span lang="EN-US" style="FONT-FAMILY: Wingdings">l<span style="FONT: 7pt 'Times New Roman'">         </span></span><span style="FONT: 7pt 'Times New Roman'">  </span><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: Times New Roman; mso-hansi-font-family: Times New Roman">使用setLogWriter() (from Driver, DataSource, or ConnectionPooledDataSource; from JDBC3.0) 帮助跟踪JDBC流</span></p><p class="MsoNormal" style="MARGIN-LEFT: 21pt; TEXT-INDENT: -21pt; mso-margin-top-alt: auto; mso-margin-bottom-alt: auto; mso-list: l77 level1 lfo11; tab-stops: list 21.0pt"><span lang="EN-US" style="FONT-FAMILY: Wingdings">l<span style="FONT: 7pt 'Times New Roman'">         </span></span><span style="FONT: 7pt 'Times New Roman'">  </span><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: Times New Roman; mso-hansi-font-family: Times New Roman">使用Connection.setReadOnly(true)优化只读数据库（操作）交互</span></p><p class="MsoNormal" style="MARGIN-LEFT: 21pt; TEXT-INDENT: -21pt; mso-margin-top-alt: auto; mso-margin-bottom-alt: auto; mso-list: l77 level1 lfo11; tab-stops: list 21.0pt"><span lang="EN-US" style="FONT-FAMILY: Wingdings">l<span style="FONT: 7pt 'Times New Roman'">         </span></span><span lang="EN-US"></span><span style="FONT: 7pt 'Times New Roman'">  <span style="FONT-SIZE: 10.5pt; FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'; mso-bidi-font-size: 12.0pt; mso-bidi-font-family: 'Times New Roman'; mso-font-kerning: 1.0pt; mso-ansi-language: EN-US; mso-fareast-language: ZH-CN; mso-bidi-language: AR-SA"></span></span><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: Times New Roman; mso-hansi-font-family: Times New Roman">使用Connection.nativeSQL()察看SQL查询如何在数据库种执行，帮助确保SQL已被优化</span></p><p class="MsoNormal" style="MARGIN-LEFT: 21pt; TEXT-INDENT: -21pt; mso-margin-top-alt: auto; mso-margin-bottom-alt: auto; mso-list: l77 level1 lfo11; tab-stops: list 21.0pt"><span lang="EN-US" style="FONT-FAMILY: Wingdings">l<span style="FONT: 7pt 'Times New Roman'">         </span></span><span style="FONT: 7pt 'Times New Roman'"> </span><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: Times New Roman; mso-hansi-font-family: Times New Roman">切记：一旦可能，立刻关闭Statement和ResultSet</span></p><p class="MsoNormal" style="MARGIN-LEFT: 21pt; TEXT-INDENT: -21pt; mso-margin-top-alt: auto; mso-margin-bottom-alt: auto; mso-list: l77 level1 lfo11; tab-stops: list 21.0pt"><span lang="EN-US" style="FONT-FAMILY: Wingdings">l<span style="FONT: 7pt 'Times New Roman'">         </span></span><span style="FONT: 7pt 'Times New Roman'"> </span><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: Times New Roman; mso-hansi-font-family: Times New Roman">使用DatabaseMetaData获得数据库功能性信息</span></p><p class="MsoNormal" style="MARGIN-LEFT: 21pt; TEXT-INDENT: -21pt; mso-margin-top-alt: auto; mso-margin-bottom-alt: auto; mso-list: l77 level1 lfo11; tab-stops: list 21.0pt"><span lang="EN-US" style="FONT-FAMILY: Wingdings">l<span style="FONT: 7pt 'Times New Roman'">         </span></span><span style="FONT: 7pt 'Times New Roman'"> </span><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: Times New Roman; mso-hansi-font-family: Times New Roman">一直捕捉和处理数据库警告和异常</span></p><p class="MsoNormal" style="MARGIN-LEFT: 21pt; TEXT-INDENT: -21pt; mso-margin-top-alt: auto; mso-margin-bottom-alt: auto; mso-list: l77 level1 lfo11; tab-stops: list 21.0pt"><span lang="EN-US" style="FONT-FAMILY: Wingdings">l<span style="FONT: 7pt 'Times New Roman'">         </span></span><span style="FONT: 7pt 'Times New Roman'"> </span><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: Times New Roman; mso-hansi-font-family: Times New Roman">使用最恰当的数据类型明确数据的类型，例如：以date类型存储日期，儿不要用varchar</span></p><p class="MsoNormal" style="MARGIN-LEFT: 21pt; TEXT-INDENT: -21pt; mso-margin-top-alt: auto; mso-margin-bottom-alt: auto; mso-list: l77 level1 lfo11; tab-stops: list 21.0pt"><span lang="EN-US" style="FONT-FAMILY: Wingdings">l<span style="FONT: 7pt 'Times New Roman'">         </span></span><span style="FONT: 7pt 'Times New Roman'"> </span><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: Times New Roman; mso-hansi-font-family: Times New Roman">使用可滚动ResultSet (JDBC 2.0)<br /><br />from: <a href="http://www.ijsp.net/2/2003-9/20/0000431.shtml">http://www.ijsp.net/2/2003-9/20/0000431.shtml</a></span></p><img src ="http://www.blogjava.net/weidagang2046/aggbug/84051.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/weidagang2046/" target="_blank">weidagang2046</a> 2006-11-28 14:59 <a href="http://www.blogjava.net/weidagang2046/articles/84051.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>我怎样创建一个序列号或是自动递增的字段？</title><link>http://www.blogjava.net/weidagang2046/articles/83633.html</link><dc:creator>weidagang2046</dc:creator><author>weidagang2046</author><pubDate>Sun, 26 Nov 2006 09:06:00 GMT</pubDate><guid>http://www.blogjava.net/weidagang2046/articles/83633.html</guid><wfw:comment>http://www.blogjava.net/weidagang2046/comments/83633.html</wfw:comment><comments>http://www.blogjava.net/weidagang2046/articles/83633.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/weidagang2046/comments/commentRss/83633.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/weidagang2046/services/trackbacks/83633.html</trackback:ping><description><![CDATA[
		<p>PostgreSQL 支持 SERIAL 数据类型。（字段定义为SERIAL后）将自动创建一个序列生成器，例如： </p>
		<pre>   CREATE TABLE person ( 
      id   SERIAL, 
      name TEXT 
   );
</pre>
		<p>会自动转换为以下SQL语句： </p>
		<pre>   CREATE SEQUENCE person_id_seq;
   CREATE TABLE person ( 
      id   INT4 NOT NULL DEFAULT nextval('person_id_seq'),
      name TEXT
   );<br /><br />from: <a href="http://www.pgsqldb.org/twiki/bin/view/PgSQL/PostgreFAQ#4.11.1">http://www.pgsqldb.org/twiki/bin/view/PgSQL/PostgreFAQ#4.11.1</a></pre>
<img src ="http://www.blogjava.net/weidagang2046/aggbug/83633.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/weidagang2046/" target="_blank">weidagang2046</a> 2006-11-26 17:06 <a href="http://www.blogjava.net/weidagang2046/articles/83633.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>放弃 ORM 改用 SqlMap 的 N 个理由</title><link>http://www.blogjava.net/weidagang2046/articles/83620.html</link><dc:creator>weidagang2046</dc:creator><author>weidagang2046</author><pubDate>Sun, 26 Nov 2006 08:02:00 GMT</pubDate><guid>http://www.blogjava.net/weidagang2046/articles/83620.html</guid><wfw:comment>http://www.blogjava.net/weidagang2046/comments/83620.html</wfw:comment><comments>http://www.blogjava.net/weidagang2046/articles/83620.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/weidagang2046/comments/commentRss/83620.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/weidagang2046/services/trackbacks/83620.html</trackback:ping><description><![CDATA[
		<p>1. 在项目中经常碰到的数据库分页查询, ORM 一般都支持的不好, 如果用 ORM, 这部分程序往往要自己扩展, 而 SqlMap 对各种查询语句不分彼此, 一概在 Map 文件里定义。</p>
		<p>2. 对一些稍微复杂些的语句, 例如在对金额等敏感数据操作时, 一个常用的操作序列是:<br />a. 先取出当前金额<br />b. 运算后得到更新的金额<br />c. 执行 Update 语句: Update &lt; tableName &gt; set amount= &lt; New amount &gt; where amount= &lt; Old amount &gt; <br />这种操作是 ORM 不能支持的, SqlMap 能很好的支持。</p>
		<p>3. SqlMap 的 Domain 对象可以直接放在业务层, 一般 ORM 的对数据访问的基类要放在数据访问层(因为带有对数据访问的接口, 放在业务层不合适), 增加了代码的冗余度。</p>
		<p>4. 用 ORM 的目的是什么, 最主要的目的是减少重复的底层编程工作量, SqlMap 完全可以做到。</p>
		<p>再说说 SqlMap 的不足:</p>
		<p>1. 因为不象 ORM 那样生成稳定可靠的对数据访问的基类, 所以要对 Map 操作做好充足的单元测试, 增加了测试的工作量。</p>
		<p>2. 每次改动数据库, Map 和 Domain 文件往往要手工修改, 因为 SqlMap 的灵活性, 往往我们会手工调整 Map 而不会直接使用 Generator 生成的代码。<br /><br />from: <a href="http://matrix.foresee.cn/blogs/simon/archives/001638.html">http://matrix.foresee.cn/blogs/simon/archives/001638.html</a></p>
<img src ="http://www.blogjava.net/weidagang2046/aggbug/83620.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/weidagang2046/" target="_blank">weidagang2046</a> 2006-11-26 16:02 <a href="http://www.blogjava.net/weidagang2046/articles/83620.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item>