几年前当我第一次面试时,考官发现我是个新手于是他让我写个连接字符串,虽然当时就知道X种连接字符串的写法,但是当时却没能写对一个,工作多年后我仍然不能写一个正确的连接字符串,但我知道打发新人时,让他写连接字符串是个不错的方法.
以下是一个常用的ADO.NET 连接MSSQL的字符串
"Data Source=.;Initial Catalog=MyDBA;Integrated Security=SSPI"
在IIS 环境下 我们也许会这么写
"Data Source=.;Initial Catalog=MyDBA;User Id=用户名;pwd=一般人我不告诉他;"
使用高效的协议(虽然我们一直这么做,但一直没注意)
首先要搞清楚我们是使用什么方式与MSSQL数据库服务沟通的,当然微软产品的好处是即使你不知道,他也会帮你选择一个最合适的方式,在IIS跟MSSQL同台服务器的情况下,默认使用共享内存.
图1)
要确认ADO.Net建立的是什么方式的连接可以通过,企业管理器中->管理->进程信息
可以看到ADO.NET提供程序,.Net SqlClient data Provider
使用LPC(本地调用,即共享内存)
如果你把上面的连接字符串改成"Data Source=127.0.0.1\SQL2K;Initial Catalog=MyDBA;Integrated
Security=SSPI"
注意我的电脑上安装了多个SQL2000实例,其中一个为SQL2K ,使用上面的连接字符串打开一个连接后,再查看进程信息,发现LPC 现在变成了Tcp/IP(注意是.Net SqlClient Data provider对应的行)这个表明现在你启用了TCP/IP来连接数据库,
当然在IIS跟SQL同服务器的情况下建议你使用LPC,这样速度跟性能都比较优越.
在连接字符串中使用端口
一般异地连接时我们多使用tcp/ip协议,默认端口是1433,有时候你可能想改变这个端口,为此你需要使用"服务器网络实用工具"(在开始->Microsoft SQL Server下面),在里面配置Tcp/ip 协议并指定一个新的端口.
这里指定了8888 为实例SQL2K的新端口,注意请不要将"隐藏服务器(H)"选择中,保存更改后就可以下面的连接字符串进行连接了"Data Source=127.0.0.1,8888;Initial Catalog=MyDBA;User ID=sa ",同样在进程信息里你能看到ADO.NET的一条用户名为sa的TCP/IP连接.但是当我们试图从远程连接这台SQL服务器时通常会出现问题,一般服务器上都有防火墙,而且只开放web,ftp,远程桌面等几个有限的端口,而你上面指定8888未在其列,因此当你在异地来连接数据库时会报错,(另外连接字符串如"Data Source=60.188.86.49,8888;Initial Catalog=MyDBA;User ID=sa;pwd=" ,中需要注意的是端口号跟IP或域名之间用逗号","隔开)为解决此问题你需要在防火墙中开放8888端口.
完成后,你可以使用上面IP+端口的形式访问数据库了,不过你在使用以上连接字符串前,是否尝试过类似
"Data Source=www.wow52.cn\SQL2K;Initial Catalog=MyDBA;User ID=sa;pwd=;" 形式的连接字符串呢,这里采用域名加实例名的样子,可能你尝试了下发现行不通,反正我当时是这样做过但发现不行,虽然感觉这个形式很直观,但是怎么就不行呢?
当然如果你细心的话,回过头去看上文有个"Data Source=127.0.0.1\SQL2K;Initial Catalog=MyDBA;Integrated
Security=SSPI"形式的连接字符串,这个是IP+实例的形式,本质上已经是域名+实例的形式了,那么为什么不行呢?问题同样是出在防火墙上面,不过这次你需要开放的是一个UDP端口1434以给运行SQL Server 解析协议 (SSRP)的服务使用,当你使用域名+实例名访问时,解析服务会解析成 IP+端口的形式进行数据库连接. 现在你通过企业管理器中的进程信息查看时,可以看到一个TCP/IP连接了,
SQL Server 解析协议 在SQL2005中被SQL Server Browser 取代,在需要进行远程连接,并且采用域名或IP+实例名的形式时,你需要启动这个服务,并且在防火墙中开放相应端口. 最后关于端口部分,还有一点是SQL2000,跟SQL2005多支持动态端口,也就是把端口设置为0(SQL2005 具体参考配置工具),这个时候为使数据库能在异地正确访问,你需要在防火墙中以添加程序的形式把sqlserver.exe添加进去,如实例SQL2k的程序位置是
D:\Program Files\Microsoft SQL Server\MSSQL$SQL2K\Binn\sqlservr.exe,具体取决于你的安装位置,SQL2005可以在服务器网络配置工具里查看. 除了需要添加sqlserver.exe程序外,你还需要开放1434 UDP端口,或者在使用SQL2005时把C:\Program Files\Microsoft SQL Server\90\Shared\sqlbrowser.exe 添加到防火墙里, sqlbrowser.exe 使用的也是1434 UDP端口.
以上所讲述在我的MSSQL2000里都不行!
如果你不幸遇到这样的情况,最重要的一点是查看你sql的版本,请在查询分析器里运行 Select @@Version,或者看帮助菜单中的"关于..." 如果版本不是8.00.2039(现在说的是SQL2000),那么赶紧升级把,下个SQLSP4来打上.
如果打上补丁还无济于事,那么请确定你在SQL Server网络实用工具中启用了TPC/IP协议,并在防火墙中开放了相关端口.
连接字符串中的一些属性
Connection lifetime 连接生存时间,默认为0,表示无限长,单位为秒.在SQL集群环境中我们才设置为具体的数值.
Connection timeout 连接生成时间,默认为15秒,通俗的讲当你conn.Open等15秒后还没连接成功,那么就会抛出一个连接超时的错误,这也是为什么其他错误来的那么突然,而数据库连接超时错误,非要你等15秒的原因了.
Pooling 是否使用连接池, 默认是起用的,使用Pooling=false来禁止.
Min Pool Size,Max Pool Size 默认情况下最小是0,最大是100.
关于pooling属性在上面所有的连接字符串中多是默认开启的,连接池能有效的提高数据库访问性能,因为创建一个连接需要消耗很多资源,尤其是进行异地访问时(一般指局域网,最好不要进行跨internet的数据库访问)更加如此,因此你在多数情况下不需要显式的禁止(也许你没在意微软一直帮你开通着), 事实上在你使用 conn.open() ,conn.close() 多少数情况下不是真的打开一个连接,而是向连接池中取一条可用连接,跟归还一条连接,conn.open操作只有在池中没有可用连接时才会创建一条连接,当然如果池中可用接数目达到Max Pool Size时,他将不再创建新的连接,而是等待一条可用连接的到来,这个时间是Connection timeout. 当然你的conn.Open操作涉及的连接池,取决于你的连接字符串,比如:
"Data Source=127.0.0.1\SQL2K;Initial Catalog=MyDBA;Integrated
Security=SSPI"
与
"Data Source=127.0.0.1\SQL2K;Initial Catalog=MyDBA;Integrated
Security=SSPI;" ,注意后面多了";"
会开启两个独立的连接池,ADO.NET 判断字符串是否相同的依据是对字符串进行2进制的比较,所以任何变动(空格,大小写)都会被判断为不同的连接字符串.
有如下代码
-----------------------------------
string strConn="Data Source=127.0.0.1\SQL2K;Initial Catalog=MyDBA;Integrated
Security=SSPI;";
for(int i=0;i<10;i++){
SqlConnection conn=new SqlConnection(strConn);
conn.Open();
conn.Close()
}
跟
string strConn="Data Source=127.0.0.1\SQL2K;Initial Catalog=MyDBA;Integrated
Security=SSPI;";
for(int i=0;i<10;i++){
strConn +=" "; //空格
SqlConnection conn=new SqlConnection(strConn);
conn.Open();
conn.Close()
}
两段代码分别运行,看企业管理器->管理->进程信息(请刷新先) ,可以看到前面的只启用了一个进程,而后面的则启用了10个进程,每个进程对应一条连接.前一段代码10个open +close()操作使用的是同一个连接池中的同一个连接,而后一个则开启了10个连接池,每个连接池中有一个连接.
再看下面的代码
for (int i = 0; i <= 10; i++)
{
Thread t = new Thread(new ThreadStart(Command));
t.Start();
}
---------------------
//Command定义
private void Command(){
string strConn="Data Source=127.0.0.1\SQL2K;Initial Catalog=MyDBA;Integrated
Security=SSPI;";
Sqlconnection conn=new SqlConnection(strConn);
SqlCommand cmd=new SqlCommand("一个运行比较长的查询",conn);
conn.Open();
cmd.ExecuteNonQuery();
conn.Close()
}
运行后可以看,企业管理器->管理->进程信息里会开启10个到MyDBA数据库的连接进程,但是需要注意的是,这10个进程是属于同个连接池的,为了证明这一点你可以将上面的连接字符串改为
private void command(){
string strConn="Data Source=127.0.0.1\SQL2K;Initial Catalog=MyDBA;Integrated
Security=SSPI;Max pool Size=5;"; //加了Max Pool Size=5;
SqlConnection conn=new SqlConnection(strConn);
SqlCommand cmd=new SqlCommand("一个运行比较长的查询",conn);
conn.Open();
cmd.ExecuteNonQuery();
conn.Close()
}
结果可以看到他只开启了5个到MyDBA数据库的连接,这证明他们是同个连接池的.当然这里能成功演示的前提是,cmd执行的任务时间要在15秒内,不然会出现后5个线程连接超时,另外使用SQL2000 的跟踪工具(SQL2005中是,SQL Server Profiler)是你观察以上实验的一个绝好的工具!