【问题标题】:Isolation level in Sql TransactionSql Transaction 中的隔离级别
【发布时间】:2012-02-29 08:33:51
【问题描述】:

我已经在 c# 中实现了SqlTransaction 来开始、提交和回滚事务。一切正常,但是在访问那些在事务期间连接的表时我遇到了一些问题。 在事务期间我无法读取表(那些正在事务中的表)。在搜索这个时,我发现它是由于排他锁而发生的。反过来,对该数据的任何后续选择都必须等待排他锁被释放。然后,我检查了SqlTransaction 提供的所有隔离级别,但都没有奏效。 因此,我需要在事务期间释放排他锁,以便其他用户可以访问该表并读取数据。 有什么方法可以做到这一点吗? 提前致谢。

这是我的交易 C# 代码

try
{
  SqlTransaction transaction = null;                         
  using (SqlConnection connection=new SqlConnection(Connection.ConnectionString))
  {
       connection.Open();
       transaction=connection.BeginTransaction(IsolationLevel.Snapshot,"FaresheetTransaction");            
       //Here all transaction occurs   
       if (transaction.Connection != null)
       {     
               transaction.Commit();
               transaction.Dispose();
       } 
   }
}
catch (Exception ex)
{
   if (transaction.Connection != null)
       transaction.Rollback();
   transaction.Dispose();   
}   `                         

此代码工作正常,但问题是当我在事务期间访问表的数据(在事务期间访问的那些)时。应用程序的其他部分正在访问这些表。因此,当我尝试从表中读取数据时,它会引发异常。

【问题讨论】:

  • 你在这里的意图是什么?更新时不取锁?还是让其他读者忽略锁? (这是有充分理由的)
  • 我不想为其他用户锁定事务处理表......事务可以持续很长时间,因为在事务处理时,成千上万的数据插入到表中,所以它可能需要很长时间......所以,在交易期间,我希望其他用户可以访问该表并可以读取数据......

标签: c# sql sql-server-2005 transactions sqlconnection


【解决方案1】:

按照设计,SQL 事务是 ACID。尤其是“我”在这里伤害了您 - 这是设计以防止其他连接看到不一致的中间状态。

单个读取连接可以通过使用NOLOCK 提示或READ UNCOMMITTED 隔离级别来选择忽略此规则,但听起来您希望写入 连接不占用锁。好吧,这不会发生。

然而,可能帮助读者使用 snapshot 隔离,它实现了隔离而不需要读者获取锁(通过查看,顾名思义,一个点- 事务开始时一致状态的及时快照)。

但是,IMO 建议您查看以下任一选项:

  • 来自作者的多个更细粒度的事务
  • 在暂存表(数据的并行副本)中执行工作,然后通过一些批量插入/更新/删除操作将其合并到真实数据中,从而最大限度地减少事务时间

第一个比较简单。

一个简单的事实是:如果您进行一个长时间运行的事务,该事务对大量数据进行操作,是的,您将导致问题。这就是您不这样做的原因。系统运行正常。

【讨论】:

  • 您的第二个解决方案几乎就是快照隔离在用于执行写入的事务时为您所做的事情;)
  • 我使用了快照隔离级别,但它再次给出了同样的问题....实际上,当我使用“select * from tbl_name with (nolock)”时它可以工作,但这个选择不适合我,因为: 1. 已经显示了尚未提交的事务的数据。 2. 表已经被应用程序查询了,所以我无法通过代码中的所有查询来更改它......
  • @akash88 是需要使用快照隔离的阅读器;如果它没有帮助,那么我想知道您是否只是没有启用快照隔离。但从根本上说,你是在尝试做一些违背系统的事情。一个等效的示例可能是“我在重写大块文件时打开了一个大文件;我不想允许共享访问,因为这样读者会看到正在进行的编辑;但文件在我关闭之前不会被锁定它!我该如何解决这个问题?回答:你没有;你设计的系统不同。
  • @akash88 您需要提供更多详细信息,特别是您在哪里使用了哪些事务/隔离级别。 Marc 已经用必要的细节回答了这个问题,如果它不起作用,你没有遵循他的指导方针或以非预期的方式使用 c# 类
【解决方案2】:

尝试在事务中执行读取,并使用隔离级别READ UNCOMMITTED。这将防止读取被锁定,但可能会产生无效结果:

SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED    
BEGIN TRANSACTION

SELECT * FROM Table

COMMIT TRANSACTION

存在一种误解,认为处理事务/隔离级别仅在写入时很重要,而实际上在读取时同样重要。

【讨论】:

  • 感谢您的回复.....我已经使用了所有类型的隔离级别,但它可以解决我的场景...实际上,我在 C# 中使用 BeginTransaction() 方法SqlTransaction ....我想为那些在事务中使用的表授予其他用户的访问权限.....但是,在每个隔离级别,排他锁都在发生......
  • 如果要写入表,排他锁是强制性的。唯一的例外是当您使用隔离级别快照时,但据我所知,这仅从 sql 2008 开始可用。因此,允许读取的唯一方法是在读取期间使用未提交的读取或混沌隔离级别,如上所述跨度>
  • 刚刚检查过,快照隔离在 SQL 2005 中已经可用,所以你去吧。写作时使用快照隔离,你应该很高兴。由于您似乎只是在阅读其他操作,因此应该没有问题。
  • 我使用了快照隔离级别,但它再次给出了同样的问题....实际上,当我使用“select * from tbl_name with (nolock)”时它可以工作,但这个选择不适合我,因为: 1. 已经显示了尚未提交的事务的数据。 2. 表已经被应用程序查询了,所以我无法通过代码中的所有查询来更改它......
【解决方案3】:

@AKASH88,SNAPSHOT 隔离级别正是您要找的。​​p>

您说即使使用 SNAPSHOT 也无法按预期工作,正在发生排他锁,我可以理解,我遇到了同样的问题。

确保您不仅在数据库选项上启用 SNAPSHOT,而且还必须打开 READ COMMITTED SNAPSHOT

这是 SQL Server 2008,所以仍然不确定这个答案是否有帮助:(

最好的问候!

【讨论】:

    【解决方案4】:

    问题不在于写入数据库的级别,而在于读取值的级别。您正在尝试读取正在插入的值。尝试将您的选择查询更改为以下内容:

    select * from your_table_with_inserts with (nolock)
    

    然而,这会覆盖当前事务的隔离级别,并可能导致脏读。

    所以问题是:您是在所有查询上使用事务还是仅插入/更新?

    【讨论】:

      猜你喜欢
      • 2011-06-28
      • 1970-01-01
      • 2015-06-19
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多