SqlConnection 需要close吗

总结,除了dataReader其他基本不用close,但是SqlDateReader dr = cmd.ExecuteReader(commandBehavious.ConnectionClosed)也只是为了关闭reader后关闭connection用的。归根结底还是为了关闭connection罢了。如果连接多了,只有gc会去回收而不是连接池。gc又是不定时不马上的。所以要加using(connection)

 

http://bbs.csdn.net/topics/390826973

摘自csdn

为了事务使用方便SqlConnection一直没有关闭,我主要是用于cs方面的。不知道不关闭连接影响大不大?会有哪些弊端?请熟悉这块的朋友指点一二

1)http://www.cnblogs.com/jjstar/archive/2004/06/10/14697.aspx

经测试证明,在大访问量的情况下,速度要比关闭数据库连接的ServicedComponent快,
但是付出的代价是数据库增加了约连接池同等数量的线程,并且可连接数量减少。

2)

不关闭比关闭连接快不快我没有测试,占用连接不知道对服务器的影响大不大。我在本机测试不关闭连接的情况下使用默认连接池大小可以开到233个连接。指定max poll size=32767循环创建没有报错了。这个大小肯定可以满足一个中小型的企业erp了。

3)分分钟你的网站就出现连接池已满无法访问而挂掉

4)

关于不关实际上没有多少区别。

关了也只是逻辑上关闭;要不关也行,连接池会帮你处理;
至于数据库,更加不用担心了,就算有上万个连接挂在上面,也不会占多少资源,更加不用说开多少线程了。

要说有问题,就是数据库连接一直保持着,无论需要与否;而且要出连接错误了还不好找真正在什么地方。

 

5)

 

为什么要关闭数据库连接,可以不关闭吗?

 分类:
 
 

目录(?)[+]

 

为什么要关闭数据库连接,可以不关闭吗?

 

首先要说明的是连接数是有限制的:

代码如下:

for (int i = 0; i < 10000; i++)
{
    SqlConnection conn = new SqlConnection(@"Data Source=.\SQLEXPRESS;
                AttachDbFilename=""E:\DB\NORTHWND.mdf"";
                Integrated Security=True;Connect Timeout=30;User Instance=True");

    conn.Open();
    Console.WriteLine("打开了{0}个连接", i);
}

运行结果如下:

image

过一会就会提示打开连接超时了:

clip_image002

 

可以看到数据库连接时有限制的,如果连接不关闭,而且使用的人比较多,那么系统很快就down掉了。

 

 

但是有时候由于某些原因应用程序可能只是几个人使用,所以就有人设计了:

在应用程序启动的时候打开数据库连接,在应用程序关闭的时候关闭数据库连接

那么使用这种方式有什么问题呢?

首先假设有一张表Nums,表定义如下:

image

Main代码如下:

SqlConnection conn = new SqlConnection(@"Data Source=.\SQLEXPRESS;
                    AttachDbFilename=""E:\DB\NORTHWND.mdf"";
                    Integrated Security=True;Connect Timeout=30;User Instance=True");
conn.Open();
Parallel.For(1, 9999, (id) =>
{
    ExecuteCommand(conn, id);
});
 

就是从1到9999开始执行ExecuteCommand

 

ExecuteCommand代码如下:

private static void ExecuteCommand(SqlConnection conn, int id)
{
    Console.WriteLine("正在执行." + id);

    Thread.Sleep(100);

    SqlCommand cmd = new SqlCommand(
       string.Format("Insert into Nums values('{0}') ", id), conn);

    cmd.ExecuteNonQuery();
}

 

运行:

clip_image002[5]

可以看到ExecuteNonQuery方法抛出了异常,原因是连接处于关闭状态。

 

可是我们的连接一直都是open着的啊,并没有调用close,dispose之类的方法啊。

于是在ExecuteCommand前面增加判断条件:

if (conn.State != System.Data.ConnectionState.Open)
    conn.Open();

再次运行:

clip_image002[7]

可以看到还是会出现连接已关闭的问题。你知道什么原因吗?

 

 

这里是由于多线程环境引起的。所以需要加锁。

private static object syncObj = new object();

private static void ExecuteCommand(SqlConnection conn, int id)
{
    lock (syncObj)
    {
        if (conn.State != System.Data.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();
    }
}

再次运行:可以发现基本没问题了.

修改Parallel.For的最大值上限,要测试下是否可以长期执行了。

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

 

 

一天测试下来,没出现任何问题。

 

结论:对于某些只有几个人使用的应用程序,可以不关闭数据库连接,但是在写代码的时候最好要加上连接是否打开的判断。

 

你有什么好的看法呢,欢迎留下!

作者:LoveJenny
本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。
 
分类: C#
 

正确使用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一下,很普遍很潇洒:

      private static void OpenSqlConnection()
        {
            string connectionString = GetConnectionString();
            using (SqlConnection connection = new SqlConnection(connectionString))
            {
                connection.Open();
                Console.WriteLine("ServerVersion: {0}", connection.ServerVersion);
                Console.WriteLine("State: {0}", connection.State);
            }
        }

        static private string GetConnectionString()
        {
            // To avoid storing the connection string in your code, 
            // you can retrieve it from a configuration file, using the 
            // System.Configuration.ConfigurationSettings.AppSettings property 
            return "Data Source=(local);Initial Catalog=AdventureWorks;"
                + "Integrated Security=SSPI;";
        }

其实我们都知道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 中启用连接池。除非显式禁用,否则,在应用程序中打开和关闭连接时,池进程会对连接进行优化。还可以提供几个连接字符串修饰符来控制连接池的行为。

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

参考:

http://msdn.microsoft.com/zh-cn/library/system.data.sqlclient.sqlconnection(VS.80).aspx

http://msdn.microsoft.com/zh-cn/library/8xx3tyca(VS.90).aspx

 

6)

数据库连接资源很宝贵,反复的打开和关闭,会造成数据库效率低下。为了确保应用程序的稳定和降低性能成本,ado.net使用了连接池的优化方法来管理维护数据库连接。
连接池可以减少创建连接的次数
SqlConnection.Open的时候,连接池会检查池子中是否有可用的连接,如果有,立即返回给调用者,否则创建新的连接。SqlConnection.Close,连接池会判断池子里连接数是否在最大连接数之内,如果是,会将连接收回池子而并不是真正的关闭,否则才会销毁连接。回到池子里的连接,在下一次Open的时候可以重复使用。

连接池默认最大连接数是100,你可以做个试验:
在连接字符串设定最多连接数MaxPoolSize=2
然后创建连接,进行关闭,当连接超过2个之后的连接,你执行Close,然后去sp_who查看,应该是销毁掉的。

或者你调用SqlConnection.ClearPool(SqlConnection connection),应该也会把连接销毁。

7)

sqlcon.Close 本来就不保证关闭与SQL Server的物理连接。谁对你说过 SqlConnection.Close 是关闭进程打开的数据库会话的呢?

SqlConnection 自己维护连接池,它会在必要时才关闭多余的会话。假设你的程序在2分钟总共内创建1万个SqlConnection,那么可能真正只有15个“与 SQL Server的会话连接”就能应付这1万个 sqlcon.Open --> sqlcon.Close了,这是连接池自己去管理的,用不着你去操心打开和关闭数据库会话连接问题,你只要在你的程序中尽快地执行 sqlcon.Close 从而能够让连接池更早地得到可复用的连接信息就行了。

8)

