2010-12-09 2 views
2

Я хотел бы увеличить производительность очень простых запросов на выбор и обновление .NET. & MSSQL 2k8. Мои запросы всегда выбирают или обновляют одну строку. Таблицы DB имеют индексы для столбцов, на которые я запрашиваю. Мой тестовый код .NET выглядит следующим образом:Улучшение производительности и производительности .NET/MSSQL

 public static MyData GetMyData(int accountID, string symbol) 
    { 
      using (var cnn = new SqlConnection(connectionString)) 
      { 
       cnn.Open(); 

       var cmd = new SqlCommand("MyData_Get", cnn); 
       cmd.CommandType = CommandType.StoredProcedure; 

       cmd.Parameters.Add(CreateInputParam("@AccountID", SqlDbType.Int, accountID)); 
       cmd.Parameters.Add(CreateInputParam("@Symbol", SqlDbType.VarChar, symbol)); 

       SqlDataReader reader = cmd.ExecuteReader(); 

       while (reader.Read()) 
       { 
        var MyData = new MyData(); 
        MyData.ID = (int)reader["ID"]; 
        MyData.A = (int)reader["A"]; 
        MyData.B = reader["B"].ToString(); 
        MyData.C = (int)reader["C"]; 
        MyData.D = Convert.ToDouble(reader["D"]); 
        MyData.E = Convert.ToDouble(reader["E"]); 
        MyData.F = Convert.ToDouble(reader["F"]); 

        return MyData; 
       } 
      } 
    } 

и по хранимая процедура выглядит следующим образом:

 PROCEDURE [dbo].[MyData_Get] 
@AccountID int, 
@Symbol varchar(25) 
AS 
BEGIN 
SET NOCOUNT ON; 
    SELECT p.ID, p.A, p.B, p.C, p.D, p.E, p.F FROM [MyData] AS p WHERE p.AccountID = @AccountID AND p.Symbol = @Symbol 
END 

Что я вижу, если я бегу GetMyData в цикле, запрашивая объекты MyData, Я не превышаю ~ 310 транзакций/сек. Я надеялся достичь более 1000 транзакций в секунду.

На стороне SQL Server не совсем уверен, что я могу улучшить для такого простого запроса. ANTI profiler показывает мне, что на стороне .NET, как и ожидалось, узким местом является cnn.Open и cnn.ExecuteReader, однако я понятия не имею, как я мог бы значительно улучшить свой код .NET?

Я видел тесты, хотя люди, похоже, легко достигают 10 000 000 транзакций в секунду.

Любые советы о том, как я могу значительно улучшить производительность для этого сценария, будут очень признательны!

Спасибо,

Том

EDIT:

рекомендация Per MrLink, добавив "TOP 1" в запросе на выборку улучшена производительность около 585 операций/сек от 310

EDIT 2:

Arash N предложил выбрать запрос «WITH (NOLOCK)», что значительно улучшило производительность! Я сейчас вижу около 2500 операций/сек

EDIT 3:

Другой небольшой оптимизация, которую я только что сделал на стороне .NET помог мне получить еще 150 транзакций/сек. Изменение в то время как (reader.Read()) до if (reader.Read()) на удивление в значительной степени изменило ситуацию. На сред. Я сейчас вижу 2719 транзакций/сек.

+0

Есть много факторов: вы пренебрегаете упоминанием аппаратного или аппаратного обеспечения. – 2010-12-09 23:30:29

+0

Этот код работает на нескольких разных системах. Бенчмарк на моей тестовой машине и в производстве довольно близок. Большинство машин - 8-ядерные xeon-машины с> 16 ГБ оперативной памяти и диски SAS в RAID5 – TJF 2010-12-09 23:46:42

+1

Прошу прощения; вы серьезно говорите, что вы разочарованы установлением соединения, запуском хранимой процедуры и обработкой результатов занимает 3 мс? – Stu 2010-12-09 23:55:55

ответ

1

Попробуйте использовать WITH (NOLOCK) в инструкции SELECT для повышения производительности. Это позволит выбрать строку без ее блокировки.

SELECT p.ID, p.A, p.B, p.C, p.D, p.E, p.F FROM [MyData] WITH(NOLOCK) AS p WHERE p.AccountID = @AccountID AND p.Symbol = @Symbol 
0

Часто ли используется ваш метод? Не могли бы вы выполнить ваши запросы, чтобы вы могли открыть свое соединение, создать свои параметры, получить результат и повторно использовать их для нескольких запросов, прежде чем снова закрыть все это?

0

Если данные не часто недействительны (обновлены), я бы использовал уровень кэша. Это один из наиболее эффективных способов (если используется правильно) для повышения производительности.

0

Вы можете использовать выходные параметры вместо select, так как всегда есть одна строка.

Вы также можете создать SqlCommand раньше времени и повторно использовать его. Если вы выполняете много запросов в одном потоке, вы можете продолжать выполнять его в том же соединении. Если нет, вы можете создать пул из них или выполнить cmdTemplate.Clone() и установить соединение.

1

Некоторые вещи для рассмотрения.

Во-первых, вы не закрываете соединение с сервером. (cnn.Close();) В конце концов, он будет закрыт сборщиком мусора. Но до тех пор, пока это не произойдет, вы создаете новое соединение с базой данных каждый раз, а не собираете один из пула соединений.

Во-вторых, у вас есть набор индексов на сервере Sql, охватывающий столбцы AccountID и Symbol?

В-третьих, в то время как accountId бытие и int - это хорошо и быстро. Столбец Symbol, являющийся varchar (25), всегда будет намного медленнее. Можете ли вы изменить это на флаг int?

1

Убедитесь, что соединения с базой данных на самом деле объединяются. Если вы видите узкое место в cnn.Open, кажется, есть хорошие шансы, что они не получат пул.

0

Попробуйте повторное использование команды и Prepare 'Сначала это.

Не могу сказать, что это определенно поможет, но, похоже, стоит попробовать.

0

В частности, нет порядка ...

  • Вы (или вашего АБД) изучил план выполнения хранимой процедуры при получении? SQL Server кэшировал фиктивный план выполнения (из-за параметров oddball или старой статистики).

  • Как часто обновляется статистика по базе данных?

  • Вы используете временные таблицы в своей хранимой процедуре? Если да, создаются ли они заранее. Если нет, вы будете делать много перекомпиляций, так как создание таблицы temp делает недействительным план выполнения.

  • Вы используете пул соединений? Открытие/закрытие соединения с SQL-сервером - дорогостоящая операция.

  • Является ли ваша таблица сгруппированной по учетуID и символу?

Наконец ...

  • Есть ли причина, что вы ударяя эту таблицу счета и символ, а не, скажем, просто извлечения всех данных для всей учетной записи одним махом?
1

Я надеялся также достичь более 1000 операций/сек [при запуске GetMyData в цикле]

Что вы просите для GetMyData работать в менее чем 1 мс - это всего лишь бессмысленно оптимизация! На минимальном уровне этот метод предполагает обратную связь с сервером базы данных (возможно, с использованием сетевого доступа) - вы не сможете сделать этот метод намного быстрее, если ваш запрос был SELECT 1.

Если у вас есть подлинное требование делать больше запросов в секунду, ответ будет либо использовать несколько потоков, либо купить более быстрый ПК.

В коде нет абсолютно ничего плохого. Я не уверен, где вы видели людей, которые управляют 10 000 транзакций в секунду, но я уверен, что это должно было включать несколько одновременных клиентов, обращающихся к одному серверу базы данных, а не к один поток управляет выполнением запросов менее чем за 10 мс!