С# Mysql - Использование блокировок в запросе базы данных для сервера ожидания async

У меня есть класс TcpListener, и я использую async/await чтение и запись.

Для этого сервера я создал отдельный экземпляр базы данных, где я подготовил все запросы к базе данных.

Но для более одного TcpClient я получаю исключение:

Произошло исключение типа MySql.Data.MySqlClient.MySqlExceptionв MySql.Data.dll, но не был обработан в коде пользователя

Дополнительная информация: уже существует открытый DataReaderс этим Connection, который должен быть закрыт первым.

Если я правильно понимаю, не может быть более одного запроса базы данных во время, что является проблемой для более чем одного клиента 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, но этот метод намного медленнее, чем блокировки:

    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;
        }
    }

Я использовал секундомер для сравнения этих методов, и запросы с одним соединением с блокировками выполняются в + - 20 мс, который представляет собой + - только задержку сети, но с потреблением + 55 мс из-за метода .Open(), который принимает + - 35 мс.

Почему многие люди используют метод с использованием, если есть гораздо худшая производительность? Или я делаю что-то неправильно?

Ответ 1

Вы правы, открытие соединения - это трудоемкая операция. Чтобы уменьшить это, ADO.NET имеет пул соединений. Подробнее см. в этой статье.

Если вы продолжите свой тест производительности и проверьте тайминги для последующих подключений, вы должны увидеть, что время для connection.Open() улучшается и приближается к 0 мс, потому что соединения фактически берутся из пула.

С реализацией блокировки вы фактически используете пул соединений только с одним соединением. Хотя этот подход может продемонстрировать лучшую производительность в рамках тривиального теста, он продемонстрирует очень слабые результаты в высоконагруженных приложениях.