Concurrency-Issue使用ExecuteNonQuery

时间:2012-04-11 10:01:38

标签: c# sql logging time-precision

我正在将简单的文本日志条目写入SQL表中,如下所示:

private SqlConnection sqlcon;

// ...

try {
    sqlcon.Open();

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

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

异步处理设置为false,所以我认为日志条目将按执行顺序出现在表中。但情况并非如此,如果在大约不到5毫秒的时间内触发了2个或更多的日志条目。

所以我的猜测是ExecuteNonQuery在不同的线程上运行,并且不同的事件混合在一起。我已经尝试过使用BeginExecuteNonQuery和EndExecuteNonQuery进行异步处理,但是它会弄乱我的代码并且无论如何都不能正常工作。

所以我的问题是:有没有办法确保,nonQueries正按照它们被触发的顺序执行?

编辑: 也许这很重要,我正在使用这样的时间戳

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

4 个答案:

答案 0 :(得分:2)

异步?

如果您打开/关闭连接,每次您无法确定时,某种程度上等同于(请原谅我,我知道这不是确切地说)调用BeginExecuteNonQuery(这是在MARS之前使用的方法)。实际上每个请求都是同步的,但是,如果你以5毫秒的间隔记录某些内容,你就无法确定SQL Server将为这些请求提供服务的顺序(这里的数据库人员会考虑旧的问题IDENTITY列。)

时间戳

然后解决方案应该是使用TimeStamp列并根据该列排序结果(写入顺序无关紧要)。这是完美的解决方案(正如其他答案所指出的那样,不要忘记使用正确的数据类型来存储数据)。

如何获得实际时间?您可以使用DateTime.Now 函数在客户端(如示例中)执行此操作,或使用SYSDATETIME在服务器端执行此操作(如@PetrAbdulin所指示)。如果您不需要高分辨率,这是一个完美的解决方案

<强> PRECISION

这两个函数都依赖于Windows系统计时器,并且它的分辨率不会超过10毫秒(那么,如果你真的需要5毫秒的粒度,你应该避免它们。)

在MSDN上,您可以看到Windows NT上的DateTime.Now分辨率为10毫秒,SYSDATETIME调用GetSystemTimeAsFileTimeFILETIME结构的精度为100毫秒,但计时器本身未被授予以实现该结果 !!!在某些情况下,你甚至可以获得1毫秒的时间,但它根本不可靠。在有关StopWatch计时器的文档中,您可以阅读:

  

秒表类使用的计时器取决于系统硬件   和操作系统。如果秒表计时器,则IsHighResolution为真   基于高分辨率性能计数器。除此以外,   IsHighResolution为false,表示秒表计时器为   基于系统计时器。

这是什么意思?永远不会授予系统计时器具有高分辨率(不要混淆用于存储时间的结构的精度)。

这可能是一个问题,取决于你将使用你的日志。如果在备份期间必须将从一个文件夹复制的文件列表记录到另一个文件夹,则可能不需要这样的精度。如果你的日志可能被用于合法的东西(是的,我知道他们应该没有任何合法价值)或调试一个微妙的线程问题,那么你将需要它。

<强>解

如果您需要高分辨率计时器(并且在Windows上,您可能会考虑任何高于10 ms的高分辨率),您必须处理性能计数器。看看this great article about timing。当然,你不需要所有的东西,但它指出了问题。

在.NET中,您可以将DateTime.NowStopWatch一起使用(检查IsHighResolution属性)。请阅读this good post here on SO,了解如何使用StopWatch来提高DateTime.Now的精确度。

常见错误

首先,不要将用于存储时间的数据类型的精度与计时器的分辨率混淆。如果将该值存储在低分辨率字段中,但是使用高分辨率字段并不会将粗略计时器转换为高分辨率字段,那么定时器的精确程度无关紧要。

此外,您不应将本地时间用作TimeStamp,当系统时间因夏令时而改变时,您会混淆日志。想一想:

               00:00   02:00   02:01   03:00   02:00
Log              #1      #2      #3              #4
System time                             -1

现在你什么时候阅读你的日志,你会收到这个订单:1,2,4,3。因此你应该从不使用DateTime.Now和{{1}对于您的日志,您应该始终更喜欢SYSDATETIMEDateTime.UtcNow函数(此外SYSUTCDATETIME的效果会更好一点。)

答案 1 :(得分:1)

如果使用SQL Server SYSDATETIME插入时间戳,该怎么办?我认为ExecuteNonQuery是同步的,但你的时间戳可能有问题。

有一份关于time resolution in Windows的好文件。

答案 2 :(得分:1)

SQL Server的日期时间精度为3毫秒。列出无序的那些条目是否具有相同的时间戳? (毫秒以0,3或7结尾)。

您需要一个具有更高精度的字段,例如datetime2数据类型(可从SQL Server 2008获得),或者需要正确排序的项目的增量计数器。

答案 3 :(得分:0)

你为什么不使用交易?如果数据很重要,那么你应该使用SqlTransaction。

为什么需要订购?你桌子上有PK吗?它是如何插入的,或者是你的PK的时间戳?

顺便说一句,请不要缓存连接。 .net正在为你做得更好。像在这个msdn example中一样使用。