连接池比保持连接慢这么多,这正常吗?

8
作为我的之前的问题:如何保持与数据库连接直到屏幕关闭? 首先,我要向大家道歉,我没有解释清楚我的情况。
好吧,我的情况是更新多达一百条记录。我使用For循环创建了一个真实的工作方式,并记录了它们的结果。
private void button1_Click(object sender, EventArgs e)
{
    int i;
    KeyEventArgs keyEvent = new KeyEventArgs(Keys.Enter); //Create keydown event 

    Performance perf = new Performance(); //Class for measure time and logging

    perf.Start(); //Start stopwatch

    for (i = 1; i <= 100; i++)
    {
        txtLotNo.Text = i.ToString("0000000000") + "$01";   //Generate input ID

        txtLotNo_KeyDown(sender, keyEvent); //Fire keydown event
    }

    perf.Stop();    //Stop stopwatch

    perf.Log(frmInvCtrl.appPath,"Stock In (Stay connected)- " + frmInvCtrl.instance);   //Logging
}

这里是性能等级。
class Performance
{
    private Stopwatch _sw = new Stopwatch();    //Create stopwatch property

    public double GetWatch
    {
        get
        {
            return this._sw.ElapsedMilliseconds;
        }
    }

    public void Start()
    {
        Stop();

        _sw.Reset();
        _sw.Start();
    }

    public void Stop()
    {
        if (_sw.IsRunning)
        {
            _sw.Stop();
        }
    }

    public void Log(string path,string menu)
    {
        string logName = path + "\\Log_" + System.DateTime.Now.ToString("yyyyMMdd") + ".txt";
        string logDetail = System.DateTime.Now.ToString("yyyy/MM/dd hh:mm:ss") + " - [" + menu + "] "
            + "Process 100 record in [" + (((double)_sw.ElapsedMilliseconds / 1000)).ToString() + "] seconds";

        using(StreamWriter writer = new StreamWriter(logName,true))
        {
            writer.WriteLine(logDetail);    //wirtelog
        }
    }
}

以下是日志结果

2017/02/19 08:16:05 - [Stock In - On Cloud] Process 100 record in [68.352] seconds
2017/02/19 08:17:34 - [Stock In - On Cloud] Process 100 record in [70.184] seconds
2017/02/19 08:20:28 - [Stock In - On Cloud] Process 100 record in [56.66] seconds
2017/02/19 08:21:34 - [Stock In - On Cloud] Process 100 record in [60.605] seconds
2017/02/19 08:22:44 - [Stock In - On Cloud] Process 100 record in [68.27] seconds
2017/02/19 08:24:43 - [Stock In - Network Server] Process 100 record in [46.86] seconds
2017/02/19 08:26:05 - [Stock In - Network Server] Process 100 record in [31.746] seconds
2017/02/19 08:26:48 - [Stock In - Network Server] Process 100 record in [31.859] seconds
2017/02/19 08:27:32 - [Stock In - Network Server] Process 100 record in [31.003] seconds
2017/02/19 08:28:17 - [Stock In - Network Server] Process 100 record in [40.487] seconds
2017/02/19 08:32:42 - [Stock In (Stay connected)- On Cloud] Process 100 record in [18.196] seconds
2017/02/19 08:35:47 - [Stock In (Stay connected)- On Cloud] Process 100 record in [14.721] seconds
2017/02/19 08:36:30 - [Stock In (Stay connected)- On Cloud] Process 100 record in [15.903] seconds
2017/02/19 08:37:31 - [Stock In (Stay connected)- On Cloud] Process 100 record in [15.811] seconds
2017/02/19 08:38:15 - [Stock In (Stay connected)- On Cloud] Process 100 record in [16.4] seconds
2017/02/19 08:43:08 - [Stock In (Stay connected)- Network Server] Process 100 record in [13.09] seconds
2017/02/19 08:43:25 - [Stock In (Stay connected)- Network Server] Process 100 record in [13.03] seconds
2017/02/19 08:43:40 - [Stock In (Stay connected)- Network Server] Process 100 record in [13.051] seconds
2017/02/19 08:43:55 - [Stock In (Stay connected)- Network Server] Process 100 record in [12.992] seconds
2017/02/19 08:44:12 - [Stock In (Stay connected)- Network Server] Process 100 record in [14.953] seconds

我曾经练习过连接池技术。但是,结果表明在处理大量记录的情况下,保持与数据库的连接速度更快。

有没有适合这种情况的实践方法?

编辑:2017/02/21

这里是在打开表单代码时打开连接:

