正确使用SqlConnection对象,兼谈数据库连接池

简介:

今晚看到上了评论头条的LoveJenny写的为什么要关闭数据库连接,可以不关闭吗?,文章写得简单易懂非常不错,而且代码贴的很到位,下面的讨论也很激烈(老赵都跑过去留言两次了,我恨)。又查看了两遍原文中的代码,我同意评论中有几位的看法,真正造成多线程并行操作数据库时的连接问题可能是由于对SqlConnection的不当使用。为什么呢?再来看一下LoveJenny兄弟贴出的一段重要源码:

1
2
3
4
5
6
7
8
9
string  sqlConnString = @"Data Source=.\SQLEXPRESS;
      AttachDbFilename=""E:\DB\NORTHWND.mdf"";
      Integrated Security=True;Connect Timeout=30;User Instance=True" ;
  SqlConnection conn = new  SqlConnection(sqlConnString);
  conn.Open();
  Parallel.For(1, Int32.MaxValue, (id) =>
  {
      ExecuteCommand(conn, id);
  });

其中,ExecuteCommand的具体实现如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
private  static  object  syncObj = new  object ();
private  static  void  ExecuteCommand(SqlConnection conn, int  id)
{
     lock  (syncObj)
     {
         if  (conn.State != ConnectionState.Open)
         {
             conn.Open();
         }
         Console.WriteLine( "正在执行."  + id);
 
         Thread.Sleep(100);
 
         SqlCommand cmd = new  SqlCommand(
             string .Format( "Insert into Nums values('{0}') " , id), conn);
 
         cmd.ExecuteNonQuery();
     }
}

代码很简洁,但是很多人包括我自己忍不住都会有几个重大疑问:

1、怎么并行操作n多次数据库只共用一个连接对象呢?

2、并行处理的地方加了锁,每次进行数据库操作都要Lock一下(我感觉这根本没有发挥多线程并行处理的优势,个人认为还不如单线程执行的快呢),这个真的有这个必要吗?

3、同一个数据库连接字符串,使用数据库连接对象SqlConnection怎么还要传参呢,显式Open和Close不是更好吗?通常不都是using一下完事吗?

经过简单思考之后,在本地机器上改进了一下实现代码进行测试,如下:

1
2
3
4
Parallel.For(1, Int32.MaxValue, (id) =>
   {
       ExecuteCommand(id);
   });

ExecuteCommand方法不再接受SqlConnection对象作为参数,去掉Lock,数据库操作看上去就像是操作一次数据库,打开一次数据库连接,保证线程安全:

1
2
3
4
5
6
7
8
9
10
11
12
private  static  void  ExecuteCommand( int  id)
{
     using  (SqlConnection conn = new  SqlConnection(sqlConnString))
     {
         conn.Open();
         Console.WriteLine( "正在执行."  + id);
         Thread.Sleep(100);
         SqlCommand cmd = new  SqlCommand(
             string .Format( "Insert into Nums values('{0}') " , id), conn);
         cmd.ExecuteNonQuery();
     }
}

当然ExecuteCommand也可以通过显式Open和Close处理:

1
2
3
4
5
6
7
8
9
10
11
private  static  void  ExecuteCommand( int  id)
{
     SqlConnection conn = new  SqlConnection(sqlConnString);
     conn.Open();
     Console.WriteLine( "正在执行."  + id);
     Thread.Sleep(100);
     SqlCommand cmd = new  SqlCommand(
         string .Format( "Insert into Nums values('{0}') " , id), conn);
     cmd.ExecuteNonQuery();
     conn.Close();
}

根据我的本地机器测试,上面改进后的两种写法数据库连接对象使用正常,线程安全,而且在性能上比加锁操作数据库提高显著,有兴趣的你也不妨一试。

但是,本文的目的不在说代码如何改进才能线程安全提升性能,而是要说明一个非常简单的问题,如何正确合理地使用数据库连接对象SqlConnection?

那么到底如何正确合理地使用数据库连接对象SqlConnection?

最经典的说法莫过于就是using一下即可,这种说法其实是非常正确的,MSDN的使用方法通常也都是using一下,很普遍很潇洒:

SqlConnection

其实我们都知道SqlConnection对象一定继承自IDispose接口,然后才可以using。using使用的对象最后都会Dispose处理掉,难道数据库连接对象的Close方法和Dispose是等价的?

这里明确告诉您,通过显式Close和using后Dispose是不一样的。到这里是不是很想知道Dispose和Close的区别?

MSDN对Close方法的解释是这样的:

关闭与数据库的连接,这是关闭任何打开连接的首选方法。 如果 SqlConnection超出范围,则不会将其关闭。因此,必须通过调用 Close 或 Dispose 显式关闭该连接。Close 和 Dispose 在功能上等效。如果连接池值 Pooling 设置为 true 或 yes,则基础连接将返回到连接池。另一方面,如果 Pooling 设置为 false 或 no,则会关闭到服务器的基础连接。 

我们可以这样理解:通过调用Close方法关闭的SqlConnection对象,最后是要放回到连接池中的,也就是说这个对象还可以继续使用,只是这个对象现在的连接状态是关闭的,等到下次向池中申请连接对象,这个对象就可以激活复用而不用去new一个新的对象。

而通过调用Dispose()方法,则该对象很可能调用了GC.SuppressFinalize(this)(某些特殊的类可能并没有调用该方法),这样GC就会优先对这些对象进行回收而可能造成该对象无法使用。这里我们只要理解,在SqlConnection对象上调用Dispose方法,则这个连接就不能在连接池中继续使用了

最后说明一下,ado.net使用了连接池技术用来复用连接,减少系统反复打开关闭连接的开销来提升性能,所以我上面说通过using“数据库操作看上去就像是操作一次数据库,打开一次数据库连接,保证线程安全”其实也是不对的。下面摘录一段MSDN的说法解惑:

连接池使新连接必须打开的次数得以减少。池进程保持物理连接的所有权。通过为每个给定的连接配置保留一组活动连接来管理连接。每当用户在连接上调用 Open 时,池进程就会查找池中可用的连接。如果某个池连接可用,会将该连接返回给调用者,而不是打开新连接。应用程序在该连接上调用 Close 时,池进程会将连接返回到活动连接池集中,而不是关闭连接。连接返回到池中之后,即可在下一个 Open 调用中重复使用。

只有配置相同的连接可以建立池连接。ADO.NET 同时保留多个池,每种配置各一个。在使用集成的安全性时,连接按照连接字符串以及 Windows 标识分到多个池中。还根据连接是否已在事务中登记来建立池连接。

池连接可以显著提高应用程序的性能和可缩放性。默认情况下,在 ADO.NET 中启用连接池。除非显式禁用,否则,在应用程序中打开和关闭连接时,池进程会对连接进行优化。还可以提供几个连接字符串修饰符来控制连接池的行为。

我们可以显式对连接池进行一些操作,但是通常不建议这么做。对连接池您还有什么疑惑吗?欢迎讨论。






本文转自JeffWong博客园博客,原文链接:http://www.cnblogs.com/jeffwongishandsome/archive/2011/08/09/2133114.html,如需转载请自行联系原作者

目录
相关文章
|
4月前
|
SQL 数据库连接 数据库
VB.NET 中使用SqlConnection类连接到Microsoft SQL Server数据库的详细步骤
VB.NET 中使用SqlConnection类连接到Microsoft SQL Server数据库的详细步骤
90 0
|
16天前
|
SQL 数据可视化 关系型数据库
轻松入门MySQL:深入探究MySQL的ER模型,数据库设计的利器与挑战(22)
轻松入门MySQL:深入探究MySQL的ER模型,数据库设计的利器与挑战(22)
|
16天前
|
存储 关系型数据库 MySQL
轻松入门MySQL:数据库设计之范式规范,优化企业管理系统效率(21)
轻松入门MySQL:数据库设计之范式规范,优化企业管理系统效率(21)
|
16天前
|
关系型数据库 MySQL 数据库
轻松入门MySQL:精准查询,巧用WHERE与HAVING,数据库查询如虎添翼(7)
轻松入门MySQL:精准查询,巧用WHERE与HAVING,数据库查询如虎添翼(7)
|
18天前
|
存储 关系型数据库 MySQL
数据库字符编码MySQL中使用UTF-8还是UTFB4
数据库字符编码MySQL中使用UTF-8还是UTFB4
20 0
|
21天前
|
缓存 NoSQL 关系型数据库
在Python Web开发过程中:数据库与缓存,MySQL和NoSQL数据库的主要差异是什么?
MySQL是关系型DB,依赖预定义的表格结构,适合结构化数据和复杂查询,但扩展性有限。NoSQL提供灵活的非结构化数据存储(如JSON),无统一查询语言,但能横向扩展,适用于大规模、高并发场景。选择取决于应用需求和扩展策略。
112 1
|
21天前
|
SQL 关系型数据库 MySQL
【MySQL技术专题】「问题实战系列」深入探索和分析MySQL数据库的数据备份和恢复实战开发指南(8.0版本升级篇)
【MySQL技术专题】「问题实战系列」深入探索和分析MySQL数据库的数据备份和恢复实战开发指南(8.0版本升级篇)
94 0
|
12天前
|
存储 关系型数据库 MySQL
MySQL基础入门:数据库操作全攻略
MySQL基础入门:数据库操作全攻略
44 0
|
12天前
|
关系型数据库 MySQL 数据库
卸载云服务器上的 MySQL 数据库
卸载云服务器上的 MySQL 数据库
30 0
|
16天前
|
SQL 关系型数据库 MySQL
轻松入门MySQL:深入学习数据库表管理,创建、修改、约束、建议与性能优化(3)
轻松入门MySQL:深入学习数据库表管理,创建、修改、约束、建议与性能优化(3)