Проблема параллелизма с использованием ExecuteNonQuery

Я пишу простые записи в текстовом журнале в SQL-таблицу примерно так:

private SqlConnection sqlcon;

// ...

try {
    sqlcon.Open();

    SqlCommand cmd = sqlcon.CreateCommand();
    cmd.CommandText = this.logString;

    cmd.ExecuteNonQuery();
} 
/* catch SqlException, InvalidOperationException, Exception */ 
/* finally sqlcon.Close() */

Асинхронная обработка установлена ​​в ложь, поэтому я думал, что записи журнала будут отображаться в таблице в порядке выполнения. Но это не тот случай, если 2 или более записей журнала запускаются менее чем за 5 мс.

Таким образом, я предполагаю, что ExecuteNonQuery работает в другом потоке и как-то смешиваются разные события. Я уже пытался использовать асинхронную обработку с BeginExecuteNonQuery и EndExecuteNonQuery, но он испортил мой код и в действительности все равно не работает.

Поэтому мой вопрос: есть ли способ убедиться, что незапросы выполняются именно в том порядке, в котором они были запущены?

редактировать: может быть, это важно, я использую метку времени, как так

cmd.Parameters.Add("timestamp", SqlDbType.DateTime ).Value = System.DateTime.Now;

4 ответа

Решение

Что если вы используете SQL Server SYSDATETIME для вставки меток времени? я думаю что ExecuteNonQuery синхронно, но, возможно, что-то не так с вашей меткой времени.

Есть хороший документ по разрешению времени в Windows.

Асинхронный?

Если вы открываете / закрываете соединение каждый раз, когда не можете быть в этом уверены, это как- то эквивалентно (пожалуйста, прости меня, я знаю, это не совсем точно), чтобы позвонить BeginExecuteNonQuery (это был метод, использовавшийся до MARS). На самом деле каждый запрос является синхронным, но если вы регистрируете что-то с интервалом в 5 мс, вы не можете быть уверены в том, что SQL Server будет обслуживать эти запросы (парни из БД подумают о старой проблеме столбцов IDENTITY).

TimeStamp

Тогда решением должно быть использование столбца TimeStamp и упорядочение результатов на основе этого столбца (порядок записи не имеет значения). Это идеальное решение (как указывают другие ответы, не забудьте использовать правильный тип данных для хранения данных).

Как узнать точное время? Вы можете сделать это на стороне клиента (как в вашем примере), используя DateTime.Now функция или на стороне сервера, используя SYSDATETIME (как указано @PetrAbdulin). Это идеальное решение, если вам не нужно высокое разрешение.

ТОЧНОСТЬ

Обе функции полагаются на системный таймер Windows, и его разрешение не должно превышать 10 мс (тогда, если вам действительно нужна гранулярность 5 мс, вам следует избегать их).

В MSDN вы можете прочитать, что DateTime.Now в Windows NT имеет разрешение 10 мс, SYSDATETIME вызывает GetSystemTimeAsFileTime, FILETIME структура имеет точность 100 мс, но сам таймер не предоставлен для достижения этого результата!!! В некоторых условиях вы можете даже получить отметку в 1 мс, но это совсем не надежно. В документации по таймеру StopWatch вы можете прочитать:

Таймер, используемый классом секундомера, зависит от системного оборудования и операционной системы. IsHighResolution имеет значение true, если таймер секундомера основан на счетчике производительности с высоким разрешением. В противном случае IsHighResolution имеет значение false, что указывает на то, что таймер секундомера основан на системном таймере.

Что это значит? Системному таймеру никогда не предоставляется высокое разрешение (не следует путать точность структуры, используемой для хранения времени).

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

РЕШЕНИЯ

Если вам нужен таймер с высоким разрешением (а в Windows вы можете рассмотреть все, что < 10 мс, как высокое разрешение), вам придется иметь дело с счетчиками производительности. Взгляните на эту замечательную статью о времени. Конечно, вам не нужны все эти вещи, но это указывает на проблему.

В.NET вы можете использовать DateTime.Now с StopWatch (проверяя IsHighResolution имущество). Прочитайте этот хороший пост здесь на SO об использовании StopWatch повысить точность DateTime.Now,

ОБЩИЕ ОШИБКИ

Во-первых, не путайте точность типа данных, используемых для хранения времени, с разрешением таймера. Не имеет значения, насколько точен ваш таймер, если вы сохраняете это значение в поле низкого разрешения, но использование поля высокого разрешения не превращает ваш грубый таймер в поле высокого разрешения.

Кроме того, вы не должны использовать местное время в качестве TimeStamp, вы запутаете свои журналы, когда системное время изменится из-за перехода на летнее время. Думать об этом:

               00:00 02:00 02:01 03:00 02:00
Журнал № 1 № 2 № 3 № 4
Системное время -1

Теперь, когда вы прочтете свой журнал, вы получите этот заказ: 1, 2, 4, 3. По этой причине вы никогда не должны использовать DateTime.Now а также SYSDATETIME для ваших журналов, вы всегда должны отдавать предпочтение DateTime.UtcNow а также SYSUTCDATETIME функции (кроме того, DateTime.UtcNow немного лучше).

SQL Server имеет точность даты и времени 3 миллисекунды. У тех записей, которые перечислены вне очереди, есть та же самая отметка времени? (С миллисекундами, заканчивающимися на 0, 3 или 7).

Вам нужно поле с более высокой точностью, например тип данных datetime2 (доступный в SQL Server 2008), или инкрементный счетчик для правильной сортировки элементов.

Почему вы не используете транзакции? если данные важны, вы должны использовать SqlTransaction.

Зачем вам заказывать? У вас есть ПК на вашем столе? как он вставлен, или это временная метка вашего ПК?

Кстати, пожалуйста, не кешируйте соединения..net делает это для тебя лучше. используйте как в этом примере msdn.

Другие вопросы по тегам