private void frm_Load(object sender, EventArgs e) //Open menu
{
    ... //statement

    frmMain.sqlConn1 = new SqlConnection();
    frmMain.sqlConn1.ConnectionString = frmMain.connectionString1;
    frmMain.sqlConn1.Open();

    ... //statement
}

更新代码:

public static long ScanUpdate(string lotNo)
{
    string scanLotNo = "";
    int scanIndex = 0;

    if (!SplitBarcode(lotNo, ref scanLotNo, ref scanIndex))
    {
        //Invalid Barcode data
        return -919;
    }

    //Prepare sql command
    string updStatus = (frmMain.shelfScan) ? "05" : "10";
    string sql = <sql statement>

    try
    {
        using (SqlCommand sqlCmd = new SqlCommand(sql, frmMain.sqlConn1))   //frmMain.sqlConn1 is connection in form_Load()
        {
            if (sqlCmd.ExecuteNonQuery() <= 0)
            {
                //No row affect
                //frmMain.sndPlay.Play();
                return -99;
            }
            else
            {
                //Completed
                return 0;
            }
        }
    }
    catch
    {
        return 99;
    }
    finally
    {

    }
}

退出时处理连接

private void btnBack_Click(object sender, EventArgs e)
{
    frmMain.sqlConn1.Dispose();
    this.Close();
}

2
你确定在连接上调用了Close()方法,以便连接返回到池中吗?你有测试过不使用池化并为每个更新操作打开一个新连接所需的时间吗? - C.Evenhuis
在(保持连接)测试中,我在打开菜单时在Form_Load()上打开连接,并在关闭窗体时进行Dispose。当我打开菜单时,只要菜单保持打开状态,我就可以使用此连接更新记录。而没有(保持连接),我使用using块,在块结束后不调用Close(),就像我之前的问题一样。 - Monkawee Maneewalaya
@MitchWheat。您的意思是我应该在更新100条记录后关闭连接,而不是每次更新100次就关闭一次连接吗? - Monkawee Maneewalaya
3个回答

6

就单线程响应性而言,保持连接开启将更快。连接池的目的是通过在线程之间共享连接来减少打开新连接的开销,同时不会在共享SQL服务器上消耗过多的连接。

每当连接释放到连接池中并被重用时,协议栈将调用sp_resetconnection来清除服务器上的状态。您可以通过针对SQL Server运行分析器跟踪来查看这一点。

由于每个进程都有自己的连接池用于每个连接字符串,因此只有当进程内存在对连接的争用时才能从连接池中受益。


2
这里大家忽略了一个关键点,那就是这是Windows CE系统,因此在紧凑版上打开连接可能会非常缓慢。尽管如此,那些时间看起来确实有些夸张。请参考此QA以获取解决方法:如何使SQL Server CE连接更快地打开?

0

在上面的代码中,您可能会遇到一些问题。

  • 您正在创建一个单独的SqlConnection实例。
  • 所有事件处理程序都调用引用同一个SqlConnection实例的静态函数。
  • 您的事件处理程序没有检查它们是否在主UI线程上运行。

SqlConnection类将在内部自动池化连接。当您调用close()函数时,连接只是返回到池中。您可以通过连接字符串中的属性来控制池化行为。通过为所有函数保留相同的连接对象,您正在强制它们被串行化。

这里有一个一些连接字符串属性的参考。看一下Connection Lifetime

我建议从form_load()函数中删除SqlConnection实例,并将ScanUpdate编写如下:

public static long ScanUpdate(string lotNo)
{
    string scanLotNo = "";
    int scanIndex = 0;

    if (!SplitBarcode(lotNo, ref scanLotNo, ref scanIndex))
    {
        //Invalid Barcode data
        return -919;
    }

    //Prepare sql command
    string updStatus = (frmMain.shelfScan) ? "05" : "10";
    string sql = <sql statement>

    try
    {
        using (SqlConnection conn = new SqlConection(frmMain.connectionString1)) {
            SqlCommand sqlCmd = new SqlCommand(sql, conn);
            if (sqlCmd.ExecuteNonQuery() <= 0)
            {
                //No row affect
                //frmMain.sndPlay.Play();
                return -99;
            }
            else
            {
                //Completed
                return 0;
            }
            conn.Close();
        }            
    }
    catch
    {
        return 99;
    }
}

对于您的*_Click事件处理程序函数,请确保检查是否需要重新调用事件:
private void button1_Click(object sender, EventArgs e)
{
   if (this.InvokeRequired)
   {
      this.Invoke(new EventArgsDelegate(button1_Click), new object[] { sender, ea });
   }

   // Do some stuff
}

查看此答案以获取有关事件处理的更多详细信息。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接