第一篇:解析各种SQL连接字符串
解析各种SQL连接字符串
数据库对软件有着重要的作用 我想每个程序员无论是在工作还是学习当中都会跟数据库频
繁的打交道吧 所以一个好的程序员对数据库的操作要足够的熟练 想要跟数据库同心就必
须先跟它建立连接
这就好像你想用QQ跟一个人聊天 首先得确认一下对方有没有在线 有没有时间 对方在线
也有时间 我们才能和他交流 数据库也是如此 我们写的连接字符串就是用来找到我们想要
进行通信的那个数据库 然后确定它可以跟我们进行交互 然后才有我们对数据库一系列的操作 下面一起看看.NET中常用的连接字符串:
?4 1.data source=服务器名;database=数据库名;uid=数据库登录名;pwd=登录密码 2.server=服务器IP;Initial Catalog=数据库名;uid=数据库登录名;pwd=登录密码 3.data source=(local);initial catalog=数据库名;Integrated Security=True 4.data source=localhost;initial catalog=数据库名;Integrated Security=SSPI
.........其实连接字符串有好多种 上面的这几个是我感觉比较常用的方式 其实以上几个字符串互
相对应的字符作用都是一样的 只是名字换了一下 什么意思呢 就是说“data source”和
“server”“database”和“initial catalog” 这些对应的字符是等价的 如果把第一个连接
字符串里的“database”换成“initial catalog”效果是一样的integrated securify=true的意思就是以当前的windows身份登录 此时不需要数据库的账
号跟密码 就算你写上uid=啥:pwd=啥 也不起什么作用 但是当值为false的时候账号密码
就起作用了 integrated security后面的值可以是true yes false no 还可以是sspi(与
true是一个效果)还有一个跟integrated securify等价的字符——trusted_connection
同样它的值也可以是true/flase/yes/no/sspi 就不过多的说了 这种用windows身份登录的方式看起来不错 但是安全性似乎不太好 而且容易出错 所以还是推荐用账号密码的方式
登录数据库
还有值得注意的就是data source 后面的参数 它可以是数据库所在计算机的机器名 IP地
址 如果是本地数据库还可以是(local)或localhost 所以如果数据库是在本地那么写哪个
都可以了(不过还是不推荐使用使用local的写法 因为一般我们开发软件数据库至少要被
一个局域网访问)而如果需要远程访问数据库那就只能用机器名或IP的方式了
第二篇:Sql语句字符串中含有变量的写法
Sql语句字符串中含有变量的写法:
Step 1:书写sql语句实例
Select * from admin where uname=’pass’ and upwd=’123456’
Step2:将常量换成变量,并在两端加”+”
Select * from admin where uname=’+txtuname.text+’ and upwd=’+txtpwd.text+’
Step3:将被+隔开的字符串两端加上””
“Select * from admin where uname=’”+txtuname.text+”’ and upwd=’”+txtpwd.text+”’”
第三篇:SQL Server字符串处理函数
SQL Server字符串处理函数大全.txt SQL Server字符串处理函数大全2009年10月18日 星期日 08:48select 字段1 from 表1 where 字段1.IndexOf(“云”)=1;这条语句不对的原因是indexof()函数不是sql函数,改成sql对应的函数就可以了。left()是sql函数。
select 字段1 from 表1 where charindex('云',字段1)=1;字符串函数对二进制数据、字符串和表达式执行不同的运算。此类函数作用于CHAR、VARCHAR、BINARY、和VARBINARY 数据类型以及可以隐式转换为CHAR 或VARCHAR的数据类型。可以在SELECT 语句的SELECT 和WHERE 子句以及表达式中使用字符串函数。常用的字符串函数有:
一、字符转换函数
1、ASCII()返回字符表达式最左端字符的ASCII 码值。在ASCII()函数中,纯数字的字符串可不用‘’括起来,但含其它字符的字符串必须用‘’括起来使用,否则会出错。
2、CHAR()将ASCII 码转换为字符。如果没有输入0 ~ 255 之间的ASCII 码值,CHAR()返回NULL。
3、LOWER()和UPPER()LOWER()将字符串全部转为小写;UPPER()将字符串全部转为大写。
4、STR()把数值型数据转换为字符型数据。
STR(
二、去空格函数
1、LTRIM()把字符串头部的空格去掉。
2、RTRIM()把字符串尾部的空格去掉。
三、取子串函数
1、left()LEFT(
2、RIGHT()RIGHT(
3、SUBSTRING()SUBSTRING(
四、字符串比较函数
1、CHARINDEX()返回字符串中某个指定的子串出现的开始位置。
CHARINDEX(<’substring_expression’>,
2、PATINDEX()返回字符串中某个指定的子串出现的开始位置。
PATINDEX(<’%substring _expression%’>,
五、字符串操作函数
1、QUOTENAME()返回被特定字符括起来的字符串。
QUOTENAME(<’character_expression’>[,quote_ character])其中quote_ character 标明括字符串所用的字符,缺省值为“[]”。
2、REPLICATE()返回一个重复character_expression 指定次数的字符串。
REPLICATE(character_expression integer_expression)如果integer_expression 值为负值,则返回NULL。
3、REVERSE()将指定的字符串的字符排列顺序颠倒。REVERSE(
4、REPLACE()返回被替换了指定子串的字符串。
REPLACE(
4、SPACE()返回一个有指定长度的空白字符串。
SPACE(
5、STUFF()用另一子串替换字符串指定位置、长度的子串。
STUFF(
如果length 长度大于character_expression1 中 start_ position 以右的长度,则character_expression1 只保留首字符。
六、数据类型转换函数
1、CAST()CAST(
2、CONVERT()CONVERT(
1)data_type为SQL Server系统定义的数据类型,用户自定义的数据类型不能在此使用。2)length用于指定数据的长度,缺省值为30。3)把CHAR或VARCHAR类型转换为诸如INT或SAMLLINT这样的INTEGER类型、结果必须是带正号或负号的数值。
4)TEXT类型到CHAR或VARCHAR类型转换最多为8000个字符,即CHAR或VARCHAR数据类型是最大长度。
5)IMAGE类型存储的数据转换到BINARY或VARBINARY类型,最多为8000个字符。
6)把整数值转换为MONEY或SMALLMONEY类型,按定义的国家的货币单位来处理,如人民币、美元、英镑等。
7)BIT类型的转换把非零值转换为1,并仍以BIT类型存储。
8)试图转换到不同长度的数据类型,会截短转换值并在转换值后显示“+”,以标识发生了这种截断。
9)用CONVERT()函数的style 选项能以不同的格式显示日期和时间。style 是将DATATIME 和SMALLDATETIME 数据转换为字符串时所选用的由SQL Server 系统提供的转换样式编号,不同的样式编号有不同的输出格式。
七、日期函数
1、day(date_expression)返回date_expression中的日期值
2、month(date_expression)返回date_expression中的月份值
3、year(date_expression)返回date_expression中的年份值
4、DATEADD()DATEADD(
5、DATEDIFF()DATEDIFF(
6、DATENAME()DATENAME(
7、DATEPART()DATEPART(
8、GETDATE()以DATETIME 的缺省格式返回系统当前的日期和时间。
演讲稿
尊敬的老师们,同学们下午好:
我是来自10级经济学(2)班的学习委,我叫张盼盼,很荣幸有这次机会和大家一起交流担任学习委员这一职务的经验。
转眼间大学生活已经过了一年多,在这一年多的时间里,我一直担任着学习委员这一职务。回望这一年多,自己走过的路,留下的或深或浅的足迹,不仅充满了欢愉,也充满了淡淡的苦涩。一年多的工作,让我学到了很多很多,下面将自己的工作经验和大家一起分享。
学习委员是班上的一个重要职位,在我当初当上它的时候,我就在想一定不要辜负老师及同学们我的信任和支持,一定要把工作做好。要认真负责,态度踏实,要有一定的组织,领导,执行能力,并且做事情要公平,公正,公开,积极落实学校学院的具体工作。作为一名合格的学习委员,要收集学生对老师的意见和老师的教学动态。在很多情况下,老师无法和那么多学生直接打交道,很多老师也无暇顾及那么多的学生,特别是大家刚进入大学,很多人一时还不适应老师的教学模式。学习委员是老师与学生之间沟通的一个桥梁,学习委员要及时地向老师提出同学们的建议和疑问,熟悉老师对学生的基本要求。再次,学习委员在学习上要做好模范带头作用,要有优异的成绩,当同学们向我提出问题时,基本上给同学一个正确的回复。
总之,在一学年的工作之中,我懂得如何落实各项工作,如何和班委有效地分工合作,如何和同学沟通交流并且提高大家的学习积极性。当然,我的工作还存在着很多不足之处。比日:有的时候得不到同学们的响应,同学们不积极主动支持我的工作;在收集同学们对自己工作意见方面做得不够,有些事情做错了,没有周围同学的提醒,自己也没有发觉等等。最严重的一次是,我没有把英语四六级报名的时间,地点通知到位,导致我们班有4名同学错过报名的时间。这次事使我懂得了做事要脚踏实地,不能马虎。
在这次的交流会中,我希望大家可以从中吸取一些好的经验,带动本班级的学习风气,同时也相信大家在大学毕业后找到好的工作。谢谢大家!
第四篇:JSP连接SQL SERVER问题总结
首先是SQL 2000数据库的安装问题,在此我主要讲些关于SQL 2000的版本与操作系统的兼容性问题:SQL 2000总共有7个不同版本,适应不同等级用户的需求。
我试了一下,在XP系统下只有“个人开发版”能正常安装而不出现错误,所以大家在安装时要注意,具体安装时的配置参照相关说明就可以了。
下面说明如何连接到SQL 2000数据库,首先当然是要下载JDBC驱动程序,最好去微软官方网站下载,然后将下载到的三个JAR包放入你的WEB应用的WEB-INF/lib/下。接下来编写程序进行测试:
/*********************************************** /* /*DBTest.java /* /******************************************* */
import java.sql.*;
public class DBTest {
Connection con;
Statement sta;
ResultSet rs;
String driver;
String url;
String user;
String pwd;
public DBTest()
{
driver = “com.microsoft.jdbc.sqlserver.SQLServerDriver”;;
url
= “jdbc:microsoft:sqlserver:
//localhost:1433;DatabaseName =test”;
//test为数据库名
user
= “sa”;
pwd
= “sa”;
//请更改为你相应的用户和密码
init();
}
public void init()
{
try{
Class.forName(driver);
System.out.println(“driver is ok”);
con = DriverManager.getConnection(url,user,pwd);
System.out.println(“conection is ok”);
sta = con.createStatement();
rs = sta.executeQuery
(“select * from room”);
while(rs.next())
System.out.println
(rs.getInt(“roomNum”));
}catch(Exception e)
{
e.printStackTrace();
}
}
public static void main(String args[])
//自己替换[]
{
new DBTest();
} }
按道理讲,上边这段代码应该没错,可首先我们来看一下,如果sqlser服务器没有升级到sp3(在使用jdbc时,如果系统是xp或者2003务必要把sqlserver 升级到sp3,往上到处都有下的),我们看看运行结果:
driver is ok java.sql.SQLException: [Microsoft] [SQLServer 2000 Driver for JDBC] Error establis hing socket.at com.microsoft.jdbc.base.BaseExceptions.createException(Unknown Source)
at com.microsoft.jdbc.base.BaseExceptions.getException(Unknown Source)
at com.microsoft.jdbc.base.BaseExceptions.getException(Unknown Source)
at com.microsoft.jdbc.sqlserver.tds.TDSConnection.
at com.microsoft.jdbc.sqlserver.SQLServerImplConnection.open(Unknown Sou rce)
at com.microsoft.jdbc.base.BaseConnection.getNewImplConnection(Unknown S ource)
at com.microsoft.jdbc.base.BaseConnection.open(Unknown Source)
at com.microsoft.jdbc.base.BaseDriver.connect(Unknown Source)
at java.sql.DriverManager.getConnection(DriverManager.java:523)
at java.sql.DriverManager.getConnection(DriverManager.java:171)
at DbTest.init(DbTest.java:32)
at DbTest.
at DbTest.main(DbTest.java:46)Press any key to continue...出现上边错误的主要原因是默认的数据库服务器端口 1433没有打开,无法直接连接。如果升级到sp3则这个问题可以结决,我们再来看看升级之后,程序运行的结果:
driver is ok conection is ok java.sql.SQLException:
[Microsoft][SQLServer 2000 Driver for JDBC] [SQLServer]对
象名 ’room’ 无效。
at com.microsoft.jdbc.base.BaseExceptions.createException(Unknown Source)
at com.microsoft.jdbc.base.BaseExceptions.getException(Unknown Source)
at com.microsoft.jdbc.sqlserver.tds.TDSRequest.processErrorToken(Unknown Source)
at com.microsoft.jdbc.sqlserver.tds.TDSRequest.processReplyToken(Unknown Source)
at com.microsoft.jdbc.sqlserver.tds.TDSExecuteRequest.processReplyToken(Unknown Source)
at com.microsoft.jdbc.sqlserver.tds.TDSRequest.processReply(Unknown Sour ce)
at com.microsoft.jdbc.sqlserver.SQLServerImplStatement.getNextResultType(Unknown Source)
at com.microsoft.jdbc.base.BaseStatement.commonTransitionToState
(Unknown Source)
at com.microsoft.jdbc.base.BaseStatement.postImplExecu te(Unknown Source)
at com.microsoft.jdbc.base.BaseStatement.commonExecute(Unknown Source)
at com.microsoft.jdbc.base.BaseStatement.executeQueryInternal
(Unknown So urce)
at com.microsoft.jdbc.base.BaseStatement.executeQuery(Unknown Source)
at DbTest.init(DbTest.java:35)
at DbTest.
at DbTest.main(DbTest.java:46)Press any key to continue...在这儿,用户已经登陆上去,但是却不能访问里边的数据表,出现这个问题的原因在于sa用户为系统用户,它虽然能够登陆数据库,但是test数据库里边却没有这个用户的访问权限,所以,我们现在为这个数据库重新建立一个用户share,建立过程如下:
在test数据库中选重用户---〉新建用户--〉名称选择(这一步中有两个关键点:身份验证选sql身份验证,默认数据库选test)-〉建立新教色share,此时更改程序,将用户登陆名和密码修改一下,重新运行程序:
driver is ok conection is ok 1001 1002 1003 1004 1005 1006 Press any key to continue...这次顺利通过测试
其实这些小问题花了我一个晚上才解决,真是浪费时间,所以写下来希望能使遇到类似问题的朋友不要重蹈覆辙,在此提醒大家遇到问题时多上网查查,多在论坛里问问,这样你学到的会更多,更节省时间,更有效率。
总结:Sqlserve 和JDBC 的融合问题,关键涉及到sp3补丁(端口开放)还有用户问题,解决这两个问题之后,剩余的便是Sqlserver 操作问题了,还有一点在远程操作的时候,要把Sqlserver 组设置一下,在安全性里边亦将身份验证更改为Sqlserve 验证即可。
------------------------------------------------windows xp sp2下jsp连接sql server 2000
注意:由于博客的文本编辑带有html代码解释功能,本文中对代码做了一定的改动,把所有的<改成了#,所有的>改成了$,如果需要用代码可以用记事本的“替换”功能替换回来即可。基本信息:windows xp sp2、Tomcat5.0、J2SDK1.5、Sql server 2000开发版
一、安装JDK 这个没有什么好说的,需要注意的是JDK1.5和以前的版本有个不同点,它默认的安装目录在C:Program FilesJava目录下,由于路径名里有空格,所以可能会给以后的命令行执行有一定的影响,并且和以往的习惯也有些不同,所以建议安装到C盘目录下就可以了。我的路径是C:jdk1.5
二、安装Tomcat 一路NEXT下来就可以了,注意有会有一个设置用户名密码的提示。我是安装在C:Tomcat 5.0了。
三、安装SQL Server2000 注意在windos xp下只能装两个版本,一个是开发版,另一个好像是个人版,记不得了。装的时候也基本上是一路NEXT下来,不过有一个地方要选择是采用Windows NT验证方式,一种是采用混合验证模式,为了安全,建议采用后者。并且要给超级管理员sa设上密码。最重要的是,不要忘了给SQL Server 2000打上补丁。由于sql server 2000存在漏洞,我们宿舍楼的局域网就因为中毒机器太多而造成网络拥塞。在windows xp中就把1433端口屏蔽了。要启用这个端口,要安装一个sql server 2000的sp3补丁。
四、设置环境变量
path路径:在系统变量的path前面加上C:Tomcat 5.0bin;C:jdk1.5bin;classpath路径.;C:jdk1.5libdt.jar;C:jdk1.5libtools.jar;C:jdk1.5librt.jar;C:jdk1.5;C:Tomcat 5.0commonlib,其中的.代表当前路径,其它的可以简单的设为C:jdk1.5lib,但是最好还是一个一个列出来。注意这里你如果在连接数据库时用到的是JDBC-ODBC桥接的方式的话,还要把桥接的驱动的路径加在classpath里面。有一个细节,好像在jdk1.5中,这个驱动不在C:jdk1.5lib下,而是在C:jdk1.5jrelib的rt.jar中,可以适当的设置一下或者把这个文件拷贝到classpath能到达的地方。
java_home路径:C:jdk1.5,即是jdk的安装目录。
tomcat_home路径:C:Tomcat 5.0,即是tomcat的安装目录。catalina_home路径:C:Tomcat 5.0 catalina_base路径:C:Tomcat 5.0,这两个环境变量的作用是可以通过命令行方式来启动和关闭tomcat,方法是打开命令行提示窗口,键入startup就可以启动,键入shutdown就可以关闭了。注意不要强行关闭tomcat,因为既然给了一个关闭的方式必然有它的道理,为了安全起见,还是用shutdown来关闭较为合适。当然不设置这两个环境变量也可以,只不要启动时用鼠标来从开始菜单启动。
五、建立数据库表
建立一个数据库testdb,在它里面建立一个表users。里面有两列,name和password,数据类型都取默认的char类型就行了。注意在SQL SERVER中表的名字不能为user。否则会出现错误。
六、配置数据DSN 打开“控制面板->性能与维护->管理工具->数据源”,点击“系统DSN”,点击“添加”,数据驱动选择“SQL SERVER”,点击“完成”,在弹出的对话框的“名称”里填入此DSN的名称,比如test。“描述”可以不填,“服务器”选择本地,即(local)。点击“下一步”,将弹出一个新的对话框。选择“使用sql server验证”,在下方填入用户名和密码。点击“下一步”,在新弹出的对话框的上方,选中“更改默认的数据库为”,在下侧的下拉菜单里选择你要映射的数据库,这里我们选择testdb。点击“下一步”,再点击“完成”。在新的对话框下方有个“测试数据源”的按钮,点击它,如果出现“测试成功”则表示建立DSN成功。点击完成即可。注意以后在程序中访问数据库是根据这里的系统DSN的名称来访问数据库的,而非真实的数据库名,在这里即是通过test而非testdb。
七、写测试网页文件
这里我们写一个简单的功能,有两个页面,第一个页面adduser.htm负责与用户进行交互,它可以让用户输入用户名和密码,并且提交它,但是它自己不处理,而是由第二个页面文件处理。第二个页面为jdbc.jsp,它接受第一个页面传递过来的参数,然后把它插入到users表中,然后把表中所有的用户都检索出来并且显示它。把这两个文件放在一个文件夹jdbc中,在jdbc文件夹中新建一个文件夹,名称为WEB-INF,在它里面建一个web.xml文件,然后把jdbc文件夹放到tomcat的工作目录C:Tomcat 5.0webapps中。web.xml内容
#?xml version=“1.0” encoding=“ISO-8859-1”?$ #!DOCTYPE web-app PUBLIC “-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN” “http://java.sun.com/dtd/web-app_2_3.dtd”$ #web-app$ #display-name$My Web Application#/display-name$ #description$ A application for test.#/description$ #/web-app$ adduser.htm文件内容:
#html$ #head$ #title$Add User#/title$ #/head$ #body$ #form method=“POST” action=“jdbc.jsp”$ #p align=“center”$姓名
#input type=“text” name=“name” size=“20”$#/p$ #p align=“center”$密码
#input type=“password” name=“pwd” size=“20”$#/p$ #p align=“center”$
#input type=“submit” value=“提交” name=“B1”$#/p$ #/form$ #/body$ #/html$
jdbc.jsp文件内容: #html$
#head$#title$Add User to DB#/title$#/head$
#%@page import=“java.sql.*”%$
#%@page import=“com.microsoft.jdbc.sqlserver.SQLServerDriver”%$
#%@page import=“com.microsoft.jdbc.*”%$
#%@page import=“java.util.*”%$ #body$
#pre$ #% //Get parameters from request String name,pwd;int num;name=request.getParameter(“name”);pwd=request.getParameter(“pwd”);//add infomation to DB try{ Class.forName(“sun.jdbc.odbc.JdbcOdbcDriver”).newInstance();Connection con=DriverManager.getConnection(“jdbc:odbc:test”,“sa”,“like”);Statement stmt=con.createStatement();stmt.executeUpdate(“insert into users values('”+name+“','”+pwd+“')”);//query DB for display all users ResultSet rs=stmt.executeQuery(“select * from users”);//rs.first();out.println(“All user in DB!”);while(rs.next()){
out.print(“user name: ”);
out.print(rs.getString(“name”)+“
password: ”);
out.println(rs.getString(“password”));} num=stmt.getMaxRows();out.println(num);rs.close();stmt.close();con.close();}catch(Exception ex){
out.println(ex.getMessage());
} %$
#/pre$ #/body$ #/html$
八、测试效果
启动tomcat,启动浏览器,在浏览器的地址栏中键入http://localhost:8080/jdbc/adduser.htm,在出现 的网页中填入用户名密码,点击“提交”,即可跳到jdbc.jsp文件,列出此时数据库中所有的用户。
第五篇:。NET 性能优化方法总结与字符串连接优化
.NET 性能优化方法总结
Ver 1.0
2009-1-20
目录
1.C#语言方面...............................................................................................................4
1.1 垃圾回收.......................................................................................................4
1.1.1 避免不必要的对象创建.....................................................................4 1.1.2 不要使用空析构函数 ★...................................................................4 1.1.3 实现 IDisposable 接口.....................................................................4
1.2 String 操作....................................................................................................5
1.2.1 使用 StringBuilder 做字符串连接...................................................5 1.2.2 避免不必要的调用 ToUpper 或 ToLower 方法...........................5 1.2.3 最快的空串比较方法.........................................................................6
1.3 多线程...........................................................................................................6
1.3.1 线程同步.............................................................................................6 1.3.2 使用 ThreadStatic 替代 NameDataSlot ★.....................................7 1.3.3 多线程编程技巧.................................................................................7
1.4 类型系统.......................................................................................................8
1.4.1 避免无意义的变量初始化动作.........................................................8 1.4.2 ValueType 和 ReferenceType...........................................................8 1.4.3 尽可能使用最合适的类型.................................................................9
1.5 异常处理.....................................................................................................10
1.5.1 不要吃掉异常★...............................................................................10 1.5.2 不要吃掉异常信息★.......................................................................10 1.5.3 避免不必要的抛出异常...................................................................10 1.5.4 避免不必要的重新抛出异常...........................................................10 1.5.5 捕获指定的异常,不要使用通用的System.Exception.................10 1.5.6 要在finally里释放占用的资源......................................................11
1.6 反射.............................................................................................................11
1.6.1 反射分类...........................................................................................12 1.6.2 动态创建对象...................................................................................12 1.6.3 动态方法调用...................................................................................12 1.6.4 推荐的使用原则...............................................................................12
1.7 基本代码技巧.............................................................................................13
1.7.1 循环写法...........................................................................................13 1.7.2 拼装字符串.......................................................................................13 1.7.3 避免两次检索集合元素...................................................................13 1.7.4 避免两次类型转换...........................................................................14 1.7.5为字符串容器声明常量,不要直接把字符封装在双引号“ ”里面。.....................................................................................................................14 1.7.6 用StringBuilder代替使用字符串连接符 “+”...............................14 1.7.7 避免在循环体里声明变量,...........................................................15
1.8 Hashtable......................................................................................................15 1.8.1 Hashtable机理...................................................................................15 1.8.2 使用HashTale代替其他字典集合类型的情形:......................16
1.9 避免使用ArrayList。..............................................................................16 1.10从XML对象读取数据.............................................................................17 1.11 避免使用递归调用和嵌套循环,...........................................................17 1.12 使用适当的Caching策略来提高性能...................................................17
2.Ado.Net....................................................................................................................17
2.1 应用Ado.net的一些思考原则..................................................................18 2.2 Connection...................................................................................................18
2.2.1 在方法中打开和关闭连接...............................................................18 2.2.2 显式关闭连接...................................................................................18 2.2.3 确保连接池启用...............................................................................19 2.2.4 不要缓存连接...................................................................................19
2.3 Command.....................................................................................................19
2.3.1 使用ExecuteScalar和ExecuteNonQuery.......................................19 2.3.2 使用Prepare.....................................................................................19 2.3.3 使用绑定变量 ★.............................................................................19
2.4 DataReader...................................................................................................20
2.4.1 显式关闭DataReader.......................................................................20 2.4.2 用索引号访问代替名称索引号访问属性.......................................20 2.4.3 使用类型化方法访问属性...............................................................20 2.4.4 使用多数据集...................................................................................20
2.5 DataSet.........................................................................................................21
2.5.1 利用索引加快查找行的效率...........................................................21 2.使用DataView......................................................................................21
3.ASP.NET..................................................................................................................21
3.1 减少往返行程(Reduce Round Trips)...................................................21 3.2 避免阻塞和长时间的作业.........................................................................22 3.3 使用缓存.....................................................................................................22 3.4 多线程.........................................................................................................22 3.5 系统资源.....................................................................................................23 3.6 页面处理.....................................................................................................23 3.7 ViewState.....................................................................................................23
4.JScript.......................................................................................................................24
4.1 JScript性能优化的基本原则.....................................................................24 4.2 JScript语言本身的优化.............................................................................24 4.3 DOM相关...................................................................................................27 4.4 其他.............................................................................................................28
1.C#语言方面
1.1 垃圾回收
垃圾回收解放了手工管理对象的工作,提高了程序的健壮性,但副作用就是程序代码可能对于对象创建变得随意。1.1.1 避免不必要的对象创建
由于垃圾回收的代价较高,所以C#程序开发要遵循的一个基本原则就是避免不必要的对象创建。以下列举一些常见的情形。
1.1.1.1 避免循环创建对象 ★
如果对象并不会随每次循环而改变状态,那么在循环中反复创建对象将带来性能损耗。高效的做法是将对象提到循环外面创建。
1.1.1.2 在需要逻辑分支中创建对象
如果对象只在某些逻辑分支中才被用到,那么应只在该逻辑分支中创建对象。
1.1.1.3 使用常量避免创建对象
程序中不应出现如 new Decimal(0)之类的代码,这会导致小对象频繁创建及回收,正确的做法是使用Decimal.Zero常量。我们有设计自己的类时,也可以学习这个设计手法,应用到类似的场景中。
1.1.1.4 使用StringBuilder做字符串连接
1.1.2 不要使用空析构函数 ★
如果类包含析构函数,由创建对象时会在 Finalize 队列中添加对象的引用,以保证当对象无法可达时,仍然可以调用到 Finalize 方法。垃圾回收器在运行期间,会启动一个低优先级的线程处理该队列。相比之下,没有析构函数的对象就没有这些消耗。如果析构函数为空,这个消耗就毫无意义,只会导致性能降低!因此,不要使用空的析构函数。
在实际情况中,许多曾在析构函数中包含处理代码,但后来因为种种原因被注释掉或者删除掉了,只留下一个空壳,此时应注意把析构函数本身注释掉或删除掉。1.1.3 实现 IDisposable 接口
垃圾回收事实上只支持托管内在的回收,对于其他的非托管资源,例如
Window GDI 句柄或数据库连接,在析构函数中释放这些资源有很大问题。原因是垃圾回收依赖于内在紧张的情况,虽然数据库连接可能已濒临耗尽,但如果内存还很充足的话,垃圾回收是不会运行的。
C#的 IDisposable 接口是一种显式释放资源的机制。通过提供 using 语句,还简化了使用方式(编译器自动生成 try...finally 块,并在 finally 块中调用 Dispose 方法)。对于申请非托管资源对象,应为其实现 IDisposable 接口,以保证资源一旦超出 using 语句范围,即得到及时释放。这对于构造健壮且性能优良的程序非常有意义!
为防止对象的 Dispose 方法不被调用的情况发生,一般还要提供析构函数,两者调用一个处理资源释放的公共方法。同时,Dispose 方法应调用
System.GC.SuppressFinalize(this),告诉垃圾回收器无需再处理 Finalize 方法了。
1.2 String 操作
1.2.1 使用 StringBuilder 做字符串连接
String 是不变类,使用 + 操作连接字符串将会导致创建一个新的字符串。如果字符串连接次数不是固定的,例如在一个循环中,则应该使用 StringBuilder 类来做字符串连接工作。因为 StringBuilder 内部有一个 StringBuffer,连接操作不会每次分配新的字符串空间。只有当连接后的字符串超出 Buffer 大小时,才会申请新的 Buffer 空间。典型代码如下:
StringBuilder sb = new StringBuilder(256);for(int i = 0;i < Results.Count;i ++){
sb.Append(Results[i]);}
如果连接次数是固定的并且只有几次,此时应该直接用 + 号连接,保持程序简洁易读。实际上,编译器已经做了优化,会依据加号次数调用不同参数个数的 String.Concat 方法。例如:String str = str1 + str2 + str3 + str4;
会被编译为 String.Concat(str1, str2, str3, str4)。该方法内部会计算总的 String 长度,仅分配一次,并不会如通常想象的那样分配三次。作为一个经验值,当字符串连接操作达到 10 次以上时,则应该使用 StringBuilder。
这里有一个细节应注意:StringBuilder 内部 Buffer 的缺省值为 16,这个值实在太小。按 StringBuilder 的使用场景,Buffer 肯定得重新分配。经验值一般用 256 作为 Buffer 的初值。当然,如果能计算出最终生成字符串长度的话,则应该按这个值来设定 Buffer 的初值。使用 new StringBuilder(256)就将 Buffer 的初始长度设为了256。1.2.2 避免不必要的调用 ToUpper 或 ToLower 方法
String是不变类,调用ToUpper或ToLower方法都会导致创建一个新的字符串。如果被频繁调用,将导致频繁创建字符串对象。这违背了前面讲到的“避免频繁创建对象”这一基本原则。例如,bool.Parse方法本身已经是忽略大小写的,调用时不要调用ToLower方法。
另一个非常普遍的场景是字符串比较。高效的做法是使用 Compare 方法,这个方法可以做大小写忽略的比较,并且不会创建新字符串。
例:
const string C_VALUE = “COMPARE”;
if(String.Compare(sVariable, C_VALUE, true)== 0)
{
Console.Write(“SAME”);
}
还有一种情况是使用 HashTable 的时候,有时候无法保证传递 key 的大小写是否符合预期,往往会把 key 强制转换到大写或小写方法。实际上 HashTable 有不同的构造形式,完全支持采用忽略大小写的 key: new HashTable(StringComparer.OrdinalIgnoreCase)。1.2.3 最快的空串比较方法
将String对象的Length属性与0比较是最快的方法:if(str.Length == 0)
其次是与String.Empty常量或空串比较:if(str == String.Empty)或if(str == “")
注:C#在编译时会将程序集中声明的所有字符串常量放到保留池中(intern pool),相同常量不会重复分配。
1.3 多线程
1.3.1 线程同步
线程同步是编写多线程程序需要首先考虑问题。C#为同步提供了 Monitor、Mutex、AutoResetEvent 和 ManualResetEvent 对象来分别包装 Win32 的临界区、互斥对象和事件对象这几种基础的同步机制。C#还提供了一个lock语句,方便使用,编译器会自动生成适当的 Monitor.Enter 和 Monitor.Exit 调用。
1.3.1.1 同步粒度
同步粒度可以是整个方法,也可以是方法中某一段代码。为方法指定 MethodImplOptions.Synchronized 属性将标记对整个方法同步。例如:
[MethodImpl(MethodImplOptions.Synchronized)] public static SerialManager GetInstance(){
if(instance == null){
instance = new SerialManager();}
return instance;}
通常情况下,应减小同步的范围,使系统获得更好的性能。简单将整个方法标记为同步不是一个好主意,除非能确定方法中的每个代码都需要受同步保护。
1.3.1.2 同步策略
使用 lock 进行同步,同步对象可以选择 Type、this 或为同步目的专门构造的成员变量。
避免锁定Type★
锁定Type对象会影响同一进程中所有AppDomain该类型的所有实例,这不仅可能导致严重的性能问题,还可能导致一些无法预期的行为。这是一个很不好的习惯。即便对于一个只包含static方法的类型,也应额外构造一个static的成员变量,让此成员变量作为锁定对象。
避免锁定 this
锁定 this 会影响该实例的所有方法。假设对象 obj 有 A 和 B 两个方法,其中 A 方法使用 lock(this)对方法中的某段代码设置同步保护。现在,因为某种原因,B 方法也开始使用 lock(this)来设置同步保护了,并且可能为了完全不同的目的。这样,A 方法就被干扰了,其行为可能无法预知。所以,作为一种良好的习惯,建议避免使用 lock(this)这种方式。
使用为同步目的专门构造的成员变量
这是推荐的做法。方式就是 new 一个 object 对象,该对象仅仅用于同步目的。
如果有多个方法都需要同步,并且有不同的目的,那么就可以为些分别建立几个同步成员变量。
1.3.1.4 集合同步
C#为各种集合类型提供了两种方便的同步机制:Synchronized 包装器和 SyncRoot 属性。
// Creates and initializes a new ArrayList ArrayList myAL = new ArrayList();myAL.Add(” The “);myAL.Add(” quick “);myAL.Add(” brown “);myAL.Add(” fox “);
// Creates a synchronized wrapper around the ArrayList ArrayList mySyncdAL = ArrayList.Synchronized(myAL);
调用 Synchronized 方法会返回一个可保证所有操作都是线程安全的相同集合对象。考虑 mySyncdAL[0] = mySyncdAL[0] + ”test“ 这一语句,读和写一共要用到两个锁。一般讲,效率不高。推荐使用 SyncRoot 属性,可以做比较精细的控制。
1.3.2 使用 ThreadStatic 替代 NameDataSlot ★ 存取 NameDataSlot 的 Thread.GetData 和 Thread.SetData 方法需要线程同步,涉及两个锁:一个是 LocalDataStore.SetData 方法需要在 AppDomain 一级加锁,另一个是 ThreadNative.GetDomainLocalStore 方法需要在 Process 一级加锁。如果一些底层的基础服务使用了 NameDataSlot,将导致系统出现严重的伸缩性问题。
规避这个问题的方法是使用 ThreadStatic 变量。示例如下:
public sealed class InvokeContext {
[ThreadStatic]
private static InvokeContext current;
private Hashtable maps = new Hashtable();}
1.3.3 多线程编程技巧
1.3.3.1 使用 Double Check 技术创建对象
internal IDictionary KeyTable { get {
if(this._keyTable == null){
lock(base._lock){
if(this._keyTable == null){
this._keyTable = new Hashtable();} } }
return this._keyTable;} }
创建单例对象是很常见的一种编程情况。一般在 lock 语句后就会直接创建对象了,但这不够安全。因为在 lock 锁定对象之前,可能已经有多个线程进入到了第一个 if 语句中。如果不加第二个 if 语句,则单例对象会被重复创建,新的实例替代掉旧的实例。如果单例对象中已有数据不允许被破坏或者别的什么原因,则应考虑使用 Double Check 技术。
1.4 类型系统 1.4.1 避免无意义的变量初始化动作
CLR保证所有对象在访问前已初始化,其做法是将分配的内存清零。因此,不需要将变量重新初始化为0、false或null。
需要注意的是:方法中的局部变量不是从堆而是从栈上分配,所以C#不会做清零工作。如果使用了未赋值的局部变量,编译期间即会报警。不要因为有这个印象而对所有类的成员变量也做赋值动作,两者的机理完全不同!1.4.2 ValueType 和 ReferenceType
1.4.2.1 以引用方式传递值类型参数
值类型从调用栈分配,引用类型从托管堆分配。当值类型用作方法参数时,默认会进行参数值复制,这抵消了值类型分配效率上的优势。作为一项基本技巧,以引用方式传递值类型参数可以提高性能。
1.4.2.2 为 ValueType 提供 Equals 方法
.net 默认实现的 ValueType.Equals 方法使用了反射技术,依靠反射来获得所有成员变量值做比较,这个效率极低。如果我们编写的值对象其 Equals 方法要被用到(例如将值对象放到 HashTable 中),那么就应该重载 Equals 方法。
public struct Rectangle {
public double Length;public double Breadth;
public override bool Equals(object ob){
if(ob is Rectangle)
return Equels((Rectangle)ob))else
return false;}
private bool Equals(Rectangle rect){
return this.Length == rect.Length && this.Breadth == rect.Breach;} }
1.4.2.3 避免装箱和拆箱
C#可以在值类型和引用类型之间自动转换,方法是装箱和拆箱。装箱需要从堆上分配对象并拷贝值,有一定性能消耗。如果这一过程发生在循环中或是作为底层方法被频繁调用,则应该警惕累计的效应。
一种经常的情形出现在使用集合类型时。例如:
ArrayList al = new ArrayList();for(int i = 0;i < 1000;i ++){ al.Add(i);// Implicitly boxed because Add()takes an object }
int f =(int)al[ 0 ];// The element is unboxed
但是得当心!如果你像使用引用类型那么频繁的使用一个值类型的话,值类型的优势会很快被耗尽。比如,把一个值类型压到一个含有对象类型的群集。这叫做装箱,很耗用处理器周期,尤其是当你的代码在把它作为值(对它进行数学运算)和把它作为引用之间来回运行时。
1.4.3 尽可能使用最合适的类型
• 尽可能使用最合适的类型来描述数据,从而减少类型转换。
• 使用泛型来创建群集和其它的数据结构,这样,在运行时,它们就可以被实例化来存储刚好合适的类型。这节省了装箱/拆箱和类型转换的时间。
• 在C#中使用as,而不是is。关键字is用来查看引用是否可以被作为某个具体的类型,但是并不返回转换到这个类型的引用。所以,通常当你从is获得一个正的结果时,你首先应该cast——有效地执行两次cast。采用as关键词时,如果可用,则返回cast为新类型的引用;否则返回null。你可以查看null然后做你喜欢做的事情。整体来说,As方法要比is方法快50%。
1.5 异常处理
异常也是现代语言的典型特征。与传统检查错误码的方式相比,异常是强制性的(不依赖于是否忘记了编写检查错误码的代码)、强类型的、并带有丰富的异常信息(例如调用栈)。1.5.1 不要吃掉异常★
关于异常处理的最重要原则就是:不要吃掉异常。这个问题与性能无关,但对于编写健壮和易于排错的程序非常重要。这个原则换一种说法,就是不要捕获那些你不能处理的异常。
吃掉异常是极不好的习惯,因为你消除了解决问题的线索。一旦出现错误,定位问题将非常困难。除了这种完全吃掉异常的方式外,只将异常信息写入日志文件但并不做更多处理的做法也同样不妥。1.5.2 不要吃掉异常信息★
有些代码虽然抛出了异常,但却把异常信息吃掉了。
为异常披露详尽的信息是程序员的职责所在。如果不能在保留原始异常信息含义的前提下附加更丰富和更人性化的内容,那么让原始的异常信息直接展示也要强得多。千万不要吃掉异常。1.5.3 避免不必要的抛出异常 抛出异常和捕获异常属于消耗比较大的操作,在可能的情况下,应通过完善程序逻辑避免抛出不必要不必要的异常。与此相关的一个倾向是利用异常来控制处理逻辑。尽管对于极少数的情况,这可能获得更为优雅的解决方案,但通常而言应该避免。1.5.4 避免不必要的重新抛出异常
如果是为了包装异常的目的(即加入更多信息后包装成新异常),那么是合理的。但是有不少代码,捕获异常没有做任何处理就再次抛出,这将无谓地增加一次捕获异常和抛出异常的消耗,对性能有伤害。
1.5.5 捕获指定的异常,不要使用通用的System.Exception.//避免
try
{
}
catch(Exception exc)
{
}
//推荐
try
{
}
catch(System.NullReferenceException exc)
{
}
catch(System.ArgumentOutOfRangeException exc)
{
}
catch(System.InvalidCastException exc)
{
}
1.5.6 要在finally里释放占用的资源
使用Try...catch...finally时,要在finally里释放占用的资源如连接,文件流等,不然在Catch到错误后占用的资源不能释放。
try
{
...}
catch
{...}
finally
{
conntion.close()
}
1.6 反射
反射是一项很基础的技术,它将编译期间的静态绑定转换为延迟到运行期间的动态绑定。在很多场景下(特别是类框架的设计),可以获得灵活易于扩展的架构。但带来的问题是与静态绑定相比,动态绑定会对性能造成较大的伤害。1.6.1 反射分类
type comparison :类型判断,主要包括 is 和 typeof 两个操作符及对象实例上的 GetType 调用。这是最轻型的消耗,可以无需考虑优化问题。注意 typeof 运算符比对象实例上的 GetType 方法要快,只要可能则优先使用 typeof 运算符。
member enumeration : 成员枚举,用于访问反射相关的元数据信息,例如Assembly.GetModule、Module.GetType、Type对象上的IsInterface、IsPublic、GetMethod、GetMethods、GetProperty、GetProperties、GetConstructor调用等。尽管元数据都会被CLR缓存,但部分方法的调用消耗仍非常大,不过这类方法调用频度不会很高,所以总体看性能损失程度中等。
member invocation:成员调用,包括动态创建对象及动态调用对象方法,主要有Activator.CreateInstance、Type.InvokeMember等。1.6.2 动态创建对象
C#主要支持 5 种动态创建对象的方式:
1.Type.InvokeMember
2.ContructorInfo.Invoke
3.Activator.CreateInstance(Type)
4.Activator.CreateInstance(assemblyName, typeName)
5.Assembly.CreateInstance(typeName)
最快的是方式 3,与 Direct Create 的差异在一个数量级之内,约慢 7 倍的水平。其他方式,至少在 40 倍以上,最慢的是方式 4,要慢三个数量级。1.6.3 动态方法调用
方法调用分为编译期的早期绑定和运行期的动态绑定两种,称为Early-Bound Invocation和Late-Bound Invocation。Early-Bound Invocation可细分为Direct-call、Interface-call和Delegate-call。Late-Bound Invocation主要有Type.InvokeMember和MethodBase.Invoke,还可以通过使用LCG(Lightweight Code Generation)技术生成IL代码来实现动态调用。
从测试结果看,相比Direct Call,Type.InvokeMember要接近慢三个数量级;MethodBase.Invoke虽然比Type.InvokeMember要快三倍,但比Direct Call仍慢270倍左右。可见动态方法调用的性能是非常低下的。我们的建议是:除非要满足特定的需求,否则不要使用!1.6.4 推荐的使用原则
模式
1. 如果可能,则避免使用反射和动态绑定
2. 使用接口调用方式将动态绑定改造为早期绑定
3. 使用Activator.CreateInstance(Type)方式动态创建对象
4. 使用typeof操作符代替GetType调用
反模式
1. 在已获得Type的情况下,却使用Assembly.CreateInstance(type.FullName)
1.7 基本代码技巧
这里描述一些应用场景下,可以提高性能的基本代码技巧。对处于关键路径的代码,进行这类的优化还是很有意义的。普通代码可以不做要求,但养成一种好的习惯也是有意义的。1.7.1 循环写法
可以把循环的判断条件用局部变量记录下来。局部变量往往被编译器优化为直接使用寄存器,相对于普通从堆或栈中分配的变量速度快。如果访问的是复杂计算属性的话,提升效果将更明显。for(int i = 0, j = collection.GetIndexOf(item);i < j;i++)
需要说明的是:这种写法对于CLR集合类的Count属性没有意义,原因是编译器已经按这种方式做了特别的优化。1.7.2 拼装字符串
拼装好之后再删除是很低效的写法。有些方法其循环长度在大部分情况下为1,这种写法的低效就更为明显了:
public static string ToString(MetadataKey entityKey){
string str = ”“;
object [] vals = entityKey.values;for(int i = 0;i < vals.Length;i ++){
str += ” , “ + vals[i].ToString();}
return str == ”“ ? ”“ : str.Remove(0 , 1);}
推荐下面的写法:
if(str.Length == 0)str = vals[i].ToString();else
str += ” , “ + vals[i].ToString();其实这种写法非常自然,而且效率很高,完全不需要用个Remove方法绕来绕去。1.7.3 避免两次检索集合元素
获取集合元素时,有时需要检查元素是否存在。通常的做法是先调用ContainsKey(或Contains)方法,然后再获取集合元素。这种写法非常符合逻辑。
但如果考虑效率,可以先直接获取对象,然后判断对象是否为null来确定元素是否存在。对于Hashtable,这可以节省一次GetHashCode调用和n次Equals比较。
如下面的示例:
public IData GetItemByID(Guid id){
IData data1 = null;
if(this.idTable.ContainsKey(id.ToString()){
data1 = this.idTable[id.ToString()] as IData;}
return data1;}
其实完全可用一行代码完成:return this.idTable[id] as IData;1.7.4 避免两次类型转换
考虑如下示例,其中包含了两处类型转换:
if(obj is SomeType){
SomeType st =(SomeType)obj;st.SomeTypeMethod();}
效率更高的做法如下:
SomeType st = obj as SomeType;if(st!= null){
st.SomeTypeMethod();}
1.7.5为字符串容器声明常量,不要直接把字符封装在双引号” “里面。
//避免
//
MyObject obj = new MyObject();
obj.Status = ”ACTIVE“;
//推荐
const string C_STATUS = ”ACTIVE“;
MyObject obj = new MyObject();
obj.Status = C_STATUS;
1.7.6 用StringBuilder代替使用字符串连接符
//避免
String sXML = ” “;
sXML += ”“;
sXML += ”Data“;
sXML += ”“;
sXML += ”“;
//推荐
StringBuilder sbXML = new StringBuilder();
sbXML.Append(” “);
sbXML.Append(”“);
sbXML.Append(”Data“);
sbXML.Append(”“);
sbXML.Append(”“);
1.7.7 避免在循环体里声明变量,应该在循环体外声明变量,在循环体里初始化。
//避免
for(int i=0;i<10;i++)
+”
“
{
SomeClass objSC = new SomeClass();} //推荐
SomeClass objSC = null;for(int i=0;i<10;i++){
objSC = new SomeClass();)
1.8 Hashtable 1.8.1 Hashtable机理
Hashtable是一种使用非常频繁的基础集合类型。需要理解影响Hashtable的效率有两个因素:一是散列码(GetHashCode方法),二是等值比较(Equals方法)。Hashtable首先使用键的散列码将对象分布到不同的存储桶中,随后在该特定的存储桶中使用键的Equals方法进行查找。
良好的散列码是第一位的因素,最理想的情况是每个不同的键都有不同的散列码。Equals方法也很重要,因为散列只需要做一次,而存储桶中查找键可能需要做多次。从实际经验看,使用Hashtable时,Equals方法的消耗一般会占到一半以上。
System.Object类提供了默认的GetHashCode实现,使用对象在内存中的地址作为散列码。我们遇到过一个用Hashtable来缓存对象的例子,每次根据传递的OQL表达式构造出一个ExpressionList对象,再调用QueryCompiler的方法编译得到CompiledQuery对象。以ExpressionList对象和CompiledQuery对象作为键值对存储到Hashtable中。ExpressionList对象没有重载GetHashCode实现,其超类ArrayList也没有,这样最后用的就是System.Object类的GetHashCode实现。由于ExpressionList对象会每次构造,因此它的HashCode每次都不同,所以这个CompiledQueryCache根本就没有起到预想的作用。这个小小的疏漏带来了重大的性能问题,由于解析OQL表达式频繁发生,导致CompiledQueryCache不断增长,造成服务器内存泄漏!解决这个问题的最简单方法就是提供一个常量实现,例如让散列码为常量0。虽然这会导致所有对象汇聚到同一个存储桶中,效率不高,但至少可以解决掉内存泄漏问题。当然,最终还是会实现一个高效的GetHashCode方法的。
以上介绍这些Hashtable机理,主要是希望大家理解:如果使用Hashtable,你应该检查一下对象是否提供了适当的GetHashCode和Equals方法实现。否则,有可能出现效率不高或者与预期行为不符的情况。
1.8.2 使用HashTale代替其他字典集合类型的情形:
其他字典集合类型(如StringDictionary,NameValueCollection,HybridCollection),存放少量数据的时候可以使用HashTable。很多非泛型集合类都有对应的泛型集合类,下面是常用的非泛型集合类以及对应的泛型集合类:
非泛型集合类 泛型集合类
ArrayList List
HashTable DIctionary
SortedList SortedList
我们用的比较多的非泛型集合类主要有 ArrayList类 和 HashTable类。我们经常用HashTable 来存储将要写入到数据库或者返回的信息,在这之间要不断的进行类型的转化,增加了系统装箱和拆箱的负担,如果我们操纵的数据类型相对确定的化
用 Dictionary
1.9 避免使用ArrayList。
因为任何对象添加到ArrayList都要封箱为System.Object类型,从ArrayList取出数据时,要拆箱回实际的类型。建议使用自定义的集合类型代替ArrayList。.net 2.0提供了一个新的类型,叫泛型,这是一个强类型,使用泛型集合就可以避免了封箱和拆箱的发生,提高了性能。
1.10从XML对象读取数据
如果只是从XML对象读取数据,用只读的XPathDocument代替XMLDocument,可以提高性能
//避免
XmlDocument xmld = new XmlDocument();
xmld.LoadXml(sXML);
txtName.Text = xmld.SelectSingleNode(”/packet/child“).InnerText;
.//推荐
XPathDocument xmldContext = new XPathDocument(new StringReader(oContext.Value));
XPathNavigator xnav = xmldContext.CreateNavigator();
XPathNodeIterator xpNodeIter = xnav.Select(”packet/child“);
iCount = xpNodeIter.Count;
xpNodeIter = xnav.SelectDescendants(XPathNodeType.Element, false);
while(xpNodeIter.MoveNext())
{
sCurrValues += xpNodeIter.Current.Value+”~“;
}
}
1.11 避免使用递归调用和嵌套循环,使用他们会严重影响性能,在不得不用的时候才使用。
1.12 使用适当的Caching策略来提高性能
2.Ado.Net
2.1 应用Ado.net的一些思考原则
1.根据数据使用的方式来设计数据访问层 2.缓存数据,避免不必要的操作 3.使用服务帐户进行连接 4.必要时申请,尽早释放 5.关闭可关闭的资源 6.减少往返
7.仅返回需要的数据 8.选择适当的事务类型 9.使用存储过程
2.2 Connection 数据库连接是一种共享资源,并且打开和关闭的开销较大。Ado.net默认启用了连接池机制,关闭连接不会真的关闭物理连接,而只是把连接放回到连接池中。因为池中共享的连接资源始终是有限的,如果在使用连接后不尽快关闭连接,那么就有可能导致申请连接的线程被阻塞住,影响整个系统的性能表现。2.2.1 在方法中打开和关闭连接
这个原则有几层含义:
1.主要目的是为了做到必要时申请和尽早释放
2.不要在类的构造函数中打开连接、在析构函数中释放连接。因为这将依赖于垃圾回收,而垃圾回收只受内存影响,回收时机不定
3.不要在方法之间传递连接,这往往导致连接保持打开的时间过长
这里强调一下在方法之间传递连接的危害:曾经在压力测试中遇到过一个测试案例,当增大用户数的时候,这个案例要比别的案例早很久就用掉连接池中的所有连接。经分析,就是因为A方法把一个打开的连接传递到了B方法,而B方法又调用了一个自行打开和关闭连接的C方法。在A方法的整个运行期间,它至少需要占用两条连接才能够成功工作,并且其中的一条连接占用时间还特别长,所以造成连接池资源紧张,影响了整个系统的可伸缩性!
2.2.2 显式关闭连接
Connection对象本身在垃圾回收时可以被关闭,而依赖垃圾回收是很不好的策略。推荐使用using语句显式关闭连接,如下例:
using(SqlConnection conn = new SqlConnection(connString)){
conn.Open();
} // Dispose is automatically called on the conn variable here
2.2.3 确保连接池启用
Ado.net是为每个不同的连接串建立连接池,因此应该确保连接串不会出现与具体用户相关的信息。另外,要注意连接串是大小写敏感的。2.2.4 不要缓存连接
例如,把连接缓存到Session或Application中。在启用连接池的情况下,这种做法没有任何意义。
2.3 Command
2.3.1 使用ExecuteScalar和ExecuteNonQuery
如果想返回像Count(*)、Sum(Price)或Avg(Quantity)那样的单值,可以使用ExecuteScalar方法。ExecuteScalar返回第一行第一列的值,将结果集作为标量值返回。因为单独一步就能完成,所以ExecuteScalar不仅简化了代码,还提高了性能。
使用不返回行的SQL语句时,例如修改数据(INSERT、UPDATE或DELETE)或仅返回输出参数或返回值,请使用ExecuteNonQuery。这避免了用于创建空DataReader的任何不必要处理。2.3.2 使用Prepare
当需要重复执行同一SQL语句多次,可考虑使用Prepare方法提升效率。需要注意的是,如果只是执行一次或两次,则完全没有必要。例如:
cmd.CommandText = ”insert into Table1(Col1, Col2)values(@val1, @val2)“;
cmd.Parameters.Add(”@val1“, SqlDbType.Int, 4, ”Col1“);cms.Parameters.Add(”@val2“, SqlDbType.NChar, 50, ”Col2“);
cmd.Parameters[0].Value = 1;
cmd.Parameters[1].Value = ”XXX“;cmd.Prepare();
cmd.ExecuteNonQuery();
cmd.Parameters[0].Value = 2;
cmd.Parameters[1].Value = ”YYY“;cmd.ExecuteNonQuery();
cmd.Parameters[0].Value = 3;
cmd.Parameters[1].Value = ”ZZZ“;cmd.ExecuteNonQuery();
2.3.3 使用绑定变量 ★
SQL语句需要先被编译成执行计划,然后再执行。如果使用绑定变量的方式,那么这个执行计划就可以被后续执行的SQL语句所复用。而如果直接把参数合并到了SQL语句中,由于参数值千变万化,执行计划就难以被复用了。例如上面Prepare一节给出的示例,如果把参数值直接写到insert语句中,那么上面的四次调用将需要编译四次执行计划。
为避免这种情况造成性能损失,要求一律使用绑定变量方式。
2.4 DataReader
DataReader最适合于访问只读的单向数据集。与DataSet不同,数据集并不全部在内存中,而是随不断发出的read请求,一旦发现数据缓冲区中的数据均被读取,则从数据源传输一个数据缓冲区大小的数据块过来。另外,DataReader保持连接,DataSet则与连接断开。2.4.1 显式关闭DataReader
与连接类似,也需要显式关闭DataReader。另外,如果与DataReader关联的Connection仅为DataReader服务的话,可考虑使用Command对象的ExecuteReader(CommandBehavior.CloseConnection)方式。这可以保证当DataReader关闭时,同时自动关闭Connection。2.4.2 用索引号访问代替名称索引号访问属性
从Row中访问某列属性,使用索引号的方式比使用名称方式有细微提高。如果会被频繁调用,例如在循环中,那么可考虑此类优化。示例如下:
cmd.CommandText = ”select Col1, Col2 from Table1“;SqlDataReader dr = cmd.ExecuteReader();
int col1 = dr.GetOrdinal(”Col1“);int col2 = dr.GetOrdinal(”Col2“);
while(dr.Read()){
Console.WriteLine(dr[col1] + ”_“ + dr[col2]);}
2.4.3 使用类型化方法访问属性
从Row中访问某列属性,用GetString、GetInt32这种显式指明类型的方法,其效率较通用的GetValue方法有细微提高,因为不需要做类型转换。2.4.4 使用多数据集
部分场景可以考虑一次返回多数据集来降低网络交互次数,提升效率。示例如下:
cmd.CommandText = ”StoredProcedureName“;// The stored procedure returns multiple result sets.SqlDataReader dr = cmd.ExecuteReader();
while(dr.read())// read first result set
dr.NextResult();
while(dr.read())//
2.5 DataSet
2.5.1 利用索引加快查找行的效率
如果需要反复查找行,建议增加索引。有两种方式:
1.设置DataTable的PrimaryKey
适用于按PrimaryKey查找行的情况。注意此时应调用DataTable.Rows.Find方法,一般惯用的Select方法不能利用索引。2.使用DataView
适用于按Non-PrimaryKey查找行的情况。可为DataTable创建一个DataView,并通过SortOrder参数指示建立索引。此后使用Find或FindRows查找行。
3.ASP.NET
3.1 减少往返行程(Reduce Round Trips)
使用下面的方法可以减少Web服务器和Browser之间的往返行程:
1.为Browser启用缓存
如果呈现的内容是静态的或变化周期较长,应启用Browser缓存,避免发出冗余的http请求。2.缓冲页面输出
如果可能,则尽量缓冲页面输出,处理结束后再一次传送到客户端,这可以避免频繁传递小块内容所造成的多次网络交互。由于这种方式在页面处理结束之前客户端无法看到页面内容,因此如果一个页面的尺寸较大的话,可考虑使用Response.Flush方法。该方法强制输出迄今为止在缓冲区中的内容,你应当采用合理的算法控制调用Response.Flush方法的次数。
3.使用Server.Transfer重定向请求
使用Server.Transfer方法重定向请求优于Response.Redirect方法。原因是Response.Redirect会向Broswer回送一个响应头,在响应头中指出重定向的URL,之后Brower使用新的URL重新发出请求。而Server.Transfer方法直接是一个简单的服务端调用,完全没有这些开销!
需要注意Server.Transfer有局限性:第一,它会跳过安全检查;第二,只适用于在同一Web应用内的页面间跳转。
3.2 避免阻塞和长时间的作业 如果需要运行阻塞或长时间运行的操作,可以考虑使用异步调用的机制,以便Web服务器能够继续处理其它的请求。
1.使用异步方式调用Web服务和远程对象
只要有可能就要避免在请求的处理过程中对Web服务和远程对象的同步调用,因为它占用的是的ASP.NET 线程池中的工作线程,这将直接影响Web服务器响应其它请求的能力。
2.考虑给不需要返回值的Web方法或远程对象的方法添加OneWay属性
这种模式能让Web Server调用之后就立即返回。可根据实际情况决定是否使用这种方法。
3.使用工作队列
将作业提交到服务器上的工作队列中。客户端通过发送请求来轮询作业的执行结果。
3.3 使用缓存
缓存能在很大程度上决定ASP.NET应用的最终性能。Asp.net支持页面输出缓存和页面部分缓存,并提供Cache API,供应用程序缓存自己的数据。是否使用缓存可考虑下面的要点:
1.识别创建与访问代价较大的数据
2.评估需要缓存数据的易变性
3.评估数据的使用频次
4.将要缓存数据中易变数据和不变数据分离,只缓存不变数据
5.选择合适的缓存机制(除Asp.net Cache外,Application state和Session state也可以作为缓存使用)
3.4 多线程
1.避免在请求处理过程中创建线程
在执行请求的过程中创建线程是一种代价较大的操作,会严重影响Web Server的性能。如果后续的操作必须用线程完成,建议通过thread pool来创建/管理线程。
2.不要依赖线程数据槽或线程静态变量
由于执行请求的线程是ASP.NET thread pool中的工作线程,同一个Client的两次请求不一定由相同的线程来处理。
3.避免阻塞处理请求的线程
参考”避免阻塞和长时间的作业“小节。
4.避免异步调用
这和1的情况类似。异步调用会导致创建新的线程,增加服务器的负担。所以,如果没有并发的作业要执行,就不要执行异步调用。
3.5 系统资源
1.考虑实现资源池以提升性能
2.明确地调用Dispose或Close释放系统资源 3.不要缓存或长时间占用资源池中的资源 4.尽可能晚的申请,尽可能早的释放
3.6 页面处理
1.尽量减小Page的尺寸
包括缩短控件的名称、CSS的class的名称、去掉无谓空行和空格、禁用不需要的ViewState
2.启用页面输出的缓冲区(Buffer)
如果Buffer的机制被关闭,可以用下面的方法打开。
使用程序打开页面输出缓存:
Response.BufferOutput = true;
使用@Page开关打开页面输出缓冲机制:
<%@ Page Buffer = ”true“ %>
使用Web.config或Machine.config配置文件的
节点:
3.利用Page.IsPostBack优化页面输出
4.通过分离页面的不同的内容,来提高缓存效率和减少呈现的时间
5.优化复杂和代价较大的循环
6.合理利用客户端的计算资源,将一些操作转移到客户端进行
3.7 ViewState
ViewState是Asp.net为服务端控件在页面回传之间跟踪状态信息而设计的一种机制。
1.关闭ViewState
如果不需要跟踪页面状态,例如页面不会 回传(PostBack)、不需要处理服务端控件事件或者每次页面刷新时都会重新计算控件内容,那么就不需要用ViewState来记录页面状态了。可以对特定的WebControl设置EnableViewState属性,也可以在页面一级设置:
<%@ Page EnableViewState=”false“ %>
2.在恰当的时间点初始化控件属性
ASP.NET的控件在执行构造函数、初始化的期间设置的属性不会被跟踪变化;而在初始化阶段之后对属性的修改都会被跟踪,并最终记录到IE页面的__VIEWSTATE之中。所以,选择合理的初始化控件属性的执行点,能有效的减小页面尺寸。
3.谨慎选择放到ViewState中的内容
放到ViewState中的内容会被序列化/反序列化,Asp.net为String、Integer、Boolean等基本类型的序列化做了优化,如果Array、ArrayList、HashTable存储的是基本类型效率也较高,但其它类型则需要提供类型转换器(Type Converter),否则将使用代价昂贵的二进制序列化程序。
4.JScript
4.1 JScript性能优化的基本原则
1.尽可能少地减少执行次数。毕竟对解释语言来说,每一个执行步骤,都需要和解释引擎做一次交互。
2.尽可能使用语言内置的功能,比如串链接。
3.尽可能使用系统提供的API来进行优化。因为这些API是编译好的二进制代码,执行效率很高。
4.书写最正确的代码。容错功能是要付出性能代价的。
4.2 JScript语言本身的优化
4.2.1 变量
1.尽量使用局部变量。
因为全局变量其实是全局对象的成员,而局部变量在栈上定义,优先查找,性能相对于全局变量要高。
2.尽量在一个语句中做定义变量和赋值。
3.省略不必要的变量定义。
如果变量的定义可以被一个常量替代,就直接使用常量。
4.使用Object语法对对象赋值。Object的赋值语法在操作复杂对象时效率更高。
例如,可以将下面的代码:
car = new Object();car.make = ”Honda“;car.model = ”Civic“;
car.transmission = ”manual“;car.miles = 100000;
car.condition = ”needs work“;替换成:
car = {
make: ”Honda“, model: ”Civic“,transmission: ”manual“, miles: 100000,condition: ”needs work“ }
4.2.2 对象缓存
1.缓存对象查找的中间结果。
因为JavaScript的解释性,所以a.b.c.d.e,需要进行至少4次查询操作,先检查a再检查a中的b,再检查b中的c,如此往下。所以如果这样的表达式重复出现,只要可能,应该尽量少出现这样的表达式,可以利用局部变量,把它放入一个临时的地方进行查询。
2.缓存创建时间较长的对象。
自定义高级对象和Date、RegExp对象在构造时都会消耗大量时间。如果可以复用,应采用缓存的方式。
4.2.3 字符串操作
1.使用”+=“ 追加字符串,使用”+“来连接字符串。
如果是追加字符串,最好使用s+=anotherStr操作,而不是要使用s=s+anotherStr。
如果要连接多个字符串,应该使用”+“,如:
s+=a;
s+=b;
s+=c;
应该写成
s+=a + b + c;
2.连接大量的字符串,应使用Array的join方法。如果是收集字符串,最好使用JavaScript数组缓存,最后使用join方法连接起来,如下:
var buf = new Array();for(var i = 0;i < 100;i++){
buf.push(i.toString());}
var all = buf.join(”“);
4.2.4 类型转换
1.使用Math.floor()或者Math.round()将浮点数转换成整型。
浮点数转换成整型,这个更容易出错,很多人喜欢使用parseInt(),其实parseInt()是用于将字符串转换成数字,而不是浮点数和整型之间的转换,我们应该使用Math.floor()或者Math.round()。
对象查找中的问题不一样,Math是内部对象,所以Math.floor()其实并没有多少查询方法和调用的时间,速度是最快的。
2.自定义的对象,推荐定义和使用toString()方法来进行类型转换。
对于自定义的对象,如果定义了toString()方法来进行类型转换的话,推荐显式调用toString()。因为内部的操作在尝试所有可能性之后,会尝试对象的toString()方法尝试能否转化为String,所以直接调用这个方法效率会更高。
4.2.5 循环的优化
1.尽可能少使用for(in)循环。
在JavaScript中,我们可以使用for(;;),while(),for(in)三种循环,事实上,这三种循环中for(in)的效率极差,因为他需要查询散列键,只要可以就应该尽量少用。
2.预先计算collection的length。
如:将for(var i = 0;i < collection.length;i++)
替换成:for(var i = 0, len = collection.length;i < len;i++)
效果会更好,尤其是在大循环中。
3.尽量减少循环内的操作。
循环内的每个操作,都会被放大为循环次数的倍数。所以,大循环内微小的改进,在性能的整体提升上都是可观的。
4.使用循环替代递归。
相比循环,递归的效率更差一些。递归的优点是在形式上更自然一些。所以,在不影响代码的维护性的前提下,用循环替代递归。
4.2.6 其它方面
1.尽量使用语言内置的语法。
”var arr = [„];“和”var arr = new Array(„);“是等效的,但是前者的效能优于后者。同样,”var foo = {};“的方式也比”var foo = new Object();“快;”var reg = /../;“要比”var reg=new RegExp()“快。
2.尽量不要使用eval。
使用eval,相当于在运行时再次调用解释引擎,对传入的内容解释运行,需要消耗大量时间。
3.使用prototype代替closure。
使用closure在性能和内存消耗上都是不利的。如果closure使用量过大,这就会成为一个问题。所以,尽量将:
this.methodFoo = function()
替换成:
MyClass.protoype.methodFoo = function()
和closure存在于对象实例之中不同,prototype存在于类中,被该类的所有的对象实例共享。
4.避免使用with语句。
With语句临时扩展对象查找的范围,节省了文字的录入时间,但付出了更多的执行时间。因为每个给出的名称都要在全局范围查找。所以,可以将下面的代码:
with(document.formname){
field1.value = ”one“;field2.value = ”two“;}
变更为:
var form = document.formname;form.field1.value = ”one“;form.field2.value = ”two“;
4.3 DOM相关
4.3.1 创建DOM节点
相比较通过document.write来给页面生成内容,找一个容器元素(比如指定一个div或者span)并设置他们的innerHTML效率更高。而设置innerHTML的方式比通过createElement方法创建节点的效率更高。事实上,设置元素的innerHTML是创建节点效率最高的一种方式。
如果必须使用createElement方法,而如果文档中存在现成的样板节点,应该是用cloneNode()方法。因为使用createElement()方法之后,你需要设置多次元素的属性,使用cloneNode()则可以减少属性的设置次数。同样,如果需要创建很多元素,应该先准备一个样板节点。
4.3.2 离线操作大型的DOM树
在添加一个复杂的DOM树时,可以先构造,构造结束后再将其添加到DOM数的适当节点。这能够节省界面刷新的时间。
同样,在准备编辑一个复杂的树时,可以先将树从DOM树上删除,等编辑结束后再添加回来。
4.3.3 对象查询
使用[”“]查询要比.item()更快。调用.item()增加了一次查询和函数的调用。
4.3.4 定时器
如果针对的是不断运行的代码,不应该使用setTimeout,而应该用setInterval。setTimeout每次要重新设置一个定时器。
4.4 其他
1.尽量减小文件尺寸。
将JScript文件中无关的空行、空格、注释去掉,有助于减小JS文件的尺寸,提高下载的时间。(可以通过工具来支持代码发布)
2.尽量不要在同一个Page内同时引用JScript和VBScript引擎
3.将Page内的JScript移入到单独的JS文件中。
4.将Page内的JScript放置在Page的最下面,有助于提高页面的响应速度。
5.利用cache,减少JScript文件的下载次数
6.在HTML内书写JScript文件的URL时,注意统一大小写。这样可以利用前面URL缓存的文件。
C# 性能优化——三种字符串拼接效率
2011年04月07日 星期四 17:56 字符串拼接主要包括三类:+,String.Format(),StringBuilder.Append()1)对于少量固定的字符串拼接,如string s= ”a“ + ”b“ + ”c“,系统会优化成s= String.Concat(”a“,”b“,”c“),不会新建多个字符串。
如果写成string s=”a“;s +=”b“;s+=”c“;则会创建三个新的字符串。2)String.Format的源代码: public static String Format(IFormatProvider provider, String format, params Object[] args){ if(format == null || args == null)throw new ArgumentNullException((format==null)?”format“:”args“);StringBuilder sb = new StringBuilder(format.Length + args.Length * 8);sb.AppendFormat(provider,format,args);return sb.ToString();} 可见,它和StringBuilder有着相似的效率,比用“+”的拼接方式高效,并且代码易于阅读。
string s= String.Format(”{0}{1}{2}“,”a“,”b“,”c");3)StringBuilder可以指定内存空间的容量,但可能需要进行数据类型转化。字符串较少时,可以使用String.Format()代替。
4)少量的字符串操作时,可以使用“+”或者String.Format();大量的字符串操作时,比如在循环体内,必须使用StringBuilder.Append()。