SQL Server - 事务隔离级别和锁

SQL Server - 事务隔离级别和锁

SET TRANSACTION ISOLATION LEVEL

控制SQL语句的锁行为和行版本控制行为

-- Syntax for SQL Server and Azure SQL Database
  
SET TRANSACTION ISOLATION LEVEL
    { READ UNCOMMITTED      -- 读未提交
    | READ COMMITTED        -- 读提交,有**READ_COMMITTED_SNAPSHOT** 开关
    | REPEATABLE READ       -- 可重复读
    | SNAPSHOT              -- 快照隔离
    | SERIALIZABLE          -- 串行化
    }
  • 脏读:所****谓的脏读,其实就是读到了别的事务回滚前的脏数据。比如事务B执行过程中修改了数据X,在未提交前,事务A读取了X,而事务B却回滚了,这样事务A就形成了脏读

  • 不可重复读:事务A首先读取了一条数据,然后执行逻辑的时候,事务B将这条数据改变了,然后事务A再次读取的时候,发现数据不匹配了,就是所谓的不可重复读了

  • 幻读:事务A首先根据条件索引得到N条数据,然后事务B改变了这N条数据之外的M条或者增添了M条符合事务A搜索条件的数据,导致事务A再次搜索发现有N+M条数据了,就产生了幻读

  • 不可重复读 与 幻读有些相似,但是前者针对的是update 和 delete ,后者对应的是 insert

  • READ UNCOMMITTED指定语句可以读取已由其他事务修改但尚未提交的行

    在 READ UNCOMMITTED 级别运行的事务,不会发出共享锁来防止其他事务修改当前事务读取的数据。 READ UNCOMMITTED 事务也不会被排他锁阻塞,排他锁会禁止当前事务读取其他事务已修改但尚未提交的行。 设置此选项之后,可以读取未提交的修改,这种读取称为脏读。 在事务结束之前,可以更改数据中的值,行也可以出现在数据集中或从数据集中消失。 该选项的作用与在事务内所有 SELECT 语句中的所有表上设置 NOLOCK 相同。 这是隔离级别中限制最少的级别。

  • READ COMMITTED(SQL Server 的默认设置):指定语句不能读取已由其他事务修改但尚未提交的数据,这样可以避免脏读

    在 READ COMMITTED 级别运行的事务,其他事务可以在当前事务的各个语句之间更改数据,从而产生不可重复读取虚拟数据(幻读)

    READ COMMITTED 的行为取决于 READ_COMMITTED_SNAPSHOT 数据库选项的设置:

    • 如果将 READ_COMMITTED_SNAPSHOT 设置为 OFF(SQL Server 上的默认设置),则 数据库引擎 会使用共享锁防止其他事务在当前事务执行读取操作期间修改行。 共享锁还会阻止语句在其他事务完成之前读取由这些事务修改的行。 共享锁类型确定它将于何时释放。 行锁在处理下一行之前释放。 页锁在读取下一页时释放,表锁在语句完成时释放。
    • 如果将 READ_COMMITTED_SNAPSHOT 设置为 ON(Azure SQL 数据库上的默认设置),则 数据库引擎 会使用行版本控制为每个语句提供一个在事务上一致的数据快照,因为该数据在语句开始时就存在。 不使用锁来防止其他事务更新数据。

    备注:

    选择事务隔离级别不影响为保护数据而获取的锁。事务总是在要其修改的数据上获取排他锁并在事务完成之前持有该锁,不管为该事务设置了什么样的隔离级别。

    此外,在 READ_COMMITTED 隔离级别进行的更新使用所选数据行的更新锁,而在 SNAPSHOT 隔离级别进行的更新使用行版本来选择要更新的行。 对于读取操作,事务隔离级别主要定义保护级别,以防受到其他事务所做更改的影响。

    即使当 READ_COMMITTED_SNAPSHOT 数据库选项设置为 ON 时,可以通过使用 READCOMMITTEDLOCK 表提示为 READ COMMITTED 隔离级别上运行的事务中的各语句请求共享锁,而不是行版本控制。

  • REPEATABLE READ指定语句不能读取已由其它事务修改但尚未提交的行,并且指定,其它任何事务都不能在当前事务完成之前修改事务读取的数据。

    对事务中的每个语句所读取的全部数据都设置了共享锁,并且该共享锁一直保持到事务完成为止。 这样可以防止其他事务修改当前事务读取的任何行。 其他事务可以插入与当前事务所发出语句的搜索条件相匹配的新行。 如果当前事务随后重试执行该语句,它会检索新行,从而产生虚拟读取(幻读)。 由于共享锁一直保持到事务结束,而不是在每个语句结束时释放,因此并发级别低于默认的 READ COMMITTED 隔离级别。 此选项只在必要时使用。

  • SNAPSHOT:指定事务中任何语句读取的数据都将是在事务开始时便存在的数据的事务上一致的版本

    事务只能识别在其开始之前提交的数据修改。 在当前事务中执行的语句将看不到在当前事务开始以后由其他事务所做的数据修改。 其效果就好像事务中的语句获得了已提交数据的快照,因为该数据在事务开始时就存在。

    除非正在恢复数据库,否则 SNAPSHOT 事务不会在读取数据时请求锁。 读取数据的 SNAPSHOT 事务不会阻止其他事务写入数据。 写入数据的事务也不会阻止 SNAPSHOT 事务读取数据。

    在数据库恢复的回滚阶段,如果尝试读取由其他正在回滚的事务锁定的数据,则 SNAPSHOT 事务将请求一个锁。 在事务完成回滚之前,SNAPSHOT 事务会一直被阻塞。 当事务取得授权之后,便会立即释放锁。

    必须将 ALLOW_SNAPSHOT_ISOLATION 数据库选项设置为 ON,才能开始一个使用 SNAPSHOT 隔离级别的事务。 如果使用 SNAPSHOT 隔离级别的事务访问多个数据库中的数据,则必须在每个数据库中将 ALLOW_SNAPSHOT_ISOLATION 都设置为 ON。

    不能将通过其他隔离级别开始的事务设置为 SNAPSHOT 隔离级别,否则将导致事务中止。 如果一个事务在 SNAPSHOT 隔离级别开始,则可以将它更改为另一个隔离级别,然后再返回 SNAPSHOT。 事务在第一次访问数据时启动。

    在 SNAPSHOT 隔离级别下运行的事务可以查看由该事务所做的更改。 例如,如果事务对表执行 UPDATE,然后对同一个表发出 SELECT 语句,则修改后的数据将包含在结果集中。

    在快照隔离模式下,事务中任何语句读取的 FILESTREAM 数据都将是在事务开始(而非语句开始)时便存在的数据的事务性一致版本。

  • SERIALIZABLE:

    请指定下列内容:

    • 语句不能读取已由其他事务修改但尚未提交的数据(避免脏读)。
    • 任何其他事务都不能在当前事务完成之前修改由当前事务读取的数据(避免不可重复读)。
    • 在当前事务完成之前,其他事务不能使用当前事务中任何语句读取的键值插入新行(避免幻读)。

    范围锁处于与事务中执行的每个语句的搜索条件相匹配的键值范围之内。 这样可以阻止其他事务更新或插入任何行,从而限定当前事务所执行的任何语句。 这意味着如果再次执行事务中的任何语句,则这些语句便会读取同一组行。 在事务完成之前将一直保持范围锁。 这是限制最多的隔离级别,因为它锁定了键的整个范围,并在事务完成之前一直保持范围锁。 因为并发级别较低,所以应只在必要时才使用该选项。 该选项的作用与在事务内所有 SELECT 语句中的所有表上设置 HOLDLOCK 相同。

