建议最小化MS SQL Server中仅附加表的锁定?

Mat*_*tin 9 sql-server logging deadlock audit-tables isolation-level

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

是否有合适的隔离级别可以最大限度地减少争用和死锁?我可以添加到insert语句或存储过程的任何查询提示?

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

我可以登录到数据库或文件,虽然登录到文件不那么有吸引力,因为我需要能够以某种方式显示结果.记录到文件会(几乎)保证日志记录不会干扰其他代码.

Rem*_*anu 5

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

要像Jeff所描述的那样造成死锁,必须有更多的方法,比如以下任何一个:

  • 系统正在使用更高的隔离级别(他们当时就已经开始使用它了,应该得到它)
  • 他们在交易期间从日志表中读取(因此不再是'仅附加')
  • 死锁链涉及应用程序层锁(即locklog4net框架中的.Net 语句),导致无法检测到的死锁(即应用程序挂起).鉴于解决问题涉及查看流程转储,我想这就是他们所拥有的方案.

因此,只要您只插入READ COMMITTED隔离级别事务的登录,您就是安全的.如果您怀疑我遇到了同样的问题(即涉及应用层锁定的死锁),那么没有多少数据库向导可以保存您,因为即使您登录单独的事务或单独连接,问题仍然可以显现.

  • 不,根本不是真的.无论您在事务中执行什么操作,INSERT都不会阻止(读取已提交)报告.并发INSERTS也永远不会出现在死锁链中,因为它们不会阻塞彼此,也不会阻塞在SELECTs之后.可以阻止插入的东西是:覆盖插入点的范围S锁(即可序列化的报告),表上的S锁(即从可重复报告中升级)或X锁(即某些活动*其他)*比'仅附加',比如升级到表X锁的更新). (3认同)
  • 在read committed中INSERT到一个表(带有聚簇索引),获取页面上的IX锁,并对该键进行X锁定.如果它无法获取页面上的IX锁,它将等待,如果它无法获取并且X锁定它将等待.SELECT语句需要共享锁.锁可能会发生冲突.死锁可能存在.阅读:http://msdn.microsoft.com/en-us/library/ms186396.aspx (2认同)
  • 与任何意图锁相冲突的S锁只能在升级方案中发生,并且S升级只能在高隔离级别发生.*insert*的X只能与范围锁冲突(因为它是*NEW*行,新键).除非使用更高的隔离级别,否则不会再发生范围锁定.我必须再次强调:APPEND ONLY日志不会导致死锁,期间.作为奖励,也不会与READ COMMITTED报告冲突. (2认同)

Sam*_*ron 5

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

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

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

最小化锁定的步骤:

  • (KEY)执行主线程/连接/事务之外的所有追加到日志记录表.
  • 确保您的日志记录表具有单调递增的聚簇索引(例如,int identity),每次附加日志消息时该索引都会增加.这可以确保插入的页面通常位于内存中,并避免使用堆表获得的性能.
  • 在事务中对日志执行多个附加(事务中的10个插入比事务中的10个插入更快,并且通常获取/释放更少的锁)
  • 休息一下.每N毫秒只执行一次对数据库的记录.批量工作.
  • 如果您需要历史地报告内容,可以考虑对日志记录表进行分区.示例:您可以每月创建一个新的日志记录表,同时具有一个日志VIEW,它是所有旧日志记录表的UNION ALL.针对最合适的来源执行报告.

通过在单个(小型)事务中刷新多个日志消息,您将获得更好的性能,并且具有如果10个线程正在执行工作和记录内容的优点,则只有一个线程正在将内容刷新到日志记录表中.这种流水线实际上使得东西更好.