数据库的锁
锁是数据库中的一个非常重要的概念它主要用于多用户环境下保证数据库完整性和一致性 我们知道多个用户能够同时操纵同一个数据库中的数据会发生数据不一致现象即如果没有锁定且多个用户同时访问一个数据库则当他们的事务同时使用相同的数据时可能会发生问题这些问题包括丢失更新髒读不可重复读和幻觉读
当两个或多个事务选择同一行然后基于最初选定的值更新该行时会发生丢失更新问题每个事务都不知道其它事务的存在最后的更新将重写由其它事务所做的更新这将导致数据丢失例如两个编辑人员制作了同一文档的电子复本每个编辑人员独立地更改其复本然后保存更改后的复本这样就覆盖了原始文档最后保存其更改复本的编辑人员覆盖了第一个编辑人员所做的更改如果在第一个编辑人员完成之后第二个编辑人员才能进行更改则可以避免该问题
髒读就是指当一个事务正在访问数据并且对数据进行了修改而这种修改还没有提交到数据库中这时另外一个事务也访问这个数据然后使用了这个数据因为这个数据是还没有提交的数据那么另外一个事务读到的这个数据是髒数据依据髒数据所做的操作可能是不正确的例如一个编辑人员正在更改电子文档在更改过程中另一个编辑人员复制了该文档(该复本包含到目前为止所做的全部更改)并将其分发给预期的用户此后第一个编辑人员认为目前所做的更改是错误的于是删除了所做的编辑并保存了文档分发给用户的文档包含不再存在的编辑内容并且这些编辑内容应认为从未存在过如果在第一个编辑人员确定最终更改前任何人都不能读取更改的文档则可以避免该问题
不可重复读是指在一个事务内多次读同一数据在这个事务还没有结束时另外一个事务也访问该同一数据那么在第一个事务中的两次读数据之间由于第二个事务的修改那么第一个事务两次读到的的数据可能是不一样的这样就发生了在一个事务内两次读到的数据是不一样的因此称为是不可重复读例如一个编辑人员两次读取同一文档但在两次读取之间作者重写了该文档当编辑人员第二次读取文档时文档已更改原始读取不可重复如果只有在作者全部完成编写后编辑人员才可以读取文档则可以避免该问题
幻觉读是指当事务不是独立执行时发生的一种现象例如第一个事务对一个表中的数据进行了修改这种修改涉及到表中的全部数据行同时第二个事务也修改这个表中的数据这种修改是向表中插入一行新数据那么以后就会发生操作第一个事务的用户发现表中还有没有修改的数据行就好象发生了幻觉一样例如一个编辑人员更改作者提交的文档但当生产部门将其更改内容合并到该文档的主复本时发现作者已将未编辑的新材料添加到该文档中如果在编辑人员和生产部门完成对原始文档的处理之前任何人都不能将新材料添加到文档中则可以避免该问题
所以处理多用户并发访问的方法是加锁锁是防止其他事务访问指定的资源控制实现并发控制的一种主要手段当一个用户锁住数据库中的某个对象时其他用户就不能再访问该对象加锁对并发访问的影响体现在锁的粒度上为了控制锁定的资源应该首先了解系统的空间管理在SQL Server 系统中最小的空间管理单位是页一个页有K所有的数据日志索引都存放在页上另外使用页有一个限制这就是表中的一行数据必须在同一个页上不能跨页页上面的空间管理单位是盘区一个盘区是个连续的页表和索引的最小占用单位是盘区数据库是由一个或者多个表或者索引组成即是由多个盘区组成放在一个表上的锁限制对整个表的并发访问;放在盘区上的锁限制了对整个盘区的访问;放在数据页上的锁限制了对整个数据页的访问;放在行上的锁只限制对该行的并发访问
SQL Server 的锁机制
SQL Server 具有多粒度锁定允许一个事务锁定不同类型的的资源为了使锁定的成本减至最少SQL Server 自动将资源锁定在适合任务的级别锁定在较小的粒度(例如行)可以增加并发但需要较大的开销因为如果锁定了许多行则需要控制更多的锁锁定在较大的粒度(例如表)就并发而言是相当昂贵的因为锁定整个表限制了其它事务对表中任意部分进行访问但要求的开销较低因为需要维护的锁较少SQL Server 可以锁定行页扩展盘区表库等资源
行是可以锁定的最小空间 行级锁占用的数据资源最少所以在事务的处理过程中允许其他事务继续操纵同一个表或者同一个页的其他数据大大降低了其他事务等待处理的时间提高了系统的并发性
页级锁是指在事务的操纵过程中无论事务处理数据的多少每一次都锁定一页在这个页上的数据不能被其他事务操纵在SQL Server 以前使用的是页级锁页级锁锁定的资源比行级锁锁定的数据资源多在页级锁中即使是一个事务只操纵页上的一行数据那么该页上的其他数据行也不能被其他事务使用因此当使用页级锁时会出现数据的浪费现象也就是说在同一个页上会出现数据被占用却没有使用的现象在这种现象中数据的浪费最多不超过一个页上的数据行
表级锁也是一个非常重要的锁表级锁是指事务在操纵某一个表的数据时锁定了这个数据所在的整个表其他事务不能访问该表中的其他数据当事务处理的数据量比较大时一般使用表级锁表级锁的特点是使用比较少的系统资源但是却占用比较多的数据资源与行级锁和页级锁相比表级锁占用的系统资源例如内存比较少但是占用的数据资源却是最大在表级锁时有可能出现数据的大量浪费现象因为表级锁锁定整个表那么其他的事务都不能操纵表中的其他数据
盘区锁是一种特殊类型的锁只能用在一些特殊的情况下簇级锁就是指事务占用一个盘区这个盘区不能同时被其他事务占用例如在创建数据库和创建表时系统分配物理空间时使用这种类型的锁系统是按照盘区分配空间的当系统分配空间时使用盘区锁防止其他事务同时使用同一个盘区当系统完成分配空间之后就不再使用这种类型的盘区锁特别是当涉及到对数据操作的事务时不使用盘区锁
数据库级锁是指锁定整个数据库防止任何用户或者事务对锁定的数据库进行访问数据库级锁是一种非常特殊的锁它只是用于数据库的恢复操作过程中这种等级的锁是一种最高等级的锁因为它控制整个数据库的操作只要对数据库进行恢复操作那么就需要设置数据库为单用户模式这样系统就能防止其他用户对该数据库进行各种操作
行级锁是一种最优锁因为行级锁不可能出现数据既被占用又没有使用的浪费现象但是如果用户事务中频繁对某个表中的多条记录操作将导致对该表的许多记录行都加上了行级锁数据库系统中锁的数目会急剧增加这样就加重了系统负荷影响系统性能因此在SQL Server中还支持锁升级(lock escalation)所谓锁升级是指调整锁的粒度将多个低粒度的锁替换成少数的更高粒度的锁以此来降低系统负荷在SQL Server中当一个事务中的锁较多达到锁升级门限时系统自动将行级锁和页面锁升级为表级锁特别值得注意的是在SQL Server中锁的升级门限以及锁升级是由系统自动来确定的不需要用户设置
SQL Server 的锁模式
在SQLServer数据库中加锁时除了可以对不同的资源加锁还可以使用不同程度的加锁方式即锁有多种模式SQL Server中锁模式包括
共享锁 SQL Server中共享锁用于所有的只读数据操作共享锁是非独占的允许多个并发事务读取其锁定的资源默认情况下数据被读取后SQL Server立即释放共享锁例如执行查询“SELECT*FROMAUTHORS”时首先锁定第一页读取之后释放对第一页的锁定然后锁定第二页这样就允许在读操作过程中修改未被锁定的第一页但是事务隔离级别连接选项设置和SELECT语句中的锁定设置都可以改变SQL Server的这种默认设置例如“ SELECT*FROMAUTHORSHOLDLOCK”就要求在整个查询过程中保持对表的锁定直到查询完成才释放锁定
更新锁 更新锁在修改操作的初始化阶段用来锁定可能要被修改的资源这样可以避免使用共享锁造成的死锁现象因为使用共享锁时修改数据的操作分为两步首先获得一个共享锁读取数据然后将共享锁升级为排它锁然后再执行修改操作这样如果同时有两个或多个事务同时对一个事务申请了共享锁在修改数据的时候这些事务都要将共享锁升级为排它锁这时这些事务都不会释放共享锁而是一直等待对方释放这样就造成了死锁如果一个数据在修改前直接申请更新锁在数据修改的时候再升级为排它锁就可以避免死锁
排它锁 排它锁是为修改数据而保留的它所锁定的资源其他事务不能读取也不能修改
结构锁 执行表的数据定义语言 (DDL) 操作(例如添加列或除去表)时使用架构修改 (SchM) 锁当编译查询时使用架构稳定性 (SchS) 锁架构稳定性 (SchS) 锁不阻塞任何事务锁包括排它锁因此在编译查询时其它事务(包括在表上有排它锁的事务)都能继续运行但不能在表上执行 DDL 操作