论文部分内容阅读
摘要:本文主要介绍了手工锁的常见类型, NOLOCK、 HOLDLOCK 、UPDLOCK、 TABLOCKX并举例说明它们在实际应用中的具体使用。
关键词:手工锁;SQL;应用
中图分类号:TP311文献标识码:A 文章编号:1009-3044(2008)36-2828-02
Lock Table Applied in SQL SERVER
TONG Li-ting
(Heilongjiang Agricultural Economy professional College, Mudanjiang 157041,China)
Abstract: The article introduces the general types of lockable such as NOLOCK、 HOLDLOCK 、UPDLOCK、 TABLOCKX and illustrates the practical application by setting examples.
Key words: Lock Table; SQL; Application
1 前言
锁是网络数据库中的一个非常重要的概念,它主要用于多用户环境下保证数据库完整性和一致性。SQL Server作为一种中小型数据库管理系统,该系统更强调由系统来管理锁。能够自动在满足锁定条件和系统性能之间为数据库加上适当的锁,同时系统在运行期间常常自动进行优化处理,实行动态加锁。
在实际应用中,有时为了应用程序正确运行和保持数据的一致性,必须人为地给数据库的某个表加锁。比如,在某应用程序的一个事务操作中,需要根据一编号对几个数据表做统计操作,为保证统计数据时间的一致性和正确性,从统计第一个表开始到全部表结束,其他应用程序或事务不能再对这几个表写入数据,这个时候,该应用程序希望在从统计第一个数据表开始或在整个事务开始时能够由程序人为地(显式地)锁定这几个表,这就需要用到手工加锁。
2 手工加锁的类型
在SQL Server 的SQL语句(SELECT、INSERT、DELETE、UPDATE)支持手工加锁。所指定的锁类型有如下几种:
HOLDLOCK: 在该表上保持共享锁,直到整个事务结束,而不是在语句执行完立即释放。
NOLOCK:不添加共享锁和排它锁,当这个选项生效后,可能读到未提交读的数据或“。
PAGLOCK:指定添加页面锁(否则通常可能添加表锁)。
READCOMMITTED:设置事务为读提交隔离性级别。
READPAST: 跳过已经加锁的数据行,这个选项将使事务读取数据时跳过那些已经被其
他事务锁定的数据行,而不是阻塞直到其他事务释放锁,READPAST仅仅应用于READ COMMITTED
READUNCOMMITTED:等同于NOLOCK。
REPEATABLEREAD:设置事务为可重复读隔离性级别。
ROWLOCK:指定使用行级锁。
SERIALIZABLE:设置事务为可串行的隔离性级别。
TABLOCK:指定使用表级锁,而不是使用行级或页面级的锁,SQL Server在该语句执行完后释放这个锁,而如果同时指定了HOLDLOCK,该锁一直保持到这个事务结束。
TABLOCKX:指定在表上使用排它锁,这个锁可以阻止其他事务读或更新这个表的数据
UPDLOCK :指定在读表中数据时设置修改锁(update lock)而不是设置共享锁,该锁一直保持到这个语句或整个事务结束,使用UPDLOCK的作用是允许用户先读取数据(而且不阻塞其他用户读数据),并且保证在后来再更新数据时,这一段时间内这些数据没有被其他程序更改。
3 常用手工锁应用举例
3.1 NOLOCK
仅在SELECT语句中可以使用。执行对应的T-SQL语句时。不发出共享锁,也不使用派它锁。当此项生效时,可能回读取未提交的事务或一组在读取中间回滚的页面,即有可能发生脏读。
例如
连接1代码如下:
BEGIN TRAN
Select * From Table(NOLOCK)
COMMIT TRAN
连接2代码如下:
BEGIN TRAN
Update table1 WITH(Tablock)
SET FieldB=’UserD’
WHERE FieldA=’5’
WAITFOR DELAY ‘0:00:5’--等待5秒
COMMIT TRAN
此示例中,同时执行数据库的两个连接中的代码。由于在连接1中使用了NOLOCK锁,而使其不受连接2中一直保持的排它锁的限制,可以立即执行,并且读取到了连接2中未提交的修改。但如果去掉NOLOCK锁,则;连接1中的事务将受到连接2中事务的表级排它锁的限制,必须等到连接2执行完毕才能够执行。
3.2 HOLDLOCK
将共享锁保留到事务完成,而不是在相应的表、行或数据面不再需要时就立即释放锁。
例如:
BEGIN TRAN
SELECT * From Tablel (HOLDLOCK)
WAITFOR DELAY ‘0:00:5’
COMMIT TRAN
这将使共享锁一直保持到事务结束。
3.3 UPDLOCK
读取表时使用修改锁,而不使用共享锁,并将锁一直保留到语句或事务的结束。UPDLOCK的优点是允许读取数据(不但塞其他事务)并在以后更新数据,同时确保自从赏赐读取数据后数据没有被更改。
例如
BEGIM TRAN
SELECT * From Table1 (UpdLock)
WAITFOR DELAY ‘0:00:5’
SELECT * FROM Table 1
COMMIT TRAN
连接2中的代码如下:
BEGIN TRAN
Update table 1
SET FieldB=’UserD’
WHERE FieldA=’5’
COMMIT TRAN
此示例中,同时执行数据库的两个连接中的代码,由于连接1中的SELECT语句使用了修改锁,所以前后量词读取操作得到的结果是一致的。如果将修改的锁去掉,则会发生非重复读的问题。
3.4 TABLOCKX
使用表的排它锁。该锁可以防止其他事务读取或更新表,并在语句或事务结束前一直持有。
例如:在并发的环境下,两个用户同时保存数据到一张表中,代码如下:
cn.Begin Trans
cn.Execute “INSERT INTO table1 ……”
Set rs=cn.Execute(“SELECT COUNT(*)FROM table1 WHERE……”)
If rs.RecordCount>0 Then
‘表A的字段A不能重复
cn.RollbackTrans
Else
cn.CommitTrans
End If
下面分析一下上面程序的執行过程。
在执行INSERT命令时如果不添加任何参数,数据库默认申请一个IX锁给表A 。当第一个用户执行INSERT INTO 语句时,连接向数据库申请了一个IX锁给表A。同时第二个用户执行INSERT INTO语句,连接也向数据库成功地申请了一个IX锁给表A。但是当执行SELECT COUNT(*)一句的时候就会有问题产生:假设第一个用户先一步执行,由于SELECT命令需要向数据库申请一个S锁给表A,而这时表A已经存在一个IX并且属于另外一个连接。因此它只好在此等候,紧接着第二个用户也执行SELECT COUNT(*)一句,它也会向数据库申请一个S锁给表A,这时数据就会自动结束较晚申请IX锁的连接同时回滚这个事务,这样,对于应用程序要实现的目标来说。就是一个失败。
针对一个例子中的问题,可以在INSERT命令中带上参数with(tablock)来解决。
cn.Bengin Trans
cn.Execute “INSERT INTO tableA with (tablock)……”
Set rs =cn.Execute(“SELECTCOUNT(*)FROM tableA WHERE ……”)
If rs.RecordCount>0 Then
‘表A的字段A不能重复
cn.CommitTrans
End if
4 总结
由上可见,在SQL Server中可以灵活多样地为SQL语句显式加锁,若适当使用,我们完全可以完成一些程序的特殊要求,保证数据的一致性和完整性。对于一般使用者而言,了解锁机制并不意味着必须使用它。事实上,SQL Server建议让系统自动管理数据库中的锁,而且一些关于锁的设置选项也没有提供给用户和数据库管理人员,对于特殊用户,通过给数据库中的资源显式加锁,可以满足很高的数据一致性和可靠性要求,只是需要特别注意避免死锁现象的出现。
关键词:手工锁;SQL;应用
中图分类号:TP311文献标识码:A 文章编号:1009-3044(2008)36-2828-02
Lock Table Applied in SQL SERVER
TONG Li-ting
(Heilongjiang Agricultural Economy professional College, Mudanjiang 157041,China)
Abstract: The article introduces the general types of lockable such as NOLOCK、 HOLDLOCK 、UPDLOCK、 TABLOCKX and illustrates the practical application by setting examples.
Key words: Lock Table; SQL; Application
1 前言
锁是网络数据库中的一个非常重要的概念,它主要用于多用户环境下保证数据库完整性和一致性。SQL Server作为一种中小型数据库管理系统,该系统更强调由系统来管理锁。能够自动在满足锁定条件和系统性能之间为数据库加上适当的锁,同时系统在运行期间常常自动进行优化处理,实行动态加锁。
在实际应用中,有时为了应用程序正确运行和保持数据的一致性,必须人为地给数据库的某个表加锁。比如,在某应用程序的一个事务操作中,需要根据一编号对几个数据表做统计操作,为保证统计数据时间的一致性和正确性,从统计第一个表开始到全部表结束,其他应用程序或事务不能再对这几个表写入数据,这个时候,该应用程序希望在从统计第一个数据表开始或在整个事务开始时能够由程序人为地(显式地)锁定这几个表,这就需要用到手工加锁。
2 手工加锁的类型
在SQL Server 的SQL语句(SELECT、INSERT、DELETE、UPDATE)支持手工加锁。所指定的锁类型有如下几种:
HOLDLOCK: 在该表上保持共享锁,直到整个事务结束,而不是在语句执行完立即释放。
NOLOCK:不添加共享锁和排它锁,当这个选项生效后,可能读到未提交读的数据或“。
PAGLOCK:指定添加页面锁(否则通常可能添加表锁)。
READCOMMITTED:设置事务为读提交隔离性级别。
READPAST: 跳过已经加锁的数据行,这个选项将使事务读取数据时跳过那些已经被其
他事务锁定的数据行,而不是阻塞直到其他事务释放锁,READPAST仅仅应用于READ COMMITTED
READUNCOMMITTED:等同于NOLOCK。
REPEATABLEREAD:设置事务为可重复读隔离性级别。
ROWLOCK:指定使用行级锁。
SERIALIZABLE:设置事务为可串行的隔离性级别。
TABLOCK:指定使用表级锁,而不是使用行级或页面级的锁,SQL Server在该语句执行完后释放这个锁,而如果同时指定了HOLDLOCK,该锁一直保持到这个事务结束。
TABLOCKX:指定在表上使用排它锁,这个锁可以阻止其他事务读或更新这个表的数据
UPDLOCK :指定在读表中数据时设置修改锁(update lock)而不是设置共享锁,该锁一直保持到这个语句或整个事务结束,使用UPDLOCK的作用是允许用户先读取数据(而且不阻塞其他用户读数据),并且保证在后来再更新数据时,这一段时间内这些数据没有被其他程序更改。
3 常用手工锁应用举例
3.1 NOLOCK
仅在SELECT语句中可以使用。执行对应的T-SQL语句时。不发出共享锁,也不使用派它锁。当此项生效时,可能回读取未提交的事务或一组在读取中间回滚的页面,即有可能发生脏读。
例如
连接1代码如下:
BEGIN TRAN
Select * From Table(NOLOCK)
COMMIT TRAN
连接2代码如下:
BEGIN TRAN
Update table1 WITH(Tablock)
SET FieldB=’UserD’
WHERE FieldA=’5’
WAITFOR DELAY ‘0:00:5’--等待5秒
COMMIT TRAN
此示例中,同时执行数据库的两个连接中的代码。由于在连接1中使用了NOLOCK锁,而使其不受连接2中一直保持的排它锁的限制,可以立即执行,并且读取到了连接2中未提交的修改。但如果去掉NOLOCK锁,则;连接1中的事务将受到连接2中事务的表级排它锁的限制,必须等到连接2执行完毕才能够执行。
3.2 HOLDLOCK
将共享锁保留到事务完成,而不是在相应的表、行或数据面不再需要时就立即释放锁。
例如:
BEGIN TRAN
SELECT * From Tablel (HOLDLOCK)
WAITFOR DELAY ‘0:00:5’
COMMIT TRAN
这将使共享锁一直保持到事务结束。
3.3 UPDLOCK
读取表时使用修改锁,而不使用共享锁,并将锁一直保留到语句或事务的结束。UPDLOCK的优点是允许读取数据(不但塞其他事务)并在以后更新数据,同时确保自从赏赐读取数据后数据没有被更改。
例如
BEGIM TRAN
SELECT * From Table1 (UpdLock)
WAITFOR DELAY ‘0:00:5’
SELECT * FROM Table 1
COMMIT TRAN
连接2中的代码如下:
BEGIN TRAN
Update table 1
SET FieldB=’UserD’
WHERE FieldA=’5’
COMMIT TRAN
此示例中,同时执行数据库的两个连接中的代码,由于连接1中的SELECT语句使用了修改锁,所以前后量词读取操作得到的结果是一致的。如果将修改的锁去掉,则会发生非重复读的问题。
3.4 TABLOCKX
使用表的排它锁。该锁可以防止其他事务读取或更新表,并在语句或事务结束前一直持有。
例如:在并发的环境下,两个用户同时保存数据到一张表中,代码如下:
cn.Begin Trans
cn.Execute “INSERT INTO table1 ……”
Set rs=cn.Execute(“SELECT COUNT(*)FROM table1 WHERE……”)
If rs.RecordCount>0 Then
‘表A的字段A不能重复
cn.RollbackTrans
Else
cn.CommitTrans
End If
下面分析一下上面程序的執行过程。
在执行INSERT命令时如果不添加任何参数,数据库默认申请一个IX锁给表A 。当第一个用户执行INSERT INTO 语句时,连接向数据库申请了一个IX锁给表A。同时第二个用户执行INSERT INTO语句,连接也向数据库成功地申请了一个IX锁给表A。但是当执行SELECT COUNT(*)一句的时候就会有问题产生:假设第一个用户先一步执行,由于SELECT命令需要向数据库申请一个S锁给表A,而这时表A已经存在一个IX并且属于另外一个连接。因此它只好在此等候,紧接着第二个用户也执行SELECT COUNT(*)一句,它也会向数据库申请一个S锁给表A,这时数据就会自动结束较晚申请IX锁的连接同时回滚这个事务,这样,对于应用程序要实现的目标来说。就是一个失败。
针对一个例子中的问题,可以在INSERT命令中带上参数with(tablock)来解决。
cn.Bengin Trans
cn.Execute “INSERT INTO tableA with (tablock)……”
Set rs =cn.Execute(“SELECTCOUNT(*)FROM tableA WHERE ……”)
If rs.RecordCount>0 Then
‘表A的字段A不能重复
cn.CommitTrans
End if
4 总结
由上可见,在SQL Server中可以灵活多样地为SQL语句显式加锁,若适当使用,我们完全可以完成一些程序的特殊要求,保证数据的一致性和完整性。对于一般使用者而言,了解锁机制并不意味着必须使用它。事实上,SQL Server建议让系统自动管理数据库中的锁,而且一些关于锁的设置选项也没有提供给用户和数据库管理人员,对于特殊用户,通过给数据库中的资源显式加锁,可以满足很高的数据一致性和可靠性要求,只是需要特别注意避免死锁现象的出现。