在 SQL Server 中,可以使用下列任意一种方法,在保护事务不脏读未提交的数据修改的同时尽量减少锁定争用:

  • READ COMMITTED 隔离级别,并将 READ_COMMITTED_SNAPSHOT 数据库选项设置为 ON。
  • SNAPSHOT 隔离级别。

SQL Server 一次只能设置一个隔离级别选项,而且设置的选项将一直对那个连接始终有效,直到显式更改该选项为止。 事务中执行的所有读取操作都会在指定的隔离级别的规则下运行,除非语句的 FROM 子句中的表提示为表指定了其他锁定行为或版本控制行为。

事务隔离级别为可读操作定义的锁类型

事务隔离级别定义了可为读取操作获取的锁类型。 针对 READ COMMITTED 或 REPEATABLE READ 获取的共享锁通常为行锁,尽管当读取引用了页或表中大量的行时,行锁可以升级为页锁表锁。 如果某行在被读取之后由事务进行了修改,则该事务会获取一个用于保护该行的排他锁,并且该排他锁在事务完成之前将一直保持。 例如,如果 REPEATABLE READ 事务具有用于某行的共享锁,并且该事务随后修改了该行,则共享行锁便会转换为排他行锁。

在事务进行期间,可以随时将事务从一个隔离级别切换到另一个隔离级别,但有一种情况例外。 即在从任一隔离级别更改到 SNAPSHOT 隔离时,不能进行上述操作。 否则会导致事务失败并回滚。 但是,可以将在 SNAPSHOT 隔离中启动的事务更改为任何其他隔离级别。

将事务从一个隔离级别更改为另一个隔离级别之后,便会根据新级别的规则对更改后读取的资源执行保护。 在更改前读取的资源将继续按照以前级别的规则受到保护。 例如,如果某个事务从 READ COMMITTED 更改为 SERIALIZABLE,则在该事务结束前,更改后所获取的共享锁将一直处于保留状态。

如果在存储过程或触发器中发出 SET TRANSACTION ISOLATION LEVEL,则当对象返回控制时,隔离级别会重设为在调用对象时有效的级别。 例如,如果在批处理中设置 REPEATABLE READ,并且该批处理调用一个将隔离级别设置为 SERIALIZABLE 的存储过程,则当该存储过程将控制返回给该批处理时,隔离级别就会恢复为 REPEATABLE READ。

当用户并发尝试访问同一数据的时,SQL Server尝试用锁来隔离不一致的数据和使用隔离级别查询数据时控制一致性(数据该如何读取),说起锁就会联想到事务,事务是一个工作单元,包括查询/更新数据和数据定义。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值