sql - 我应该提交还是回滚读取事务?

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

  • 提交交易
  • 回滚事务
  • 什么都不做(这将导致事务在使用块结束时回滚)

做每一个有什么含义?

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?
    }
}

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

Stefan Moser asked 2019-09-10T18:36:11Z
12个解决方案
49 votes

你提交。 期。 没有其他合理的选择。 如果您开始了交易,则应该关闭它。 提交释放您可能拥有的任何锁,并且对ReadUncommitted或Serializable隔离级别同样敏感。 依赖隐式回滚 - 虽然技术上可能等同 - 只是糟糕的形式。

如果这还没有说服你,那么想象下一个在代码中间插入更新语句的人,并且必须追踪发生的隐式回滚并删除他的数据。

Mark Brackett answered 2019-09-10T18:36:32Z
20 votes

如果您没有更改任何内容,则可以使用COMMIT或ROLLBACK。 任何一个都会释放你已经获得的任何读锁定,并且由于你没有做任何其他更改,它们将是等效的。

Graeme Perrow answered 2019-09-10T18:36:56Z
5 votes

如果您开始交易,那么最佳做法始终是提交。 如果在您的use(事务)块中抛出异常,则事务将自动回滚。

Neil Barnwell answered 2019-09-10T18:37:20Z
3 votes

恕我直言,在事务中包装只读查询是有意义的(特别是在Java中)你可以告诉事务是“只读”,反过来JDBC驱动程序可以考虑优化查询(但不必,所以没有人 将阻止您发布INSERT)。 例如。 Oracle驱动程序将完全避免在标记为只读的事务中对查询进行表锁定,从而在大量读取驱动的应用程序上获得大量性能。

Oliver Drotbohm answered 2019-09-10T18:37:44Z
3 votes

考虑嵌套事务。

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

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

某些数据库包装器可能会将内部事务中的回滚视为发生错误的标志,并在最外层事务中回滚所有内容,无论最外层事务是否已提交或回滚。

因此,当您不能排除某些软件模块使用您的组件时,COMMIT是一种安全的方法。

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

关于性能:根据隔离级别,SELECT可能需要不同程度的LOCK和临时数据(快照)。 交易结束时清理。 这是通过COMMIT还是ROLLBACK完成并不重要。 花费的CPU时间可能没有显着差异 - COMMIT可能比ROLLBACK(两个字符更少)和其他微小差异更快解析。 显然,这只适用于只读操作!

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

Klaws answered 2019-09-10T18:38:54Z
2 votes

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

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的使用块移动到顶部。

Joel Coehoorn answered 2019-09-10T18:39:25Z
1 votes

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

set transaction isolation level read uncommitted

那么你就不必跳过C#代码中的任何环节。 在存储过程中设置事务隔离级别不会导致该设置适用于该连接的所有将来使用(由于连接已合并,因此您必须担心其他设置)。 在存储过程结束时,它只会返回到初始化连接的任何内容。

Eric Z Beard answered 2019-09-10T18:39:56Z
1 votes

ROLLBACK主要用于出现错误或异常情况,并在成功完成时使用COMMIT。

我们应该使用COMMIT(成功)和ROLLBACK(失败)来关闭事务,即使在只读事务中它似乎并不重要。 事实上,它对于一致性和面向未来非常重要。

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

  • 查询不会按预期返回一行
  • 存储过程引发异常
  • 发现的数据不一致
  • 用户中止交易,因为它花了太长时间
  • 死锁或超时

如果COMMENT和ROLLBACK正确用于只读事务,如果在某个时刻添加了DB写代码,它将继续按预期工作,例如, 用于缓存,审核或统计。

当应用程序因不可恢复的错误,网络故障,电源故障等崩溃或退出时,隐式ROLLBACK应仅用于“致命错误”情况。

Sam Watkins answered 2019-09-10T18:41:18Z
0 votes

鉴于READ不会改变状态,我什么都不做。 执行提交将不会执行任何操作,除非浪费一个周期将请求发送到数据库。 您尚未执行已更改状态的操作。 同样适用于回滚。

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

Brett McCann answered 2019-09-10T18:41:49Z
0 votes

如果将AutoCommit设置为false,则为YES。

在使用JDBC(Postgresql驱动程序)的实验中,我发现如果选择查询中断(由于超时),则除非您回滚,否则无法启动新的选择查询。

Shiv Krishna Jaiswal answered 2019-09-10T18:42:19Z
-2 votes

在您的代码示例中,您拥有的位置

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

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

如果没有,就没有“读取”事务......只有来自插入,更新和删除语句的更改(可以更改数据的语句)才在事务中......你所说的是SQL的锁定 由于影响该数据的OTHER事务,服务器会对您正在读取的数据进行处理。 这些锁的级别取决于SQL Server隔离级别。

但是,如果您的SQL语句没有改变任何内容,则无法提交或返回任何内容。

如果要更改数据,则可以在不显式启动转换的情况下更改隔离级别...每个单独的SQL语句都隐式在事务中。 显式启动事务只需要确保2个或更多语句在同一个事务中。

如果要设置事务隔离级别,只需将命令的CommandText设置为“设置事务隔离级别可重复读取”(或任何级别),将CommandType设置为CommandType.Text,然后执行命令。 (你可以使用Command.ExecuteNonQuery())

注意:如果您正在执行MULTIPLE读取语句,并希望它们全部“看到”与第一个相同的数据库状态,那么您需要设置隔离级别顶部可重复读取或可序列化...

Charles Bretana answered 2019-09-10T18:43:29Z
-3 votes

您是否需要阻止其他人阅读相同的数据? 为什么要使用交易?

@Joel - 我的问题更好地表达为“为什么在读取查询中使用事务?”

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

SELECT * FROM SomeTable WITH (NOLOCK)

编辑@评论3:由于问号标签中有“sqlserver”,我假设MSSQLServer是目标产品。 现在已经澄清了这一点,我已经编辑了标签以删除特定的产品参考。

我仍然不确定你为什么要首先在读操作上进行交易。

StingyJack answered 2019-09-10T18:44:19Z
translate from https://stackoverflow.com:/questions/309834/should-i-commit-or-rollback-a-read-transaction