【问题标题】:What's the point to enclose select statements in a transaction?在事务中包含选择语句有什么意义?
【发布时间】:2011-03-07 03:11:32
【问题描述】:

在事务中包含选择语句有什么意义?我认为选择语句只是从数据库中“获取”数据,它们没有机会回滚某些东西,因为您无法更改数据。那么,这是否意味着我们永远不需要在事务中放置 select 语句?我说的对吗?

谢谢。

【问题讨论】:

    标签: sql-server-2005 tsql transactions


    【解决方案1】:

    您是对的:在标准 isolation levelread committed 中,您不需要在事务中包装 select 语句。无论您是否将它们包装在事务中,都将保护 Select 语句免受脏读。

    connection 1:                          connection 2:
    
                                           begin transaction
                                           update user set name = 'Bill' where id = 1
    select name from users where id = 1
                                           rollback transaction
    

    select 语句不会读取回滚的更新:它们没有包装在事务中并不重要。

    如果您需要repeatable reads,那么将选择包装在默认事务中没有帮助:

    connection 1:                          connection 2:
    
    begin transaction
    select name from users where id = 1
                                           update user set name = 'Bill' where id = 1
    select name from users where id = 1
    commit transaction
    

    begincommit 语句在这里不起作用:第二个 select 可能读取旧名称,或者它可能读取新名称.

    但是,如果您以更高的隔离级别运行,例如 serializablerepeatable read,则该组将受到不可重复读取的保护:

    connection 1:                          connection 2:
    
    set transaction isolation level
        repeatable read
    begin transaction
    select name from users where id = 1
                                           update user set name = 'Bill' where id = 1
    select name from users where id = 1              |
    commit transaction                               |
                                                     |--> executed here
    

    在这种情况下,update 将阻塞直到第一个事务完成。

    很少使用较高的隔离级别,因为它们会降低可以同时在数据库中工作的人数。在最高级别 serializable,报告查询会暂停任何更新活动。

    【讨论】:

    • 关于你的最后一段,为什么更新会阻塞?难道更新仍然会在没有阻塞的情况下进行,但是第一个事务是可重复读取的,还会继续使用旧值吗?
    • @Pacerier:Repeatable-read 表示如果您阅读两次,第二次阅读将返回相同的结果。与 Oracle 或 PostgeSQL 不同,SQL Server 不会保留旧值。您可以使用 SET READ_COMMITTED_SNAPSHOT ON 更改此行为。
    • 嗯,这很奇怪,似乎是 MySQL 的默认设置。那么对于sql server,在运行set read_committed_snapshot on之后,说“更新”将不再阻塞是对的吗?
    • @Pacerier:MySQL 不支持 MVCC。 MySQL 的默认设置是返回不一致的数据。使用read_committed_snapshot,读取不会阻止更新,但一个更新仍会阻止另一个更新。为此,SQL Server 必须做更多的磁盘 I/O。
    【解决方案2】:

    您可能在此交易期间进行其他更新/插入。如果您访问数据库的代码是以可重用的方式编写的,那么您可能不知道选择是否是事务中唯一发生的事情。

    您的 select 语句可能希望在事务期间保持一致,并与其他事务中发生的数据更改保持一致。您需要在系统中设置某种isolation level,以防止脏读(读取另一个事务中未提交的更改)或幻读(读取另一个事务中已提交的更改)。

    不用说,使用交易会更好地为您服务。

    【讨论】:

      【解决方案3】:

      单个 SELECT 语句一开始是原子的 - 将它包含在事务中是多余的。如果有多个 SELECT 语句,则可以保证在所有语句都完成之前没有人更改任何影响它们的任何东西。

      【讨论】:

        【解决方案4】:

        可能不会更改数据,但其他一些数据库连接可以。

        【讨论】:

          【解决方案5】:

          没有。

          事务为您提供数据库的一致视图。

          如果您希望您的选择在重复它们时返回相同的结果,事务可以提供。

          【讨论】:

          • 缺乏细节。我认为安多玛的回答是最准确的
          • @Konrad 八年前,您在提出问题后不到 5 分钟就写出这样的答案?
          • 是的,我是。这仍然是一个答案。
          【解决方案6】:

          使用带有选择的事务的另一个原因:

          在某些时候,您可能希望从参与事务的其他方法中调用您的 select 方法,并且您希望 select 参与当前事务。如果您有一个一致的设计,其中所有数据库操作都在事务中执行,那么任何方法的调用者都知道它将参与他们的事务。

          对于少量的前期开发成本,这可以帮助避免一些相当大的变化,以便在需求发生变化或添加新需求时试图强行交易。

          【讨论】:

            【解决方案7】:

            如果您确定正在发生的所有事情都是一个 SELECT,那么它就不需要在事务中。您是否 100% 确定现在和永远只是一个 SELECT?

            【讨论】:

              猜你喜欢
              • 2021-03-17
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 2016-05-26
              • 2014-04-09
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              相关资源
              最近更新 更多