C# Mysql - 在异步等待服务器上使用数据库查询锁

10

我有一个 TcpListener 类,正在使用 async/await 来读写。

对于这个服务器,我创建了单个数据库实例,其中准备了所有数据库查询。

但是,当有多个 TcpClient 时,我会不断收到异常:

类型为 MySql.Data.MySqlClient.MySqlException 的异常发生在 MySql.Data.dll 中,但未在用户代码中处理

其他信息:该 Connection 已经与此关联了一个打开的 DataReader,必须先关闭它。

如果我理解正确,一次只能有一个数据库查询,这就是多个 async 客户端的问题。

因此,我简单地在我的查询中添加了锁定,像这样,一切似乎都很好。

   // One MySqlConnection instance for whole program.

   lock (thisLock)
   {
    var cmd = connection.CreateCommand();

    cmd.CommandText = "SELECT Count(*) FROM logins WHERE username = @user AND password = @pass";
    cmd.Parameters.AddWithValue("@user", username);
    cmd.Parameters.AddWithValue("@pass", password);

    var count = int.Parse(cmd.ExecuteScalar().ToString());
    return count > 0;
}

我也尝试了SO社区中某人提到的使用“usings”为每个查询创建新连接的方法,但是这种方法比锁定方法慢得多:

我也尝试了SO社区中某人提到的使用“usings”为每个查询创建新连接的方法,但是这种方法比使用锁定的方法慢得多:

    using (MySqlConnection connection = new MySqlConnection(connectionString))
    {
        connection.Open();   // This takes +- 35ms and makes worse performance than locks

        using (MySqlCommand cmd = connection.CreateCommand())
        {
            cmd.CommandText = "SELECT Count(*) FROM logins WHERE username = @user AND password = @pass";
            cmd.Parameters.AddWithValue("@user", username);
            cmd.Parameters.AddWithValue("@pass", password);

            int count = int.Parse(cmd.ExecuteScalar().ToString());
            return count > 0;
        }
    }

我使用了计时器来测试这些方法和查询,使用带锁的单连接执行时间约为20ms,仅是网络延迟;但使用usings后执行时间约为55ms,因为.Open()方法需要约35ms的时间。

如果性能更差,为什么很多人还要使用带usings的方法?或者是我的做法有问题吗?


1
为什么不打开另一个连接,而不是使用锁定? - JSteward
“using”只是语法糖吗? - Rick James
@Rick James,你正在正确使用IDisposable对象的糖语法。 - Erik Šťastný
您可以使用连接池,使用连接池可以维护空闲连接。对于单个连接来说,可能看起来会慢一些,但在实际场景中它会表现更好。 - Rohit Harkhani
没问题,小写字母当然是变量。connectionString = "SERVER=" + server + ";" + "DATABASE=" + database + ";" + "UID=" + uid + ";" + "PASSWORD=" + password + ";CHARSET=utf8mb4"; - Erik Šťastný
显示剩余4条评论
1个回答

8
你说得没错,打开连接是一个耗时的操作。为了缓解这种情况,ADO.NET 提供了连接池。具体详情请参见此文章
如果你继续进行性能测试并检查后续连接的时间,你会发现 connection.Open() 的时间会改善并接近于0毫秒,因为实际上连接是从池中获取的。
使用你的锁实现,你实际上只使用了一个连接的连接池。尽管这种方法在简单测试中可能表现更好,但在高负载应用程序中将显示非常差的结果。

我会尝试进行测试。 - Erik Šťastný
1
除此之外,值得一提的是,在已经为您构建了这种工作(上下文实例中的连接池处理)的情况下,使用ORM也是值得考虑的。 - War
我同意,在高并发应用程序中,单个锁相对于使用 ADO.NET 连接池会显示出可怕的性能。 - Federico Dipuma
我现在可以确认这个了。谢谢你让我明白了事情。 - Erik Šťastný

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