C# Mysql - 在异步等待服务器的数据库查询上使用锁
C# Mysql - Using of locks on database query for async await server
我有 TcpListener
class 并且我正在使用 async/await
阅读和写作。
对于这个服务器,我创建了一个数据库实例,我已经在其中准备了所有数据库查询。
但是对于不止一个 TcpClient
我不断收到异常:
An exception of type MySql.Data.MySqlClient.MySqlException
occurred
in MySql.Data.dll
but was not handled in user code
Additional information: There is already an open DataReader
associated
with this Connection
which must be closed first.
如果我理解正确的话,一次不能超过一个数据库查询,而超过一个 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;
}
我也尝试过 usings 的方法,它为 SO 社区的某个人提到的每个查询创建新连接,但这种方法比锁慢得多:
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;
}
}
我使用 Stopwatch 对这种方法进行了基准测试,并且在 +- 20 毫秒内执行了一个带锁连接的查询,这只是网络延迟,但使用它是 +- 55 毫秒,因为 .Open()
方法花费 +- 35 毫秒。
如果性能更差,为什么很多人使用方法和 usings?还是我做错了什么?
没错,打开连接是一个耗时的操作。为了缓解这种情况,ADO.NET 有连接池。检查 this article 了解详情。
如果您继续进行性能测试并检查后续连接的计时,您应该会看到 connection.Open()
的时间有所改善并接近 0 毫秒,因为连接实际上是从池中获取的。
通过您的锁定实现,您实际上只使用了一个连接的连接池。虽然这种方法可以在简单的测试中表现出更好的性能,但在高负载的应用程序中会表现出非常差的结果。
我有 TcpListener
class 并且我正在使用 async/await
阅读和写作。
对于这个服务器,我创建了一个数据库实例,我已经在其中准备了所有数据库查询。
但是对于不止一个 TcpClient
我不断收到异常:
An exception of type
MySql.Data.MySqlClient.MySqlException
occurred inMySql.Data.dll
but was not handled in user codeAdditional information: There is already an open
DataReader
associated with thisConnection
which must be closed first.
如果我理解正确的话,一次不能超过一个数据库查询,而超过一个 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;
}
我也尝试过 usings 的方法,它为 SO 社区的某个人提到的每个查询创建新连接,但这种方法比锁慢得多:
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;
}
}
我使用 Stopwatch 对这种方法进行了基准测试,并且在 +- 20 毫秒内执行了一个带锁连接的查询,这只是网络延迟,但使用它是 +- 55 毫秒,因为 .Open()
方法花费 +- 35 毫秒。
如果性能更差,为什么很多人使用方法和 usings?还是我做错了什么?
没错,打开连接是一个耗时的操作。为了缓解这种情况,ADO.NET 有连接池。检查 this article 了解详情。
如果您继续进行性能测试并检查后续连接的计时,您应该会看到 connection.Open()
的时间有所改善并接近 0 毫秒,因为连接实际上是从池中获取的。
通过您的锁定实现,您实际上只使用了一个连接的连接池。虽然这种方法可以在简单的测试中表现出更好的性能,但在高负载的应用程序中会表现出非常差的结果。