[英]Concurrency-Issue using 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() */
异步处理设置为 false,所以我认为日志条目会按执行顺序出现在表中。 但情况并非如此,如果在大约不到 5 毫秒的时间内触发了 2 个或更多日志条目。
所以我的猜测是 ExecuteNonQuery 在不同的线程上运行,并且不同的事件以某种方式混淆了。 我已经尝试将异步处理与 BeginExecuteNonQuery 和 EndExecuteNonQuery 结合使用,但它弄乱了我的代码并且无论如何都无法正常工作。
所以我的问题是:有没有什么办法可以确保nonQueries 完全按照它们被触发的顺序执行?
编辑:也许这很重要,我正在使用这样的时间戳
cmd.Parameters.Add("timestamp", SqlDbType.DateTime ).Value = System.DateTime.Now;
如果您每次都打开/关闭连接,您不能真正确定这一点,那么调用BeginExecuteNonQuery
(这是 MARS 之前使用的方法)在某种程度上等同于(请原谅我,我知道这并不准确)。 实际上每个请求都是同步的,但是,如果您以 5 毫秒的间隔记录某些内容,则您无法确定 SQL 服务器将为该请求提供服务的顺序(这里的数据库人员会考虑 IDENTITY 列的老问题)。
那么解决方案应该是使用 TimeStamp 列并根据该列对结果进行排序(写入顺序无关紧要)。 这是完美的解决方案(正如其他答案所指出的那样,不要忘记使用正确的数据类型来存储数据)。
如何获得实际时间? 您可以在客户端(如您的示例中)使用DateTime.Now
function或在服务器端使用SYSDATETIME
(如@PetrAbdulin 所指出)。 如果您不需要高分辨率,这是一个完美的解决方案。
精确
这两个函数都依赖于 Windows 系统定时器,并且它的分辨率不超过 10 毫秒(然后,如果你真的需要 5 毫秒的粒度,你应该避免它们)。
在 MSDN 上,您可以阅读 Windows NT 上的DateTime.Now的分辨率为 10 毫秒, SYSDATETIME调用GetSystemTimeAsFileTime , FILETIME
结构具有 100 毫秒的精度,但定时器本身未被授予实现该结果! 在某些情况下,您甚至可能会得到 1 毫秒的滴答声,但它根本不可靠。 在有关 StopWatch 计时器的文档中,您可以阅读:
秒表 class 使用的计时器取决于系统硬件和操作系统。 如果秒表计时器基于高分辨率性能计数器,则 IsHighResolution 为真。 否则,IsHighResolution 为 false,表示 Stopwatch 计时器基于系统计时器。
这是什么意思? 系统计时器永远不会被授予高分辨率(不要被用于存储时间的结构的精度所混淆)。
这可能是一个问题,这取决于您将使用日志的内容。 如果您必须在备份期间记录从一个文件夹复制到另一个文件夹的文件列表,那么您可能不需要这样的精度。 如果您的日志可能用于合法用途(是的,我知道它们不应该具有任何法律价值)或用于调试微妙的线程问题,那么您将需要它。
解决方案
如果您需要高分辨率计时器(并且,在 Windows 上,您可以将小于 10 毫秒的任何东西视为高分辨率),您必须处理性能计数器。 看看这篇关于计时的精彩文章。 当然,您不需要所有这些东西,但它指出了问题所在。
在 .NET 中,您可以将DateTime.Now
与IsHighResolution
StopWatch
)。 在 SO 上阅读这篇关于使用StopWatch
提高DateTime.Now
精度的好文章。
常见错误
首先不要混淆用于存储时间的数据类型的精度与计时器的分辨率。 如果您将该值存储在低分辨率字段中,但使用高分辨率字段不会将您的粗略计时器转换为高分辨率,那么您的计时器的精确度并不重要。
此外,你不应该使用本地时间作为时间戳,当系统时间因夏令时而改变时,你会混淆你的日志。 想一想:
00:00 02:00 02:01 03:00 02:00 Log #1 #2 #3 #4 System time -1
现在你什么时候阅读你的日志,你会得到这个顺序:1、2、4、3。因此,你永远不应该为你的日志使用DateTime.Now
和SYSDATETIME
,你应该总是更喜欢DateTime.UtcNow
和SYSUTCDATETIME
函数(而且DateTime.UtcNow
的性能要好一些)。
如果使用 SQL Server SYSDATETIME插入时间戳怎么办? 我认为ExecuteNonQuery
是同步的,但您的时间戳可能有问题。
Windows 中有一篇关于时间分辨率的好文档。
SQL 服务器的日期时间精度为 3 毫秒。 那些乱序列出的条目是否具有相同的时间戳? (毫秒以 0、3 或 7 结尾)。
您需要一个精度更高的字段,例如datetime2数据类型(可从 SQL Server 2008 获得),或用于正确排序项目的增量计数器。
你为什么不使用交易? 如果数据很重要,那么您应该使用 SqlTransaction。
为什么需要订购? 你的桌子上有pk吗? 它是如何插入的,或者是你的 pk 的时间戳?
顺便说一句,请不要缓存连接。 .net 正在为您做的更好。 在此 msdn 示例中使用 using like 。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.