2

我正在将简单的文本日志条目写入 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 一起使用,但它会弄乱我的代码并且无论如何都无法正常工作。

所以我的问题是:有什么方法可以确保非查询按照它们被触发的顺序执行?

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

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

4 回答 4

2

异步?

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

时间戳

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

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

精确

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

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

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

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

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

解决方案

如果您需要一个高分辨率计时器(并且,在 Windows 上,您可以将任何小于 10 毫秒的时间视为高分辨率),您必须处理性能计数器。看看这篇关于时间的好文章。当然,您不需要所有这些东西,但它指出了问题所在。

在 .NET 中,您可以使用DateTime.Nowa StopWatch(检查IsHighResolution属性)。在 SO 上阅读这篇关于使用StopWatch提高DateTime.Now.

常见错误

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

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

               00:00 02:00 02:01 03:00 02:00
日志 #1 #2 #3 #4
系统时间-1

现在你什么时候阅读你的日志,你会得到这个顺序:1、2、4、3。出于这个原因,你永远不应该使用DateTime.NowandSYSDATETIME对于你的日志,你应该总是更喜欢DateTime.UtcNowandSYSUTCDATETIME函数(而且性能DateTime.UtcNow有点更好的)。

于 2012-04-11T10:17:26.303 回答
1

如果您使用 SQL Server SYSDATETIME插入时间戳会怎样?我认为这ExecuteNonQuery是同步的,但您的时间戳可能有问题。

Windows 中有一个关于时间分辨率的好文档。

于 2012-04-11T10:26:01.090 回答
1

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

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

于 2012-04-11T10:27:12.003 回答
0

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

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

顺便说一句,请不要缓存连接。.net 为您做得更好。在这个 msdn示例中使用 using like 。

于 2012-04-11T10:31:26.247 回答