简体   繁体   English

使用 ExecuteNonQuery 的并发问题

[英]Concurrency-Issue using ExecuteNonQuery

I'm writing simple text log Entries to an SQL-table like so:我正在将简单的文本日志条目写入 SQL 表,如下所示:

private SqlConnection sqlcon;

// ...

try {
    sqlcon.Open();

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

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

Asynchronous Processing is set to false, so i thought the log entries would appear in the table in the order of execution.异步处理设置为 false,所以我认为日志条目会按执行顺序出现在表中。 But that is not the case, if 2 or more log entries are fired in about less than 5ms.但情况并非如此,如果在大约不到 5 毫秒的时间内触发了 2 个或更多日志条目。

So my guess is that ExecuteNonQuery runs on a different thread and somehow the different events get mixed up.所以我的猜测是 ExecuteNonQuery 在不同的线程上运行,并且不同的事件以某种方式混淆了。 I've already tried using Asynchronous Processing with BeginExecuteNonQuery and EndExecuteNonQuery, but it messes up my code and is not really working anyway.我已经尝试将异步处理与 BeginExecuteNonQuery 和 EndExecuteNonQuery 结合使用,但它弄乱了我的代码并且无论如何都无法正常工作。

So my question is: Is there any way to ensure , that the nonQueries are being executed in exactly the order they were triggered?所以我的问题是:有没有什么办法可以确保nonQueries 完全按照它们被触发的顺序执行?

edit: Maybe it's important, i'm using a timestamp like so编辑:也许这很重要,我正在使用这样的时间戳

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

Asynchronous?异步?

If you open/close the connection each time you can't be really sure of that, it's somehow equivalent (please forgive me, I know this isn't really exact) to call BeginExecuteNonQuery (it was the method used before MARS).如果您每次都打开/关闭连接,您不能真正确定这一点,那么调用BeginExecuteNonQuery (这是 MARS 之前使用的方法)在某种程度上等同于(请原谅我,我知道这并不准确)。 Actually each request is synchronous but, if you log something with a 5 ms interval, you can't be sure about the order SQL Server will serve that requests (DB guys here will think about the old problem of IDENTITY columns).实际上每个请求都是同步的,但是,如果您以 5 毫秒的间隔记录某些内容,则您无法确定 SQL 服务器将为该请求提供服务的顺序(这里的数据库人员会考虑 IDENTITY 列的问题)。

TimeStamp时间戳

Then the solution should be to use a TimeStamp column and to order results based on that column (write order doesn't matter).那么解决方案应该是使用 TimeStamp 列并根据该列对结果进行排序(写入顺序无关紧要)。 It's the perfect solution (as pointed by other answers do not forget to use the right data type to store the data).这是完美的解决方案(正如其他答案所指出的那样,不要忘记使用正确的数据类型来存储数据)。

How to get the actual time?如何获得实际时间? You can do it on client-side (as in your example) using DateTime.Now function or on server-side using SYSDATETIME (as pointed by @PetrAbdulin).您可以在客户端(如您的示例中)使用DateTime.Now function或在服务器端使用SYSDATETIME (如@PetrAbdulin 所指出)。 It's a perfect solution if you do not need high resolution .如果您不需要高分辨率,这是一个完美的解决方案。

PRECISION精确

Both functions rely on Windows system timer and it's resolution isn't granted to be more than 10 ms (then, if you really need 5 ms granularity, you should avoid them).这两个函数都依赖于 Windows 系统定时器,并且它的分辨率不超过 10 毫秒(然后,如果你真的需要 5 毫秒的粒度,你应该避免它们)。

On MSDN you can read that DateTime.Now on Windows NT has a resolution of 10 ms, the SYSDATETIME calls GetSystemTimeAsFileTime , the FILETIME structure has a 100 ms precision but the timer itself isn't granted to achieve that result !!!在 MSDN 上,您可以阅读 Windows NT 上的DateTime.Now的分辨率为 10 毫秒, SYSDATETIME调用GetSystemTimeAsFileTimeFILETIME结构具有 100 毫秒的精度,但定时器本身未被授予实现该结果 In some conditions you may even get a 1 ms tick but it's not reliable at all.在某些情况下,您甚至可能会得到 1 毫秒的滴答声,但它根本不可靠 In the documentation about the StopWatch timer you can read:在有关 StopWatch 计时器的文档中,您可以阅读:

The timer used by the Stopwatch class depends on the system hardware and operating system.秒表 class 使用的计时器取决于系统硬件和操作系统。 IsHighResolution is true if the Stopwatch timer is based on a high-resolution performance counter.如果秒表计时器基于高分辨率性能计数器,则 IsHighResolution 为真。 Otherwise, IsHighResolution is false, which indicates that the Stopwatch timer is based on the system timer.否则,IsHighResolution 为 false,表示 Stopwatch 计时器基于系统计时器。

What does it mean?这是什么意思? A system timer is never granted to have a high resolution (do not be confused by the precision of the structure used to store the time).系统计时器永远不会被授予高分辨率(不要被用于存储时间的结构的精度所混淆)。

It could be a problem or not, it depends for what you'll use your logs .这可能是一个问题,这取决于您将使用日志的内容 If you have to log the list of file copied from one folder to another during a backup then you may not need such precision.如果您必须在备份期间记录从一个文件夹复制到另一个文件夹的文件列表,那么您可能不需要这样的精度。 If your logs may be used for legal stuffs (yes, I know they should not have any legal value) or to debug a subtle threading issue then you'll need it.如果您的日志可能用于合法用途(是的,我知道它们不应该具有任何法律价值)或用于调试微妙的线程问题,那么您将需要它。

SOLUTIONS解决方案

If you need a high resolution timer (and, on Windows, you may consider anything < 10 ms as high resolution) you have to deal with Performance Counters.如果您需要高分辨率计时器(并且,在 Windows 上,您可以将小于 10 毫秒的任何东西视为高分辨率),您必须处理性能计数器。 Take a look at this great article about timing .看看这篇关于计时的精彩文章 Of course you do not need all that stuff but it points the problem.当然,您不需要所有这些东西,但它指出了问题所在。

In .NET you may use DateTime.Now with a StopWatch (checking the IsHighResolution property).在 .NET 中,您可以将DateTime.NowIsHighResolution StopWatch )。 Read this good post here on SO about the use of the StopWatch to increase the precision of DateTime.Now . 在 SO 上阅读这篇关于使用StopWatch提高DateTime.Now精度的好文章。

COMMON ERRORS常见错误

First do not confuse the precision of the data type used to store the time with the resolution of the timer.首先不要混淆用于存储时间的数据类型的精度与计时器的分辨率。 It doesn't matter how much your timer is precise if you store that value in a low resolution field but to use a high resolution field doesn't transform your coarse timer into a high resolution one.如果您将该值存储在低分辨率字段中,但使用高分辨率字段不会将您的粗略计时器转换为高分辨率,那么您的计时器的精确度并不重要。

Moreover you should not use the local time as TimeStamp, you'll confuse your logs when the system time will change because of daylight saving.此外,你不应该使用本地时间作为时间戳,当系统时间因夏令时而改变时,你会混淆你的日志。 Think about this:想一想:

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

Now when'll you read your log you'll get this order: 1, 2, 4, 3. For this reason you should NEVER use DateTime.Now and SYSDATETIME for your logs, you should always prefer the DateTime.UtcNow and SYSUTCDATETIME functions (moreover performances of DateTime.UtcNow are a little bit better).现在你什么时候阅读你的日志,你会得到这个顺序:1、2、4、3。因此,你永远不应该为你的日志使用DateTime.NowSYSDATETIME ,你应该总是更喜欢DateTime.UtcNowSYSUTCDATETIME函数(而且DateTime.UtcNow的性能要好一些)。

What if you use SQL Server SYSDATETIME to insert timestamps?如果使用 SQL Server SYSDATETIME插入时间戳怎么办? I think that ExecuteNonQuery is synchronous, but maybe something wrong with your timestamp.我认为ExecuteNonQuery是同步的,但您的时间戳可能有问题。

There is a good document about time resolution in Windows . Windows 中有一篇关于时间分辨率的好文档。

SQL Server has a datetime precision of 3 milliseconds. SQL 服务器的日期时间精度为 3 毫秒。 Do those entries that's listed out-of-order have the same timestamp?那些乱序列出的条目是否具有相同的时间戳? (With milliseconds ending with 0, 3 or 7). (毫秒以 0、3 或 7 结尾)。

You need a field with higher precision, like the datetime2 data type (available from SQL Server 2008), or an incremental counter for the items to be correctly sorted.您需要一个精度更高的字段,例如datetime2数据类型(可从 SQL Server 2008 获得),或用于正确排序项目的增量计数器。

Why aren't you using transactions?你为什么不使用交易? if the data is important then you should use SqlTransaction.如果数据很重要,那么您应该使用 SqlTransaction。

Why do you need ordering?为什么需要订购? Do you have a pk on your table?你的桌子上有pk吗? how is it inserted, or is the timestamp your pk?它是如何插入的,或者是你的 pk 的时间戳?

By the way, please don't cache the connections.顺便说一句,请不要缓存连接。 .net is doing that for you better. .net 正在为您做的更好。 use using like in this msdn example .在此 msdn 示例中使用 using like 。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM