9

我正在编写一些将在生产中运行的日志记录/审计代码(不仅仅是在抛出错误时或在开发时)。在阅读了 Coding Horror 在死锁和日志记录方面的经验后,我决定应该寻求建议。(Jeff 的“不记录”解决方案对我不起作用,这是法律规定的安全审计)

是否有合适的隔离级别来最小化争用和死锁?我可以添加到插入语句或存储过程中的任何查询提示?

我非常关心除审计表之外的所有事务的事务完整性。这个想法是,将记录这么多,如果一些条目失败,这不是问题。如果日志记录停止了其他一些事务——那就不好了。

我可以登录到数据库或文件,尽管登录到文件的吸引力较小,因为我需要能够以某种方式显示结果。记录到文件将(几乎)保证记录不会干扰其他代码。

4

4 回答 4

5

一个正常的事务(即 READ COMMITTED)插入已经进行了“最小”锁定。插入密集型应用程序不会在插入上发生死锁,无论插入与其他操作混合的顺序如何。在最坏的情况下,密集的插入系统可能会在发生插入的热点上导致页面锁存器争用,但不会导致死锁。

To cause deadlocks as described by Jeff there has to be more at play, like any one of the following:

  • The system is using a higher isolation level (they had it coming then and well deserve it)
  • They were reading from the log table during the transaction (so is no longer 'append-only')
  • The deadlock chain involved application layer locks (ie. .Net lock statements in the log4net framework) resulting in undetectable deadlocks (ie. application hangs). Given that solving the problem involved looking at process dumps, I guess this is the scenario they were having.

因此,只要您只在 READ COMMITTED 隔离级别事务中插入日志记录,您就是安全的。如果您预料到我怀疑 SO 有同样的问题(即涉及应用程序层锁的死锁),那么再多的数据库魔法也无法拯救您,因为即使您登录单独的事务或单独的连接,问题仍然会显现出来。

于 2009-06-13T08:16:04.900 回答
5

如果您不关心日志记录表的一致性,为什么不从单独的线程执行所有日志记录。

我可能不会在记录之前等待事务完成,因为日志对于诊断长期运行的事务至关重要。此外,这使您能够查看回滚的事务所做的所有工作。

在日志线程中获取堆栈跟踪和所有日志数据, 当有新的日志消息时将其放入队列中,在单个事务中将它们刷新到数据库。

最小化锁定的步骤:

  • (KEY) 在主线程/连接/事务之外执行所有附加到日志表。
  • 确保您的日志记录表具有单调增加的聚集索引(例如 int identity ),每次附加日志消息时该索引都会增加。这样可以确保插入的页面通常在内存中,并避免堆表对性能的影响。
  • 在事务中对日志执行多个附加(事务中的 10 次插入比事务中的 10 次插入快,并且通常获取/释放更少的锁)
  • Give it a break. Only perform logging to your db every N milliseconds. Batch up bits of works.
  • If you need to report on stuff historically, you can consider partitioning your logging table. Example: You could create a new logging table every month, and at the same time have a log VIEW that is a UNION ALL of all the older logging tables. Perform the reporting against the most appropriate source.

You will get better performance by flushing multiple logging messages in a single (smallish) transaction, and have the advantage that if 10 threads are doing work and logging stuff, only a single thread is flushing stuff to the logging table. This pipelining actually makes stuff scale better.

于 2009-06-14T03:18:28.540 回答
1

由于您不关心审计表的事务完整性,您显然可以在事务之外执行日志记录(即在它完成之后)。这将最大限度地减少对交易的影响。

此外,如果您想最大限度地减少锁定,您应该尝试确保尽可能多的查询工作负载覆盖非聚集索引。(SQL Server 2005及以上,INCLUDE在NC索引中使用语句可以大不同)

于 2009-06-13T05:41:40.110 回答
1

防止日志记录与“常规”数据库发生锁定问题的一种简单方法是不使用相同的数据库。只需为您的日志记录创建另一个数据库。作为奖励,日志数据库的快速增长不会导致主数据库出现碎片。个人而言,我通常更喜欢登录到一个文件——但话说回来,我习惯于在我的编辑器——VIM 中进行繁重的文本操作。记录到单独的数据库应该有助于避免死锁问题。

只要确保如果你尝试为你使用的日志框架编写你自己的数据库附加程序,你要非常小心你的锁(我猜这是你参考的博客文章中 Jeff 的绊脚石)。如果编写得当(请参阅 Jeff 帖子中的一些评论),您的日志框架不应该有锁定问题,除非他们做了一些奇怪的事情。

于 2009-06-13T06:39:00.393 回答