仅仅当你看到那个 SqlCommand 的 ExecuteReader(CommandBehavior.CloseConnection) 这种语句时,你才意识到可能需要调用这个语句返回的 SqlDataReader 的 Close()。当然,调用其 Dispose() 也就调用了其 Close()。

而假设不是这个参数,那么调用它的 Close() 或者 Dispose() 都是毫无意义的,不能因为道听途说觉得它比较“时髦”于是你就调用它。调用它除了让你的程序变慢一点还能有什么结果呢?

而假设你不想陷入这种争论中,特别是当你的SQLHelper除了给SqlConnection使用,也还给OleConnection或者Oracle或者Sqlite等等使用时,你就更无需去纠结在SqlDataReader上。那么通用地,只要保证对DbConnection保证调用其Close(),你就消解了一切关于“是否需要关闭SqlDataReader”的争论。

能够少写一些代码就要尽量少写代码,能够少浪费脑细胞去记忆一些争论就最好养成一种新的编程模式来避免争论。但是如果需要问“为什么要这样的编程模式”,我们必须知道这样编程的目的,知道深入一点的原理,决不能稀里糊涂地凭“时髦与否”来判断是否应该那样编程。

我们都知道,SqlConnection实际与连接池来管理的。默认地,连接池里最多只能有200个连接。这在一般来说是足够了。但是在高并发的情况下,你必须及时地调用其Close()方法,好将物理连接还给连接池,共享给别的逻辑连接。如果靠普通的GC的回收机制,那么往往并不及时,那么有可能在高并发情况下出现“连接池已满”这种异常。

仅仅对于这个数据库逻辑连接,我们需要尽快地调用它的Close()方法。或者说是通过其 IDisposable 接口的 Dispose() 方法调用其 Close()方法。对于 DbCommand、DbDabaReader等等,完全没有必要“闲着没事时”去调用其 Dispose() 方法。

posted @ 2016-03-14 18:48  阿玛  阅读(3732)  评论(0)    收藏  举报