RDBMS能在事务中维护数据的完整性,这是通过数据库对象实现的多种机制来实现的,下面列出的是4个最重要的对象:
- 锁
- 约束
- 键
- 索引
在SQL Server中,锁可以使多个用户同时访问,同一数据,并且保证在读取数据时,数据不会被修改。同时,锁也用来确保一个进程在修改数据时,不和其他进行数据修改操作或者数据读取操作的进程发生冲突。
SQL Server以连接为单位对锁进行管理,这就是说,一个锁不能被多个连接同时持有;SQL Server也以事务为单位对锁进行管理,和多个连接不能同时持有同一个锁一样,多个事务也不能同时持有,同一个锁。
比如,假如一个应用程序打开了一个SQL Server连接,这个连接在某个表上,被赋予了共享锁,这个应用程序就不能再打开一个可以修改该表数据的新连接。对事物也是一样。如果一个应用程序启动了一个用于修改特定数据的事务,那么在这个事务的工作完成之前,其他任何事物,都不能修改这些数据。即使多个事务共享相同的连接,上述情况也成立。
SQL Server使用6种类型的锁,更准确的说,是6种资源锁模式:
- 共享
- 更新
- 排他
- 意向
- 模式
- 批量更新
共享锁,更新锁,排他锁和意向锁可用于表或索引的行、页(表,或者索引所用的8KB的存储页面)、扩展(64KB的8个连续的索引或者表页面)、表,数据库。
模式锁与批量更新锁适用于表。
共享锁
共享锁允许多个连接和事务同时读取所分配的共同资源。只要给连接和事务授予了共享锁,任何其他连接或事务就不能修改数据。当一个应用程序成功的读取了数据之后,共享锁通常会被释放,但在某些特殊情况下,这个动作是可以修改的。例如:给整个事务分配了共享锁,保证事务基于的数据,在该事务完成之前不被修改,从而最大限度的确保数据的一致性。这个扩展锁,可用于事务一致性必须100%保证的情况,但持有锁的代价是降低了数据的并发访问,例如,要从储蓄账户中取100美元,在包含储蓄账户的余额上放置一个共享锁。这些数据用于确保有足够的资金支持这个取款操作。最好禁止其他连接修改该余额,直到取款操作完成为止。 由于共享锁之间是互相兼容的,因此,不同的事务和连接在读取相同的数据时,不会发生冲突。
更新锁
SQL Server使用更新锁,在防止出现死锁的情况。出现死锁是很糟糕的,通常死锁是由于拙劣的编程技术造成的。当两个进程争夺同一个资源时,就将发生死锁。回到前面银行的例子:在这个假定的银行事务中,我妻子和我同时上线,将储蓄账户中的资金转帐到支票账户上。碰巧我们同时要进行转账,于是两个进程分别启动并执行了转账操作。当我的进程访问这两个账户时,进程在资源上发出了共享锁。到目前为止一切还算正常,但是当我们的进程试图修改资源时,混淆便随之而来了。首先,我妻子的进程尝试将共享锁升格为排他锁,以便对数据进行修改。几乎同时,我的进程也尝试进行相同的升格操作。然而,我们所共有的共享锁阻止了任何一个进程,实现升格到排他锁的企图。由于没有一个进程愿意释放其,共享锁,就发生了死锁。
SQL Server不会对死锁现象特别关照,假如死锁发生了,SQL Server就会自动选择其中的一个进程,将它作为牺牲品而停止。SQL Server选择与其关联代价最小的进程,停止它,回滚相关事务,然后向相关应用程序中返回错误代码1205.如果用户正确的捕捉到了错误,就会得到这样的消息:“事务##在X资源上与其他进程,出现了死锁,并被选择为死锁的牺牲品,重新运行事务”。
为了防止死锁发生,SQL Server通常使用更新锁来代替共享锁。只有一个进程可以得到更新锁,这样可以防止与其相对的进程升格其拥有的锁,其底线是,如果以更新为唯一目的而进行了读操作,则SQL Server可以发出一个更新锁,而不是共享锁,以便避免潜在的死锁危险。SQL提供了防止死锁的逻辑,只要细心的规划与实现,就可以避免死锁的产生。
排他锁
在执行修改操作时,SQL Server通常发出排他锁。在更改一行汇总的某个字段值的时候,SQL Server授予相关进程访问此行的排他访问权限。这样的排他访问,可以防止任何并发事务或者连接的相关进程,对处于修改中的数据,进行读、更新、删除操作。排他锁与任何其他类型的锁都不兼容。
意向锁
为了防止任何一个并发事务,或者连接中的进程,在一个已经被其他进程锁住的资源上,放置排他锁,SQL Server设计了意向锁。比如,执行一个事务,在表中更新单个行,SQL Server在该行上授予此事务排他锁的同时,也在包含此行的表上授予此事务意向锁。这样就能防止其他进程在该表中放置排他锁。
举一个现实中的例子:它可以解释SQL 编程中意向锁的行为:用户住进了SQL旅馆的404房间,现在具有使用4楼的房间4的唯一(排他)权限。旅馆的其他主顾都不允许进入此房间。而且没有主顾可以将旅馆的所有房间都预定下来,因为404房间已经被用户单独占用。对于旅馆,用户就有了意向锁;而对于404房间,用户有排他锁。意向锁,与其他比它低级的锁,都是兼容的。
批处理更新锁
表上的批处理更新锁允许多个批处理加载线程,把数据加载到表中,同时禁止其他类型的数据访问。在表上启用表锁定时,或者用批处理操作选择表锁定选项时,就发出了批处理更新锁。
键范围锁
在使用可串行化的隔离级别时,键范围锁保护结果集中隐含的一个行范围,不被T-SQL语句读取。可串行化的隔离级别要求,在事务中每次查询时,都必须获得相同的行集。键范围锁,禁止其他事务插入其键值(由可串行化的事务读取)在指定的范围内的新行,来满足这个要求。