代码之家  ›  专栏  ›  技术社区  ›  Stefan Moser

我应该提交还是回滚一个读事务?

  •  84
  • Stefan Moser  · 技术社区  · 16 年前

    我有一个在事务中执行的读取查询,以便可以指定隔离级别。一旦查询完成,我应该怎么做?

    • 提交事务
    • 回滚事务
    • 不执行任何操作(这将导致事务在使用块末尾回滚)

    做每件事的意义是什么?

    using (IDbConnection connection = ConnectionFactory.CreateConnection())
    {
        using (IDbTransaction transaction = connection.BeginTransaction(IsolationLevel.ReadUncommitted))
        {
            using (IDbCommand command = connection.CreateCommand())
            {
                command.Transaction = transaction;
                command.CommandText = "SELECT * FROM SomeTable";
                using (IDataReader reader = command.ExecuteReader())
                {
                    // Read the results
                }
            }
    
            // To commit, or not to commit?
        }
    }
    

    编辑:问题不在于是否应该使用事务或者是否有其他方法来设置事务级别。问题是,如果提交或回滚不修改任何内容的事务,这是否会产生任何影响。是否有性能差异?它会影响其他连接吗?还有什么区别吗?

    12 回复  |  直到 6 年前
        1
  •  47
  •   Mark Brackett    16 年前

    你承诺。期间。没有其他明智的选择。如果您启动了一个事务,您应该关闭它。提交将释放您可能拥有的任何锁,并且对于readuncommitted或serializable隔离级别也同样敏感。依赖于隐式回滚——虽然在技术上可能是等效的——只是形式不好。

    如果这还不能让你信服,想象下一个在代码中间插入update语句的人,他必须跟踪发生的隐式回滚并删除他的数据。

        2
  •  19
  •   Graeme Perrow    16 年前

    如果您没有更改任何内容,那么可以使用提交或回滚。任何一个都将释放您获得的任何读锁,并且由于您没有进行任何其他更改,它们将是等效的。

        3
  •  5
  •   Neil Barnwell    16 年前

    如果您开始了一个事务,那么最佳实践总是提交它。如果在使用(事务)块内引发异常,则事务将自动回滚。

        4
  •  3
  •   Oliver Drotbohm    16 年前

    IMPO在打包事务中只读查询是有意义的(特别是在爪哇),您可以将事务描述为“只读”,JDBC驱动程序可以考虑优化查询(但不必如此),所以没有人会阻止您发出 INSERT 尽管如此)例如,在标记为只读的事务中,Oracle驱动程序将完全避免表锁定查询,这在重读驱动的应用程序上获得了很大的性能。

        5
  •  3
  •   Klaws    7 年前

    考虑 嵌套事务 .

    大多数RDBMSE不支持嵌套事务,或者尝试以非常有限的方式模拟它们。

    例如,在MS SQL Server中,内部事务中的回滚(这不是真正的事务,MS SQL Server只计算事务级别!)将回滚 最外面的 事务(这是真正的事务)。

    某些数据库包装程序可能会将内部事务中的回滚视为发生错误的迹象,并回滚最外面事务中的所有内容,而不管最外面的事务是提交的还是回滚的。

    因此,当您不能排除某个软件模块使用了您的组件时,提交是安全的方法。

    请注意,这是问题的一般答案。代码示例通过打开一个新的数据库连接巧妙地解决了外部事务的问题。

    关于性能:根据隔离级别,选择可能需要不同程度的锁和临时数据(快照)。这在事务关闭时清除。不管这是通过提交还是回滚来完成。在CPU时间上可能有一个微不足道的差异——提交可能比回滚(少于两个字符)和其他细微的差异更快地进行解析。显然,这只适用于只读操作!

    完全没有要求:另一个可能会阅读代码的程序员可能会认为回滚意味着一个错误条件。

        6
  •  2
  •   Joel Coehoorn    16 年前

    只是一个旁注,但是您也可以这样编写代码:

    using (IDbConnection connection = ConnectionFactory.CreateConnection())
    using (IDbTransaction transaction = connection.BeginTransaction(IsolationLevel.ReadUncommitted))
    using (IDbCommand command = connection.CreateCommand())
    {
        command.Transaction = transaction;
        command.CommandText = "SELECT * FROM SomeTable";
        using (IDataReader reader = command.ExecuteReader())
        {
            // Do something useful
        }
        // To commit, or not to commit?
    }
    

    如果你只是重新构建了一些东西,你也许可以将IDataReader的使用块也移动到顶部。

        7
  •  1
  •   Eric Z Beard    16 年前

    如果将SQL放入存储过程并将其添加到查询上方:

    set transaction isolation level read uncommitted
    

    那么你就不必跳过C代码中的任何一个环。在存储过程中设置事务隔离级别不会导致该设置应用于该连接的所有未来用途(由于连接被合并,因此您必须担心其他设置)。在存储过程的末尾,它只返回到初始化连接时使用的任何对象。

        8
  •  1
  •   Sam Watkins    9 年前

    回滚主要用于错误或异常情况下,并在成功完成时提交。

    我们应该通过提交(为了成功)和回滚(为了失败)来关闭事务,即使在只读事务的情况下,这似乎并不重要。事实上,它确实很重要,为了一致性和未来的检验。

    只读事务可以在许多方面逻辑上“失败”,例如:

    • 查询没有按预期返回一行
    • 存储过程引发异常
    • 发现获取的数据不一致
    • 用户中止事务,因为它花费的时间太长
    • 死锁或超时

    如果将commit和rollback正确地用于只读事务,那么如果在某个时间点(例如用于缓存、审核或统计)添加了db write代码,它将继续按预期工作。

    隐式回滚只能用于“致命错误”情况,即应用程序崩溃或退出时出现不可恢复的错误、网络故障、电源故障等。

        9
  •  0
  •   Brett McCann    16 年前

    如果一个读不改变状态,我什么也不做。执行提交除了浪费一个周期将请求发送到数据库之外,什么都不做。您尚未执行已更改状态的操作。对于回滚也是如此。

    但是,应该确保清理对象并关闭与数据库的连接。如果反复调用此代码,不关闭连接可能会导致问题。

        10
  •  0
  •   Shiv Krishna Jaiswal    6 年前

    如果设置autocommit为false,则为yes。

    在JDBC(PostgreSQL驱动程序)的一个实验中,我发现如果select查询中断(因为超时),那么除非回滚,否则您不能启动新的select查询。

        11
  •  -2
  •   Charles Bretana    16 年前

    在代码示例中,

    1. //做一些有用的事情

      您是否正在执行更改数据的SQL语句?

    如果没有,就没有“读取”事务…只有来自insert、update和delete语句(可以更改数据的语句)的更改才在事务中…您所说的是SQL Server对您正在读取的数据加锁,因为其他事务会影响该数据。这些锁的级别取决于SQL Server隔离级别。

    但是,如果您的SQL语句没有更改任何内容,则不能提交或回滚任何内容。

    如果您正在更改数据,那么您可以更改隔离级别,而无需显式启动传输…每个SQL语句都隐含在事务中。显式启动事务只是为了确保两个或多个语句在同一事务中才有必要。

    如果您只想设置事务隔离级别,那么只需将命令的commandText设置为“设置事务隔离级别可重复读取”(或您想要的任何级别),将commandType设置为commandType.text,然后执行该命令。(可以使用command.executeNonQuery())

    注意:如果您正在执行多个read语句,并且希望它们都“看到”与第一个语句相同的数据库状态,那么您需要设置隔离级别top repeatable read或serializable…

        12
  •  -3
  •   StingyJack    16 年前

    是否需要阻止其他人读取相同的数据?为什么要使用事务?

    @乔尔-我的问题最好用“为什么在读查询上使用事务”这样的措词。

    @如果要使用临时SQL而不是存储过程,那么只需在查询中的表后添加WITH(NOLOCK)。这样,您就不会在应用程序和数据库中产生事务开销(尽管是最小的)。

    SELECT * FROM SomeTable WITH (NOLOCK)
    

    edit@comment 3:因为问题标签中有“sqlserver”,所以我假设mssqlserver是目标产品。现在这一点已经被澄清,我已经编辑了标签以删除特定的产品参考。

    我仍然不知道为什么你要在读操作上做交易。