c# MySQL连接池限制,并清理连接

本文关键字:连接 MySQL | 更新日期: 2023-09-27 18:16:39

我有一个简单的DB管理器类(一个比它的能力更大的名字):

class DbManager
{
    private MySqlConnectionStringBuilder _connectionString;
    public DbManager()
    {
        _connectionString = new MySqlConnectionStringBuilder();
        _connectionString.UserID = Properties.Database.Default.Username;
        _connectionString.Password = Properties.Database.Default.Password;
        _connectionString.Server = Properties.Database.Default.Server;
        _connectionString.Database = Properties.Database.Default.Schema;
        _connectionString.MaximumPoolSize = 5;
    }

    public MySqlConnection GetConnection()
    {
        MySqlConnection con = new MySqlConnection(_connectionString.GetConnectionString(true));
        con.Open();
        return con;
    }
}

然后我在其他地方有另一个类,表示其中一个表中的记录,我像这样填充它:

class Contact
{
    private void Populate(object contactID)
    {
        using (OleDbConnection con = DbManager.GetConnection())
        {
            string q = "SELECT FirstName, LastName FROM Contacts WHERE ContactID = ?";
            using (OleDbCommand cmd = new OleDbCommand(q, con))
            {
                cmd.Parameters.AddWithValue("?", contactID);
                using (OleDbDataReader reader = cmd.ExecuteReader())
                {
                    if (reader.HasRows)
                    {
                        reader.Read();
                        this.FirstName = reader.GetString(0);
                        this.LastName = reader.GetString(1);
                        this.Address = new Address();
                        this.Address.Populate(ContactID)
                    }
                }
            }
        }
    }
}

class Address
{
    private void Populate(object contactID)
    {
        using (OleDbConnection con = DbManager.GetConnection())
        {
            string q = "SELECT Address1 FROM Addresses WHERE ContactID = ?";
            using (OleDbCommand cmd = new OleDbCommand(q, con))
            {
                cmd.Parameters.AddWithValue("?", contactID);
                using (OleDbDataReader reader = cmd.ExecuteReader())
                {
                    if (reader.HasRows)
                    {
                        reader.Read();
                        this.Address1 = reader.GetString(0);
                    }
                }
            }
        }
    }
}

现在我认为所有的using语句将确保连接在完成时返回到池中,为下一次使用做好准备,但是我有一个循环,创建了数百个这些联系人并填充它们,并且似乎连接没有被释放。

连接、命令和读取器都在它们自己的using语句中。

c# MySQL连接池限制,并清理连接

如果应用程序是多线程的,那么你可能会有10个线程同时运行。每个线程都需要自己的连接,但是如果你将池大小限制为5,那么第六个线程将无法从池中获得连接。

你可能会以某种方式限制你的线程,但我建议显著增加你的应用程序池的大小,以确保你有更多的可用连接比你可能有线程。作为一个指标,默认大小(对大多数人来说通常足够好)是100。

此外,如果您在using块中有任何递归,(例如再次调用populate),正如您在注释中所指出的那样,而不是上面的代码,那么您将遇到进一步的问题。

如果你在using块中调用populate,那么你将有来自父进程的连接打开并在使用中(因此不可重用),然后子进程调用将打开另一个连接。如果这种情况只发生几次,您将耗尽分配的连接。

要防止这种情况,您需要将次要Populate调用移出using块。最简单的方法不是循环遍历记录集,为每个ID调用populate,而是将ID添加到列表中,然后在关闭连接后,为所有新ID执行填充。

或者你可以只是对地址之类的东西进行惰性求值。将addressID存储在私有字段中,然后使Address成为一个属性,该属性检查其支持字段(不是addressID)是否填充,如果没有,则使用addressID查找。这样做的好处是,如果您从不查看地址,您甚至不会进行数据库调用。根据数据的使用情况,这可能会为您节省大量的数据库访问,但如果您确实使用了所有细节,那么它只是将它们移动,可能会将它们分散开来,这可能有助于提高性能,或者可能根本没有区别。:)

一般来说,在数据库访问中,我试图尽快抓取所有数据并关闭连接,最好是在对数据进行任何复杂的计算之前。这样做的另一个很好的原因是,根据您的数据库查询等,您可能会在您的查询访问的表上持有锁,这可能会导致数据库方面的锁定问题。

我建议做一些改变:

1)在using语句结束之前显式地关闭连接。通过查看OleDbConnection(和DbConnection和DbConnectionInternal)的源代码,我相信连接在dispose时没有显式关闭,只是放弃。

2)修改地址。填充以接受连接参数,这样您就不必在只需要一个连接时创建两个打开的连接,并从联系人传递打开的连接。如果在某些情况下地址。当连接不可用时将调用Populate,您可以在Address中创建Populate的重载版本,在使用连接对象调用Populate重载之前打开连接。根据Chris的建议更新:我曾假设在这种情况下人们会知道关闭打开的阅读器,但事实并非如此。如果使用此方法,在将打开的连接传递给该方法之前,必须显式关闭任何打开的读取器。

刚刚验证了建议#1。来自MSDN文档:

"如果DbConnection超出作用域,它不会被关闭。因此,必须通过调用close或Dispose显式地关闭连接,这两个方法在功能上是等效的。如果连接池值"pooling"设置为true或yes,也会释放物理连接。"