RT

解决方案 »

  1.   

    数据库加锁的知识收藏
    关于数据库加锁的知识
    LockType 属性
    指示编辑过程中对记录使用的锁定类型。
    设置和返回值
    设置或返回以下某个LockTypeEnum 的值。
    常量说明
    adLockReadOnly 默认值,只读。无法更改数据。
    adLockPessimistic 保守式记录锁定(逐条)。提供者执行必要的操作确保成功编辑记录,通
    常采用编辑时立即锁定数据源的记录的方式。
    adLockOptimistic 开放式记录锁定(逐条)。提供者使用开放式锁定,只在调用Update 方
    法时锁定记录。
    adLockBatchOptimistic 开放式批更新。用于与立即更新模式相反的批更新模式。
    说明
    打开Recordset 前设置LockType 属性可指定打开时提供者应该使用的锁定类型。读取该属
    性可返回在打开的Recordset 对象上正在使用的锁定类型。Recordset 关闭时LockType 属
    性为读/写,打开时该属性为只读。
    提供者可能不支持所有的锁定类型。如果某提供者不支持所需的LockType 设置,则将替换
    为其他类型的锁定。要确定Recordset 对象可用的实际锁定功能,请通过adUpdate 和
    adUpdateBatch 使用Supports 方法。
    如果CursorLocation 属性被设置为adUseClient,将不支持adLockPessimistic 设置。设置
    不支持的值不会产生错误,因为此时将使用支持的最接近的LockType 的值。
    远程数据服务用法当在客户端(ADOR) 的Recordset 对象上使用时,LockType 属性只
    能设置为adLockOptimisticBatch。
    ------------------------------------------------------------------------------------
    /********** 加锁***************
    设table1(A,B,C)
    A B C
    a1 b1 c1
    a2 b2 c2
    a3 b3 c3
    1)排它锁
    新建两个连接
    在第一个连接中执行以下语句
    begin tran
    update table1
    set A=’aa’
    where B=’b2’
    waitfor delay ’00:00:30 ’ --等待30 秒
    commit tran
    在第二个连接中执行以下语句
    begin tran
    select * from table1
    where B=’b2’
    commit tran
    若同时执行上述两个语句,则select 查询必须等待update 执行完毕才能执行即要等待30 秒
    2)共享锁
    在第一个连接中执行以下语句
    begin tran
    select * from table1 holdlock -holdlock 人为加锁
    where B=’b2’
    waitfor delay ’00:00:30 ’ --等待30 秒
    commit tran
    在第二个连接中执行以下语句
    begin tran
    select A,C from table1
    where B=’b2’
    update table1
    set A=’aa’
    where B=’b2’
    commit tran
    若同时执行上述两个语句,则第二个连接中的select 查询可以执行
    而update 必须等待第一个连接中的共享锁结束后才能执行即要等待30 秒
    3)死锁
    增设table2(D,E)
    D E
    d1 e1
    d2 e2
    在第一个连接中执行以下语句
    begin tran
    update table1
    set A=’aa’
    where B=’b2’
    waitfor delay ’00:00:30 ’
    update table2
    set D=’d5’
    where E=’e1’
    commit tran
    在第二个连接中执行以下语句
    begin tran
    update table2
    set D=’d5’
    where E=’e1’
    waitfor delay ’00:00:10 ’
    update table1
    set A=’aa’
    where B=’b2’
    commit tran
    同时执行,系统会检测出死锁,并中止进程
    SET IMPLICIT_TRANSACTIONS ON --用户每次必须显式提交或回滚。否则当用户断开连
    接时,
    --事务及其所包含的所有数据更改将回滚
    SET IMPLICIT_TRANSACTIONS OFF --自动提交模式。在自动提交模式下,如果各个语
    句成功
    --完成则提交。
    --------------------------------------------------------------------------------------
    修改前
    RS.lock
    完成后
    RS.unlock
    --------------------------------------------------------------------------------------
    Sql 中游标和加锁的问题
    日期:2000-11-27 15:42:00
    出处:ALL ASP
    作者:feny
    <%
    ’游标类型
    Const adOpenForwardOnly = 0
    Const adOpenKeyset = 1
    Const adOpenDynamic = 2
    Const adOpenStatic = 3
    ’加锁类型
    Const adLockReadOnly = 1
    Const adLockPessimistic = 2
    Const adLockOptimistic = 3
    Const adLockBatchOptimistic = 4
    >%
    <% set conn = server.createobject(’adodb.connection’) >%
    <% set rsmov = server.createobject(’adodb.recordset’) >%
    <% conn.open ’数据源名称’, ’sa’, ’ >%
    <% rsmov.open sqlmov, conn, adopenkeyset, adlockreadonly >%
    游标使用时是比较灵活的,它有时用来描述一个记录集,有时又是用来描述当前记录集
    中某一条记录的指针。游标主要是用来建立一个关系数据库中行/列关系的一种SQL 可利用
    的访问格式。与游标有关系的技术术语还有一个叫Book 的。如果你选择的游标方式支
    持Books。数据库将提供有关记录数目的强大功能。
    在上面写出的那么多游标方式中,adOpenDynamic 是没有太的用处的,虽然它提供实时
    显示数据库中的记录的所有更新操作的功能,但是因为并不是所有的数据库都支持该游标方
    式,没有移植性的游标方式对当前错综复杂的数据库来说真是用处不大。
    在实际的编程中,我相信大家使用得最频繁的是adOpenStatic 方式,当然这种方式的缺
    点是不能够就、实时反应出数据库中内容改变时的状况。如果要想看到数据库被其它用户改
    变的状况,可使用adOpenKeyse 方式(但是它只能够反应出被编辑的改变情况,也就是说
    不能够反映出新增和删除记录的改变情况。)
    其实上面的内容大家一般都可以在微软的技术参考资料中找到,下面来说说在使用这些
    游标方式和加锁方式时要注意到的问题。
    首先要注意到的是这两种方式在混合使用时的问题,就是说你同时设置游标方式和加锁
    方式。除非你是在使用Access 数据库,一般而言当你混合使用时是并不能够得到你预期想
    要的游标方式和加锁方式的。例如,如果你同时将游标设置为adOpenStatic 方式,而将加锁
    设置为adLockOptimistic,你将得不到adOpenStatic 方式的游标,你这时使用的游标方式将
    是adOpenKeyset,也就是说你使用ADO 的话,它将返回adOpenKeyset 的游标。
    其次,游标和加锁的混合使用还会导致ADO 返回的不是你想要的加锁方式, ADO 会
    改变你的加锁方式。例如,在默认状态下游标方式是adOpenForwardOnly,在使用这种游标
    方式的同时如果你使用的加锁方式为-1 (就是让数据源来判断加锁方式) 或则
    adLockReadOnly,那么这种混合方式基本上不支持RecordSet 的任何方法,也就是说
    RecordSet 的任何方法将返回False(你的recordcount,absoultpage,addnew,delete,update 等都会
    返回-1,-1 就是表示不支持该属性),但是这时如果你使用的是adOpenForwardOnly 游标方
    式和其它的加锁方式混合,它反而会支持填加,删除和更新。
    -----------------------------------------------------------------------------------------------
    就启明星提出的在SQL Server 中使用加锁的问题,我就以前的经验和收集的一些资料简单
    的提出我自己的一些看法,不知道对启明星是否有所帮助:
    一般而言,下面是个典型的打开数据库的过程。
    <%
    ’游标类型
    Const adOpenForwardOnly = 0
    Const adOpenKeyset = 1
    Const adOpenDynamic = 2
    Const adOpenStatic = 3
    ’加锁类型
    Const adLockReadOnly = 1
    Const adLockPessimistic = 2
    Const adLockOptimistic = 3
    Const adLockBatchOptimistic = 4
    >%
    <% set conn = server.createobject(’adodb.connection’) >%
    <% set rsmov = server.createobject(’adodb.recordset’) >%
    <% conn.open ’soc’, ’’, ’’ >%
    <% rsmov.open sqlmov, conn, adopenkeyset, adlockreadonly >%
    游标使用时是比较灵活的,它有时用来描述一个记录集,有时又是用来描述当前记录集中某
    一条记录的指针。游标主要是用来建立一个关系数据库中行/列关系的一种SQL 可利用的访
    问格。与游标有关系的技术术语还有一个叫Book 的。如果你选择的游标方式支持
    Books。数据库将提供有关记录数目的强大功能。在上面写出的那么多游标方式中,
    adOpenDynamic 是没有太的用处的,虽然它提供实时显示数据库中的记录的所有更新操作的
    功能,但是因为并不是所有的数据库都支持该游标方式,没有移植性的游标方式对当前错综
    复杂的数据库来说真是用处不大。在实际的编程中,我相信大家使用得最频繁的是
    adOpenStatic 方式,当然这种方式的缺点是不能够就、实时反应出数据库中内容改变时的状
    况。如果要想看到数据库被其它用户改变的状况,可使用adOpenKeyse 方式(但是它只能
    够反应出被编辑的改变情况,也就是说不能够反映出新增和删除记录的改变情况。)
    其实上面的内容大家一般都可以在微软的技术参考资料中找到,下面来说说在使用这些游标
    方式和加锁方式时要注意到的问题。
    1。首先要注意到的是这两种方式在混合使用时的问题,就是说你同时设置游标方式和加锁
    方式。
    除非你是在使用Access 数据库,一般而言当你混合使用时是并不能够得到你预期想要的游
    标方式和加锁方式的。例如,如果你同时将游标设置为adOpenStatic 方式,而将加锁设置为
    adLockOptimistic,你将得不到adOpenStatic 方式的游标,你这时使用的游标方式将是
    adOpenKeyset,也就是说你使用ADO 的话,它将返回adOpenKeyset 的游标。
    2。其次,游标和加锁的混合使用还会导致ADO 返回的不是你想要的加锁方式,ADO 会改
    变你的加锁
    方式。例如,在默认状态下游标方式是adOpenForwardOnly,在使用这种游标方式的同时如

    你使用的加锁方式为-1(就是让数据源来判断加锁方式)或则adLockReadOnly,那么这种
    混合方式基本上不支持RecordSet 的任何方法,也就是说RecordSet 的任何方法将返回Fals e
    (你的recordcount,absoultpage,addnew,delete,update 等都会返回-1,-1 就是表示不支持该属性),
    但是这时如果你使用的是adOpenForwardOnly 游标方式和其它的加锁方式混合,它反而
    会支持填加,删除和更新。
    --------------------------------------------------------------------------------------------------------------
    SELECT 语句中“加锁选项”的功能说明
    SQL Server 提供了强大而完备的锁机制来帮助实现数据库系统的并发性和高性能。用户既能
    使用SQL Server 的缺省设置也可以在select 语句中使用“加锁选项”来实现预期的效果。本
    文介绍了SELECT 语句中的各项“加锁选项”以及相应的功能说明。
    功能说明:
    NOLOCK(不加锁)
    此选项被选中时,SQL Server 在读取或修改数据时不加任何锁。在这种情况下,用户有可
    能读取到未完成事务(Uncommited Transaction)或回滚(Roll Back)中的数据, 即所谓的“脏数
    据”。
    HOLDLOCK(保持锁)
    此选项被选中时,SQL Server 会将此共享锁保持至整个事务结束,而不会在途中释放。
    UPDLOCK(修改锁)
    此选项被选中时,SQL Server 在读取数据时使用修改锁来代替共享锁,并将此锁保持至整
    个事务或命令结束。使用此选项能够保证多个进程能同时读取数据但只有该进程能修改数
    据。
    TABLOCK(表锁)
    此选项被选中时,SQL Server 将在整个表上置共享锁直至该命令结束。这个选项保证其他
    进程只能读取而不能修改数据。
    PAGLOCK(页锁)
    此选项为默认选项, 当被选中时,SQL Server 使用共享页锁。
    TABLOCKX(排它表锁)
    此选项被选中时,SQL Server 将在整个表上置排它锁直至该命令或事务结束。这将防止其
    他进程读取或修改表中的数据。
    使用这些选项将使系统忽略原先在SET 语句设定的事务隔离级别( Transaction Isolation
    Level)。请查阅SQL Server 联机手册获取更多信息。
    -------------------------------------------------------------------------------------------------------------
    1 如何锁一个表的某一行
    A 连接中执行
    SET TRANSACTION ISOLATION LEVEL REPEATABLE READ
    begin tran
    select * from tablename with (rowlock) where id=3
    waitfor delay ’00:00:05 ’
    commit tran
    B 连接中如果执行
    update tablename set colname=’10’ where id=3 --则要等待5 秒
    update tablename set colname=’10’ where id<>3 --可立即执行
    2 锁定数据库的一个表
    SELECT * FROM table WITH (HOLDLOCK)
    注意: 锁定数据库的一个表的区别
    SELECT * FROM table WITH (HOLDLOCK)
    其他事务可以读取表,但不能更新删除
    SELECT * FROM table WITH (TABLOCKX)
    其他事务不能读取表,更新和删除
    select * from table with (..)
    SELECT 语句中“加锁选项”的功能说明
    SQL Server 提供了强大而完备的锁机制来帮助实现数据库系统的并发性和高性能。用户既
    能使用SQL Server 的缺省设置也可以在select 语句中使用“加锁选项”来实现预期的效果。
    本文介绍了SELECT 语句中的各项“加锁选项”以及相应的功能说明。
    功能说明:
    NOLOCK(不加锁)
    此选项被选中时,SQL Server 在读取或修改数据时不加任何锁。在这种情况下,用户有
    可能读取到未完成事务(Uncommited Transaction)或回滚(Roll Back)中的数据, 即所谓的“脏
    数据”。
    HOLDLOCK(保持锁)
    此选项被选中时,SQL Server 会将此共享锁保持至整个事务结束,而不会在途中释放。
    UPDLOCK(修改锁)
    此选项被选中时,SQL Server 在读取数据时使用修改锁来代替共享锁,并将此锁保持至
    整个事务或命令结束。使用此选项能够保证多个进程能同时读取数据但只有该进程能修改数
    据。
    TABLOCK(表锁)
    此选项被选中时,SQL Server 将在整个表上置共享锁直至该命令结束。这个选项保证其
    他进程只能读取而不能修改数据。
    PAGLOCK(页锁)
    此选项为默认选项, 当被选中时,SQL Server 使用共享页锁。
    TABLOCKX(排它表锁)
    此选项被选中时,SQL Server 将在整个表上置排它锁直至该命令或事务结束。这将防止
    其他进程读取或修改表中的数据。
    使用这些选项将使系统忽略原先在SET 语句设定的事务隔离级别( Transaction Isolation
    Level)。请查阅SQL Server 联机手册获取更多信息。
    ----------------------------------------------------------------------------------------------------------------------
    --
      

  2.   

    1 如何锁一个表的某一行
    SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED
    SELECT * FROM table ROWLOCK WHERE id = 1
    2 锁定数据库的一个表
    SELECT * FROM table WITH (HOLDLOCK)
    加锁语句:
    sybase:
    update 表set col1=col1 where 1=0 ;
    MSSQL:
    select col1 from 表(tablockx) where 1=0 ;
    oracle:
    LOCK TABLE 表IN EXCLUSIVE MODE ;
    加锁后其它人不可操作,直到加锁用户解锁,用commit 或rollback 解锁
    几个例子帮助大家加深印象
    设table1(A,B,C)
    A B C
    a1 b1 c1
    a2 b2 c2
    a3 b3 c3
    1)排它锁
    新建两个连接
    在第一个连接中执行以下语句
    begin tran
    update table1
    set A='aa'
    where B='b2'
    waitfor delay '00:00:30' --等待30 秒
    commit tran
    在第二个连接中执行以下语句
    begin tran
    select * from table1
    where B='b2'
    commit tran
    若同时执行上述两个语句,则select 查询必须等待update 执行完毕才能执行即要等待30 秒
    2)共享锁
    在第一个连接中执行以下语句
    begin tran
    select * from table1 holdlock -holdlock 人为加锁
    where B='b2'
    waitfor delay '00:00:30' --等待30 秒
    commit tran
    在第二个连接中执行以下语句
    begin tran
    select A,C from table1
    where B='b2'
    update table1
    set A='aa'
    where B='b2'
    commit tran
    若同时执行上述两个语句,则第二个连接中的select 查询可以执行
    而update 必须等待第一个事务释放共享锁转为排它锁后才能执行即要等待30 秒
    3)死锁
    增设table2(D,E)
    D E
    d1 e1
    d2 e2
    在第一个连接中执行以下语句
    begin tran
    update table1
    set A='aa'
    where B='b2'
    waitfor delay '00:00:30'
    update table2
    set D='d5'
    where E='e1'
    commit tran
    在第二个连接中执行以下语句
    begin tran
    update table2
    set D='d5'
    where E='e1'
    waitfor delay '00:00:10'
    update table1
    set A='aa'
    where B='b2'
    commit tran
    锁的一些用法
      

  3.   

    锁概念以及例程说明收藏
    锁的概述
    一. 为什么要引入锁
    多个用户同时对数据库的并发操作时会带来以下数据不一致的问题:
    丢失更新
    A,B 两个用户读同一数据并进行修改,其中一个用户的修改结果破坏了另一个修改的结果,比
    如订票系统
    脏读
    A 用户修改了数据,随后B 用户又读出该数据,但A 用户因为某些原因取消了对数据的修改,
    数据恢复原值,此时B 得到的数据就与数据库内的数据产生了不一致
    不可重复读
    A 用户读取数据,随后B 用户读出该数据并修改,此时A 用户再读取数据时发现前后两次的值
    不一致
    并发控制的主要方法是封锁,锁就是在一段时间内禁止用户做某些操作以避免产生数据不一

    二锁的分类
    锁的类别有两种分法:
    1. 从数据库系统的角度来看:分为独占锁(即排它锁),共享锁和更新锁
    MS-SQL Server 使用以下资源锁模式。
    锁模式描述
    共享(S) 用于不更改或不更新数据的操作(只读操作),如SELECT 语句。
    更新(U) 用于可更新的资源中。防止当多个会话在读取、锁定以及随后可能进行的资源更
    新时发生常见形式的死锁。
    排它(X) 用于数据修改操作,例如INSERT、UPDATE 或DELETE。确保不会同时同一资
    源进行多重更新。
    意向锁用于建立锁的层次结构。意向锁的类型为:意向共享(IS)、意向排它(IX) 以及与
    意向排它共享(SIX)。
    架构锁在执行依赖于表架构的操作时使用。架构锁的类型为:架构修改(Sch-M) 和架构稳
    定性(Sch-S)。
    大容量更新(BU) 向表中大容量复制数据并指定了TABLOCK 提示时使用。
    共享锁
    共享(S) 锁允许并发事务读取(SELECT) 一个资源。资源上存在共享(S) 锁时,任何其它
    事务都不能修改数据。一旦已经读取数据,便立即释放资源上的共享(S) 锁,除非将事务
    隔离级别设置为可重复读或更高级别,或者在事务生存周期内用锁定提示保留共享(S) 锁。
    更新锁
    更新(U) 锁可以防止通常形式的死锁。一般更新模式由一个事务组成,此事务读取记录,
    获取资源(页或行)的共享(S) 锁,然后修改行,此操作要求锁转换为排它(X) 锁。如果
    两个事务获得了资源上的共享模式锁,然后试图同时更新数据,则一个事务尝试将锁转换为
    排它(X) 锁。共享模式到排它锁的转换必须等待一段时间,因为一个事务的排它锁与其它
    事务的共享模式锁不兼容;发生锁等待。第二个事务试图获取排它(X) 锁以进行更新。由
    于两个事务都要转换为排它(X) 锁,并且每个事务都等待另一个事务释放共享模式锁,因
    此发生死锁。
    若要避免这种潜在的死锁问题,请使用更新(U) 锁。一次只有一个事务可以获得资源的更
    新(U) 锁。如果事务修改资源,则更新(U) 锁转换为排它(X) 锁。否则,锁转换为共享
    锁。
    排它锁
    排它(X) 锁可以防止并发事务对资源进行访问。其它事务不能读取或修改排它(X) 锁锁定
    的数据。
    意向锁
    意向锁表示SQL Server 需要在层次结构中的某些底层资源上获取共享(S) 锁或排它(X)
    锁。例如,放置在表级的共享意向锁表示事务打算在表中的页或行上放置共享(S) 锁。在
    表级设置意向锁可防止另一个事务随后在包含那一页的表上获取排它(X) 锁。意向锁可以
    提高性能,因为SQL Server 仅在表级检查意向锁来确定事务是否可以安全地获取该表上的
    锁。而无须检查表中的每行或每页上的锁以确定事务是否可以锁定整个表。
    意向锁包括意向共享(IS)、意向排它(IX) 以及与意向排它共享(SIX)。
    锁模式描述
    意向共享(IS) 通过在各资源上放置S 锁,表明事务的意向是读取层次结构中的部分(而
    不是全部)底层资源。
    意向排它(IX) 通过在各资源上放置X 锁,表明事务的意向是修改层次结构中的部分(而
    不是全部)底层资源。IX 是IS 的超集。
    与意向排它共享(SIX) 通过在各资源上放置IX 锁,表明事务的意向是读取层次结构中的
    全部底层资源并修改部分(而不是全部)底层资源。允许顶层资源上的并发IS 锁。例如,
    表的SIX 锁在表上放置一个SIX 锁(允许并发IS 锁),在当前所修改页上放置IX 锁(在
    已修改行上放置X 锁)。虽然每个资源在一段时间内只能有一个SIX 锁,以防止其它事务
    对资源进行更新,但是其它事务可以通过获取表级的IS 锁来读取层次结构中的底层资源。
    独占锁:只允许进行锁定操作的程序使用,其他任何对他的操作均不会被接受。执行数据更
    新命令时,SQL Server 会自动使用独占锁。当对象上有其他锁存在时,无法对其加独占锁。
    共享锁:共享锁锁定的资源可以被其他用户读取,但其他用户无法修改它,在执行Select
    时,SQL Server 会对对象加共享锁。
    更新锁:当SQL Server 准备更新数据时,它首先对数据对象作更新锁锁定,这样数据将不
    能被修改,但可以读取。等到SQL Server 确定要进行更新数据操作时,他会自动将更新锁
    换为独占锁,当对象上有其他锁存在时,无法对其加更新锁。
    2. 从程序员的角度看:分为乐观锁和悲观锁。
    乐观锁:完全依靠数据库来管理锁的工作。
    悲观锁:程序员自己管理数据或对象上的锁处理。
    MS-SQLSERVER 使用锁在多个同时在数据库内执行修改的用户间实现悲观并发控制
    三锁的粒度
    锁粒度是被封锁目标的大小,封锁粒度小则并发性高,但开销大,封锁粒度大则并发性低但开
    销小
    SQL Server 支持的锁粒度可以分为为行、页、键、键范围、索引、表或数据库获取锁
    资源描述
    RID 行标识符。用于单独锁定表中的一行。
    键索引中的行锁。用于保护可串行事务中的键范围。
    页8 千字节(KB) 的数据页或索引页。
    扩展盘区相邻的八个数据页或索引页构成的一组。
    表包括所有数据和索引在内的整个表。
    DB 数据库。
    四锁定时间的长短
    锁保持的时间长度为保护所请求级别上的资源所需的时间长度。
    用于保护读取操作的共享锁的保持时间取决于事务隔离级别。采用READ COMMITTED 的
    默认事务隔离级别时,只在读取页的期间内控制共享锁。在扫描中,直到在扫描内的下一页
    上获取锁时才释放锁。如果指定HOLDLOCK 提示或者将事务隔离级别设置为
    REPEATABLE READ 或SERIALIZABLE,则直到事务结束才释放锁。
    根据为游标设置的并发选项,游标可以获取共享模式的滚动锁以保护提取。当需要滚动锁时,
    直到下一次提取或关闭游标(以先发生者为准)时才释放滚动锁。但是, 如果指定
    HOLDLOCK,则直到事务结束才释放滚动锁。
    用于保护更新的排它锁将直到事务结束才释放。
    如果一个连接试图获取一个锁,而该锁与另一个连接所控制的锁冲突,则试图获取锁的连接
    将一直阻塞到:
    将冲突锁释放而且连接获取了所请求的锁。
    连接的超时间隔已到期。默认情况下没有超时间隔,但是一些应用程序设置超时间隔以防止
    无限期等待
    五SQL Server 中锁的自定义
    1 处理死锁和设置死锁优先级
    死锁就是多个用户申请不同封锁,由于申请者均拥有一部分封锁权而又等待其他用户拥有的
    部分封锁而引起的无休止的等待
    可以使用SET DEADLOCK_PRIORITY 控制在发生死锁情况时会话的反应方式。如果两个
    进程都锁定数据,并且直到其它进程释放自己的锁时,每个进程才能释放自己的锁,即发生
    死锁情况。
    2 处理超时和设置锁超时持续时间。
    @@LOCK_TIMEOUT 返回当前会话的当前锁超时设置,单位为毫秒
    SET LOCK_TIMEOUT 设置允许应用程序设置语句等待阻塞资源的最长时间。当语句等待
    的时间大于LOCK_TIMEOUT 设置时,系统将自动取消阻塞的语句,并给应用程序返回"
    已超过了锁请求超时时段"的1222 号错误信息
    示例
    下例将锁超时期限设置为1,800 毫秒。
    SET LOCK_TIMEOUT 1800
    3) 设置事务隔离级别。
    4 ) 对SELECT、INSERT、UPDATE 和DELETE 语句使用表级锁定提示。
    5) 配置索引的锁定粒度
    可以使用sp_indexoption 系统存储过程来设置用于索引的锁定粒度
    六查看锁的信息
    1 执行EXEC SP_LOCK 报告有关锁的信息
    2 查询分析器中按Ctrl+2 可以看到锁的信息
    七使用注意事项
    如何避免死锁
    1 使用事务时,尽量缩短事务的逻辑处理过程,及早提交或回滚事务;
    2 设置死锁超时参数为合理范围,如:3 分钟-10 分种;超过时间,自动放弃本次操作,避
    免进程悬挂;
    3 优化程序,检查并避免死锁现象出现;
    4 .对所有的脚本和SP 都要仔细测试,在正是版本之前。
    5 所有的SP 都要有错误处理(通过@error)
    6 一般不要修改SQL SERVER 事务的默认级别。不推荐强行加锁
    解决问题如何对行表数据库加锁
    八几个有关锁的问题
    1 如何锁一个表的某一行
    SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED
    SELECT * FROM table ROWLOCK WHERE id = 1
    2 锁定数据库的一个表
    SELECT * FROM table WITH (HOLDLOCK)
    加锁语句:
    sybase:
    update 表set col1=col1 where 1=0 ;
    MSSQL:
    select col1 from 表(tablockx) where 1=0 ;
    oracle:
    LOCK TABLE 表IN EXCLUSIVE MODE ;
    加锁后其它人不可操作,直到加锁用户解锁,用commit 或rollback 解锁
    几个例子帮助大家加深印象
    设table1(A,B,C)
    A B C
    a1 b1 c1
    a2 b2 c2
    a3 b3 c3
    1)排它锁
    新建两个连接
    在第一个连接中执行以下语句
    begin tran
    update table1
    set A='aa'
    where B='b2'
    waitfor delay '00:00:30' --等待30 秒
    commit tran
    在第二个连接中执行以下语句
    begin tran
    select * from table1
    where B='b2'
    commit tran
    若同时执行上述两个语句,则select 查询必须等待update 执行完毕才能执行即要等待30 秒
    2)共享锁
    在第一个连接中执行以下语句
    begin tran
    select * from table1 holdlock -holdlock 人为加锁
    where B='b2'
    waitfor delay '00:00:30' --等待30 秒
    commit tran
    在第二个连接中执行以下语句
    begin tran
    select A,C from table1
    where B='b2'
    update table1
    set A='aa'
    where B='b2'
    commit tran
    若同时执行上述两个语句,则第二个连接中的select 查询可以执行
    而update 必须等待第一个事务释放共享锁转为排它锁后才能执行即要等待30 秒
    3)死锁
    增设table2(D,E)
    D E
    d1 e1
    d2 e2
    在第一个连接中执行以下语句
    begin tran
    update table1
    set A='aa'
    where B='b2'
    waitfor delay '00:00:30'
    update table2
    set D='d5'
    where E='e1'
    commit tran
    在第二个连接中执行以下语句
    begin tran
    update table2
    set D='d5'
    where E='e1'
    waitfor delay '00:00:10'
    update table1
    set A='aa'
    where B='b2'
    commit tran
    同时执行,系统会检测出死锁,并中止进程
    补充一点:
    Sql Server2000 支持的表级锁定提示
    HOLDLOCK 持有共享锁,直到整个事务完成,应该在被锁对象不需要时立即释放,等于
    SERIALIZABLE 事务隔离级别
    NOLOCK 语句执行时不发出共享锁,允许脏读,等于READ UNCOMMITTED 事务隔离
    级别
    PAGLOCK 在使用一个表锁的地方用多个页锁
    READPAST 让sql server 跳过任何锁定行,执行事务,适用于READ UNCOMMITTED 事务
    隔离级别只跳过RID 锁,不跳过页,区域和表锁
    ROWLOCK 强制使用行锁
    TABLOCKX 强制使用独占表级锁,这个锁在事务期间阻止任何其他事务使用这个表
    UPLOCK 强制在读表时使用更新而不用共享锁
    应用程序锁:
    应用程序锁就是客户端代码生成的锁,而不是sql server 本身生成的锁
    处理应用程序锁的两个过程
    sp_getapplock 锁定应用程序资源
    sp_releaseapplock 为应用程序资源解锁
    注意: 锁定数据库的一个表的区别
    SELECT * FROM table WITH (HOLDLOCK) 其他事务可以读取表,但不能更新删除
    SELECT * FROM table WITH (TABLOCKX) 其他事务不能读取表,更新和删除
    SQL code
    1 如何锁一个表的某一行
    A 连接中执行
    SET TRANSACTION ISOLATION LEVEL REPEATABLE READ
    begin tran
    select * from tablename with (rowlock) where id=3
    waitfor delay '00:00:05'
    commit tran
    B 连接中如果执行
    update tablename set colname='10' where id=3 --则要等待5 秒
    update tablename set colname='10' where id<>3 --可立即执行
    2 锁定数据库的一个表
    SELECT * FROM table WITH (HOLDLOCK)
    注意: 锁定数据库的一个表的区别
    SELECT * FROM table WITH (HOLDLOCK)
    其他事务可以读取表,但不能更新删除
    SELECT * FROM table WITH (TABLOCKX)
    其他事务不能读取表,更新和删除
      

  4.   

    1.1 行级锁 
    行级锁是针对行来锁定的,比如在事务里,进程A执行了一条update语句: 
    update student set name='xx' where id=13 
    则行级锁会锁住student表里id=13的记录,不让别的进程对它操作, 
    只有等事务完成后才解除锁,举个例子,以 SQL SERVER为例, 
    同时打开两个查询分析器,在第一个查询分析器里写: 
    use northwind 
    select * from suppliers 
    begin transaction 
       update suppliers set CompanyName='xx' where SupplierID=3 
       waitfor delay '00:00:20' 
    commit transaction 
    在第二个查询分析器里写: 
    select * from suppliers 
    然后先运行第一个查询分析器里的代码,再运行第二个查询分析器里的 
    代码,可以看到第一个查询分析器一直运行,运行了大概20秒后执行 
    完毕,第二个查询分析器也一样,运行了大概20秒才停止, 
    这说明执行 select * from suppliers 时在等待,如果不运行第一个 
    查询分析器里的代码,直接运行第二个查询分析器里的代码,那几乎不 
    用等待就可以看到结果了; 
    修改第二个查询分析器的代码为:  
    select * from suppliers where SupplierID<>3 
    然后先运行第1个查询分析器的代码,再运行第二个查询分析器的代码, 
    可以看到第二个查询分析器一运行马上就出结果了,没有等待, 
    再修改代码为: 
    select * from suppliers where SupplierID=3 
    重复前面的操作,可以看到需要等待,等待约20秒后才看到结果 
    这很明显的告诉我们,行级锁会锁住修改的行,让别的进程无法操作 
    那些行,只有事务完成后别的进程才可以操作,而没有修改的行,别的 
    进程就可以任意操作,不会有限制 
    1.2 页级锁(1) 
    先理解页这个概念,在SQL SERVER里建表时,如果字段大小比较大时, 
    往往会有提示: 表中允许的最大行大小 8060 
    比如在SQL SERVER2000 里新建一个表: 
    create table Test(Fld1 char(5000),Fld2 char(5000)) 
    会提示建立失败,原因如: 
    “创建表 'Test' 失败,因为行大小将为 10021(包括内部开销),而该值超过了表中允许的最大行大小 8060。” 
    为什么它会限制行大小为8060呢?因为在 SQLSERVER2000里,一页的 
    大小为8K,这8K里包括96字节的页头、36字节的其它信息、8060字节的 
    数据区,而数据就存储在8060字节的数据区里, 
    一页能存储多少行呢?这要看行的大小了,比如如果行大小为2000, 
    则一页能存储4行,注意行大小不包括文本和图象字段, 
    比如数据库northwind的customers表的行大小为 298, 
    则一页可以存储 27 行 
     看看行大小计算的问题: 
    use northwind 
    alter table customers add xx char(8000) 
    运行结果有警告: 
    ----------------------------------------- 
     警告: 已创建表 'customers',但其最大行大小(8578)超过了每行的最大字节数(8060)。如果结果行长度超过 8060 字节,则此表中行的 INSERT 或 UPDATE 将失败 
    ----------------------------------------- 
    它提示行大小为8578,则修改前的customers的行大小为578, 
    可为什么我将各个字段的大小加起来才268呢, 
    这有两个原因,一方面,数据库用两个字节存储一个nvarchar类型的字符 
    nchar也一样,而customers的字段类型为nchar和nvarchar,所以实际大小为 268*2=536 ,那还有42呢?42是表的其它开销。 
    从行与页的关系可以看出,行的大小越小,则一页能存储的行数越多, 
    数据库查询时,从一页读到另一页,比只读一页的记录要慢得多, 
    所以要减少跨页读取的次数, 
     
    比较下面的两个语句: 
    create table x1(a char(5000),b char(5000)) 
    create table x2(a varchar(5000),b char(5000)) 
    运行结果为: 
    ------------------------------------ 
    服务器: 消息 1701,级别 16,状态 2,行 2 
    创建表 'x1' 失败,因为行大小将为 10021(包括内部开销),而该值超过了表中允许的最大行大小 8060。 
    警告: 已创建表 'x2',但其最大行大小(10023)超过了每行的最大字节数(8060)。如果结果行长度超过 8060 字节,则此表中行的 INSERT 或 UPDATE 将失败。 
    ------------------------------------ 
    x1创建失败,x2创建成功,但有警告,为什么呢? 
    这要比较char和varchar的区别了,当创建x1时,最大行大小10023就是 
    实际的行大小,因为char是定长的,大小总是10023,而x2不同, 
    varchar是变长的,虽然最大行大小是10023,而实际行大小却不一定的, 
    实际行大小随字段a的值的大小的变化而变化, 
    所以,每页能存储的行数,如果是定长的,那在建表时就可以确定了, 
    如果是变长的,那要根据表中的数据来确定,当然,SQL SERVER存储记录 
    时,对于页的选择还会考虑一些问题,也并不完全是这样看看Northwind数据库的Customers表吧, 
    Customers的主键字段为CustomerID,主键是聚集索引的, 
    主键的顺序代表了行的实际存储顺序, 
    比如你往Customers里插入一条记录: 
    insert into Customers(CustomerId,CompanyName) values('cvcvc','ff') 
    然后用select * from customers查看数据, 
    可以看到新插入的记录自动排在了CustomerId等于CONSH的后面, 
    看起来就和 select * from customers order by customerId 
    查出来的数据一样,聚集索引就是这样,记录的物理存储顺序与 
    聚集索引的顺序是一样的.
     
    看看Customers表,打开三个查询分析器,在第一个表写: 
    begin transaction  
     update customers with(PagLock) set Address=Address 
     where customerId='ALFKI' 
     waitfor delay '00:00:30'  
    commit transaction  
    在第二个查询分析器里写: 
    select * from customers where customerId='GREAL' 
    在第三个查询分析器里写: 
    select * from customers where customerId='GROSR' 
    先运行第一个查询分析器,然后运行第二个,再运行第三个, 
    可以看到,第一和第二个查询分析器等待执行了20秒, 
    而第三个查询分析器没有等待立即就显示运行结果了, 
    我更新的是'ALFKI',因为是页锁,所以它锁住了一页的数据, 
    从'ALFKI'到'GREAL'的行都锁住了,这也说明, 
    'ALFKI'到'GREAL'之间的共34行都是属于同一页的, 
    你可以将第一个查询分析器的'ALFKI'换成'DRACD',可以看到运行 
    结果是一样的,如果换成'HANAR',那结果就变了, 
    变成第一个和第三个查询分析器在等待,而第二个查询分析器不用等待, 
    因为'HANAR'和'GROSR'属于同一页,而'GREAL'在其它的页, 
    页锁概念是比较简单的,但页的概念却比较复杂, 
    页是在SQLSERVER的内部管理的,用户看不到,页比较抽象, 
    对于变长的数据类型,页的分配是随数据的变化而变化的, 
    请参考数据库相关的资料。 
    本文来自CSDN博客,转载请标明出处:http://blog.csdn.net/ytbada/archive/2007/04/20/1572433.aspx
      

  5.   

    1.3 表锁 
    在第一个查询分析器写: 
    begin transaction tran1 
     update customers with(TabLock) set City=City  
     where CustomerId='ALFKI' 
     waitfor delay '00:00:20' 
    commit transaction tran1 
    在第二个查询分析器写: 
    select * from customers where customerId='WOLZA' 
    先运行第一个查询分析器,再运行第二个,两个查询分析器都在等待. 
    注意customerId='WOLZA'是表的最后一条记录  
     
     
    1.4 阻塞 
    前面的例子里一个事务未提交,导致别的事务必须等待,这就是阻塞, 
    查看阻塞可以用sp_lock,打开三个查询分析器, 
    第一个写: 
    begin transaction tran1 
     update products set productName=productName+'A' 
     where ProductId=1 
     waitfor delay '00:00:30' 
    commit transaction tran1 
    第二个写: 
    select * from products 
    第三个写: 
    sp_lock 
    依次运行第一个、第二个、第三个, 
    然后查看第三个分析器,看看Status列, 
    看是否有Status='Wait'的行,比如我这里查看有这么一行: 
    53 6 117575457 1 KEY (010086470766) S WAIT 
    其中ObjId=117575457 
    然后运行: 
    use northwind 
    select object_name(117575457) 
    可以看到对应的表为 Products 
    1.5 死锁 
    同时打开两个查询分析器, 
    第一个写: 
    begin transaction tran2 
     update products set productName=productName+'A' 
     where ProductId=2 
     waitfor delay '00:00:10' 
     update products set productName=productName+'A' 
     where ProductId=1 
    commit transaction tran2 
    第二个写: 
    begin transaction tran1 
     update products set productName=productName+'A' 
     where ProductId=1 
     waitfor delay '00:00:10' 
     update products set productName=productName+'A' 
     where ProductId=2 
    commit transaction tran1 
    先运行第一个,再运行第二个 
    然后等待它们执行,等待大概十多秒, 
    检查运行结果,可以看到其中一个出错,错误提示如: 
    服务器: 消息 1205,级别 13,状态 50,行 1 
    在查询分析器里按F1打开帮助,在帮助里选择索引选项卡, 
    输入 1205 ,你仔细查看帮助文档是如何描述 1205 错误的, 
    为什么会死锁呢?看看执行过程,为了简单,我将productId简写为id 
    先是分析器1更新id=2的记录,并锁住id=2的记录,那别的进程都无法 
    操作id=2的记录, 
    然后分析器2更新id=1的记录,并锁住id=1的记录,同样别的进程无法 
    操作id=1的记录, 
    然后分析器1更新id=1的记录,因为id=1的记录被分析器2锁住了, 
    所以必须等待,分析器1被阻塞 
    同样分析器2更新id=2的记录,因为id=2的记录被分析器1锁住了, 
    所以也要等待,分析器2被阻塞 
    两个分析器都要等待对方,所以就出现死锁,哪个都不能执行, 
    当然,SQLSERVER2000为了解决死锁问题,它会干掉其中一个进程 
    来结束死锁。 1.6 占用读 
    占用读指可以读别的进程未提交的数据, 
    打开两个查询分析器,第一个写: 
    begin transaction tran1 
     update products set productName=productName+'C' 
     where ProductId=1 
     waitfor delay '00:00:15' 
    commit transaction tran1 
    第二个写: 
    set transaction isolation level read uncommitted 
    select * from products where ProductId=1 
    依次运行第一个和第二个, 
    可以看到第一个在等待,而第二个不用等待, 
    因为我在第二个里设置了隔离级别为read uncommitted, 
    就是允许读别的事务未提交的数据, 
    你看看第二个的运行结果,找到products列,看到products列已经修改了 
    如果你修改第二个查询分析器代码为: 
    set transaction isolation level read committed 
    select * from products where ProductId=1 
    同样运行,那第二个也要等待了,因为隔离级别是read committed, 
    只能读提交后的数据,不能读未提交的修改,这样就防止了 
    占用读,SQLSERVER2000里默认是read committed 
    说明,占用读也叫脏读,脏读就是修改了但没提交的数据, 
    在文本编辑器里也有脏读的概念,就是修改了但未保存的数据 
    1.7 不可重复读 
    事务里执行两次相同的查询时,查询出来的结果不相同, 
    说明是不可重复读,打开两个查询分析器, 
    第一个写: 
    use northwind 
    set transaction isolation level read committed 
    begin transaction tran1 
     select * from region where regionId=3 
     waitfor delay '00:00:10' 
     select * from region where regionId=3 
    commit transaction tran1 
    第二个写: 
    use northwind 
    update region set regionDescription='xx' where regionId=3 
    依次运行第一个和第二个分析器,第一个分析器等待10秒,第二个 
    不用等待立即得到结果,第一个分析器运行结果为: 
    3 Northern 
    3 xx 
    两次读得的值不相同, 
    修改第一个查询分析器代码为: 
    use northwind 
    set transaction isolation level repeatable read 
    begin transaction tran1 
     select * from region where regionId=3 
     waitfor delay '00:00:10' 
     select * from region where regionId=3 
    commit transaction tran1 
    第二个修改为: 
    use northwind 
    update region set regionDescription='yy' where regionId=3 
    同样依次运行第一个和第二个,看到第一个在等待, 
    第二个也在等待,第一个分析器运行结果为: 
    3 xx 
    3 xx 
    看看两次的区别,第一次我设置隔离级别为read committed, 
    第二次我设置为repeatable read, 
    repeatable read 会锁住读的数据, 
    read committed 会锁住修改的数据, 
    repeatable read会锁住insert、update、delete、select操作的数据 
    read committed只锁insert 、update、delete, 不锁select查询的数据 
    1.8 幻像读 
    打开两个查询分析器,第一个写:  
    use northwind 
    set transaction isolation level repeatable read 
    begin transaction tran1 
     select * from region 
     waitfor delay '00:00:10' 
     select * from region 
    commit transaction tran1 
    第二个写:  
    use northwind 
    insert into region values(5,'xx') 
    依次运行第一个和第二个分析器,第一个分析器等待10秒,第二个  
    不用等待立即得到结果,第一个分析器运行结果为:  
    1 Eastern  
    2 Western  
    3 Northern  
    4 Southern  
    1 Eastern  
    2 Western  
    3 Northern  
    4 Southern  
    5 xx  
    比较两次查询的结果,第二次查询多了一行, 
    修改第一个分析器的代码为: 
    use northwind 
    set transaction isolation level serializable 
    begin transaction tran1 
     select * from region 
     waitfor delay '00:00:10' 
     select * from region 
    commit transaction tran1 
    修改第二个分析器代码为: 
    use northwind 
    insert into region values(6,'yy') 
    再依次运行第一个和第二个分析器, 
    可以看到两个分析器都要等待,第一个的运行结果是: 
    两次查询返回的行数是相同的。 理解占用读、不可重复读、幻像读要从数据库如何操作来避免他们上 
    来理解,如果只从概念上去理解,概念往往很抽象,比较晦涩难懂, 
    而且概念往往只说到其中一个方面,应该弄清楚各种级别的琐是如何 
    避免出现占用读、不可重复读、幻像读的。 
    read uncommitted不设置锁, 
    read commmitted会锁住update、insert、delete 
    repeatable read会锁住update、insert、delete、select 
    seriablizable会锁住update、insert、delete、select 
    repeatable read和seriablizable的区别在于: 
    repeatable read锁住时别的事务不能update、delete锁住的数据, 
    但别的事务能够插入, 
    seriablizable锁住时别的事务不能update、delete、insert 说明: 
    repeatable read 和 seriablizable 对 select 的锁定采用范围的方式 
    要锁哪些行,主要是受where语句的限制,另外还受行锁、页锁、表锁方式 
    的限制,对于update、insert、delete的锁定范围比较明确, 
    repeatable read隔离级别对select的锁定也比较明确, 
    而seriablizable对select的锁定,当别的事务insert时, 
    哪些时候不能插入呢?这个范围如何确定? 
    因为锁的概念往往是针对已有的数据,而insert插入的数据是原来表里 
    没有的,原来表里没有,那又如何锁定呢? 
    比如: 
    set transaction isolation level seriablizable  
    select * from region 
    则锁住表的所有行,比如开始有四行,则锁住四行, 
    那insert一行呢,这里有个范围,那就是select的范围, 
    它会判断insert的行是否在锁定的范围之内,比如: 
    use northwind 
    set transaction isolation level serializable 
    begin transaction tran1 
     select * from region where regionId>6 
     waitfor delay '00:00:10' 
    commit transaction tran1 
    如果别的事务插入记录: 
    insert into region values(5,'yy') 
    因为插入的记录regionId=5,而6<5,不满足锁定的条件, 
    所以该插入是允许的,如果别的事务插入记录: 
    insert into region values(7,'yy') 
    因为7>6,满足条件,所以插入被阻塞,只能等待锁释放才能插入 
    不过,到底锁定哪些记录,这比较难说,锁做为SQLSERVER的内部管理, 
    到底是怎么样的不怎么清楚,我试过有些情况不满足的条件的记录也 
    被阻塞,不过有点是清楚的,那就是满足条件的一定被阻塞 1.8 隔离级别 
    前面已经说过了,有四种隔离级别: 
    read uncommitted 
    read committed 
    repeatable read 
    serializable 
    read committed是默认的隔离级别, 
    隔离级别对单个用户有用,比如你设置了隔离级别为serializable, 
    那只对你自己有用,对别的用户不起作用,设置了隔离级别后, 
    那一直有效,直到用户退出为止, 
    锁的作用主要是用来保证数据一致性的, 
    read uncommitted不会在被读的数据上放置锁,所以它执行 
    的速度是最快的,也不会造成阻塞和死锁,但因为数据一致 
    性问题,所以往往不采用,当然可以通过别的技术比如增加 
    rowverision列等来保证数据一致性,但对于复杂的操作,还 
    是选择事务比较安全,对于事务我经历过一些教训, 
    比如一次我在VB里保存数据,大致如: 
    begin transaction 
     declare @id as int 
     insert into A(...) values(...) 
     select @id=max(id) from t1 
     insert into B(AId...) values(@id...) 
    commit transation 
    其中A表的id字段是自动编号的,我先在A表插入一条记录, 
    再将A表刚插入的记录的Id插入到B表,必须保证@id是前面insert 
    生成的那个id, 
    但测试时,因为客户有很多电脑同时录,所以导致一些id不一致的情况, 
    为什么会不一致呢?我不是已经加了事务了吗? 
    做一个例子,同样打开两个查询分析器,第一个写: 
    use northwind 
    set transaction isolation level serializable 
    begin transaction tran1 
     insert into region values(10,'aa') 
     waitfor delay '00:00:10' 
     select max(regionId) from region 
    commit transaction tran1 
    第二个写: 
     insert into region values(11,'aa') 
    依次执行第一个和第二个分析器, 
    本来我希望第一个分析器里查询出来的是10,可结果却是11 
    在这里,唯一的方法是指定表锁,如: 
    insert into region with(TabLock) values(10,'aa') 
    只有指定表锁,让别的事务无法操作它,才能保证数据一致, 
    当然,如果是自动编号,那可以用 @@identity 来获取刚生成的Id号, 
    比如: 
    insert into orders(CustomerId) values('ALFKI') 
    print @@identity 
    这个技巧在别的数据库驱动程序里可能无效,事务才是普遍支持的 补充(表来源: SQL SERVER7.0 系统管理指南) 
    隔离级别    阻塞风险 防止占用读 防止不可重复读 防止幻像读 
    --------------------------------------------------------------- 
    read uncommitted 最低   NO      NO       NO 
    read committed  较低   Yes      NO       NO 
    repeatable read 较高   Yes      Yes       NO 
    serializable   最高   Yes      Yes       Yes 
    1.9 显式锁 
    显式锁是在select、insert、delete、update语句里指定锁的类型, 
    如: 
    select * from authors with(RowLock) where au_id <='555-55-5555' 
    如果不用显式锁,那就是: 
    select * from authors where au_id <='555-55-5555' 
    如果不用显式锁,那采用那种锁是由SQL SERVER内部来决定的, 本文来自CSDN博客,转载请标明出处:http://blog.csdn.net/ytbada/archive/2007/04/20/1572433.aspx
      

  6.   

    买本书回家看看就知道了。看书+Baidu+GooGle+Bing+Csdn+Msdn
      

  7.   

    分析死锁并处理_整理贴5
    http://topic.csdn.net/u/20080721/20/15a60db6-34b5-4ea1-b392-11c42270aaab.html
      

  8.   

    死锁 多个会话同时访问数据库一些资源时,当每个会话都需要别的会话正在使用的资源时,死锁就有可能发生。死锁在多线程系统中都有可能出现,并不仅仅局限于于关系数据库管理系统。 锁的类型 一个数据库系统在许多情况下都有可能锁数据项。其可能性包括: Rows—数据库表中的一整行 
    Pages—行的集合(通常为几kb) 
    Extents—通常是几个页的集合 
    Table—整个数据库表 
    Database—被锁的整个数据库表 除非有其它的说明,数据库根据情况自己选择最好的锁方式。不过值得感谢的是,SQL Server提供了一种避免默认行为的锁提示 Tansact-SQL提供了一系列不同级别的锁提示,你可以在SELECT,INSERT,UPDATE和DELETE中使用它们来告诉SQL Server你需要如何通过重设锁。可以实现的提示包括: ·  FASTFIRSTROW—选取结果集中的第一行,并将其优化 ·  HOLDLOCK—持有一个共享锁直至事务完成 ·  NOLOCK—不允许使用共享锁或独享锁。这可能会造成数据重写或者没有被确认就返回的情况; 因此,就有可能使用到脏数据。这个提示只能在SELECT中使用。 ·  PAGLOCK—锁表格 ·  READCOMMITTED—只读取被事务确认的数据。这就是SQL Server的默认行为。 ·  READPAST—跳过被其它进程锁住的行,所以返回的数据可能会忽略行的内容。这也只能在SELECT中使用。 ·  READUNCOMMITTED—等价于NOLOCK. ·  REPEATABLEREAD—在查询语句中,对所有数据使用锁。这可以防止其它的用户更新数据, 但是新的行可能被其它的用户插入到数据中,并且被最新访问该数据的用户读取。 ·  ROWLOCK—按照行的级别来对数据上锁。SQL Server通常锁到页或者表级别来修改行, 所以当开发者使用单行的时候,通常要重设这个设置。 ·  SERIALIZABLE—等价于HOLDLOCK. ·  TABLOCK—按照表级别上锁。在运行多个有关表级别数据操作的时候,你可能需要使用到这个提示。 ·  UPDLOCK—当读取一个表的时候,使用更新锁来代替共享锁,并且保持一直拥有这个锁直至事务结束。 它的好处是,可以允许你在阅读数据的时候可以不需要锁,并且以最快的速度更新数据。 ·  XLOCK—给所有的资源都上独享锁,直至事务结束。 微软将提示分为两类:granularity和isolation-level。Granularity提示包括PAGLOCK, NOLOCK, ROWLOCK和TABLOCK。而isolation-level提示包括HOLDLOCK, NOLOCK, READCOMMITTED, REPEATABLEREAD和SERIALIZABLE。 可以在Transact-SQL声明中使用这些提示。它们被放在声明的FROM部分中,位于WITH之后。WITH声明在SQL Server 2000中是可选部分,但是微软强烈要求将它包含在内。这就使得许多人都认为在未来的SQL Server发行版中,就可能会包含这个声明。下面是提示应用于FROM从句中的例子: [ FROM { < table_source > } [ ,...n ] ]< table_source > ::=table_name [ [ AS ] table_alias ] [ WITH ( < table_hint > [ ,...n ] ) ]  < table_hint > ::=  { INDEX ( index_val [ ,...n ] )  | FASTFIRSTROW  | HOLDLOCK  | NOLOCK  | PAGLOCK  | READCOMMITTED  | READPAST  | READUNCOMMITTED  | REPEATABLEREAD  | ROWLOCK  | SERIALIZABLE  | TABLOCK  | TABLOCKX  | UPDLOCK  | XLOCK }词汇表会话 (session) English Query 中由 English Query 引擎执行的操作序列。会话在用户登录时开始,在用户注销时结束。 会话期间的所有操作构成一个事务作用域,并受由登录用户名和密码决定的权限的支配。 堆表 (heap table) 如果一个表没有索引,数据行以随机的顺序存储,这种结构称为堆。这种表称为堆表。 意向锁 (intent lock) 放置在资源层次结构的一个级别上的锁,以保护较低级别资源上的共享或排它锁。例如,在 SQL Server 2000 数据库引擎任务应用表内的共享或排它行锁之前,在该表上放置意向锁。如果另一个任务试图在该表级别上应用共享或排它锁,则受到由第一个任务控制的表级别意向锁的阻塞。第二个任务在锁定该表前不必检查各个页或行锁,而只需检查表上的意向锁。 排它锁(exclusive lock) 一种锁,它防止任何其它事务获取资源上的锁,直到在事务的末尾将资源上的原始锁释放为止。在更新操作(INSERT、UPDATE 或 DELETE)过程中始终应用排它锁。 隔离级别 (isolation level) 控制隔离数据以供一个进程使用并防止其它进程干扰的程度的事务属性。设置隔离级别定义了 SQL Server 会话中所有 SELECT 语句的默认锁定行为。 扩展 (extent) 每当 SQL Server 对象(如表或索引)需要更多空间时分配给该对象的空间的单元。在 SQL Server 2000 中,一个扩展是八个邻接的页。 锁粒度(lock granularity) SQL Server中数据以8KB为一页(page)的单位保存,连续的8个页组成一个扩展(extent)。创建数据库时, 按这种方式来分配磁盘空间。当数据库容量增加时,意味着要创建更多的页和扩展。按照数据的存储结构 (row,page,extent)进行加锁,就是锁粒度。 SQL Server 2000里,最低的锁粒度是行(row)锁。SQL Server可以单独锁行,数据页,扩展,表。 假设在UPDATE操作中只影响一行记录,SQL Server会将该行记录锁定,其他用户只有等该行记录的 更新操作完毕后才能修改。另一方面,对于没有锁定的行记录,其他用户是可以进行修改的。 因此行级锁对于并发是最佳的。 现在假设UPDATE操作影响1000行记录,SQL Server是否一次锁定一行?那就意味着如果有一个这样的选项,在 内存允许前提下,需要1000个锁。实际上,SQL Server会根据这些数据是否分布在连续的页,来决定是否用几个页面锁,或者扩展锁,或者是表锁。如果SQL Server加了页面锁,那么这些页面上的记录其它用户就无法 访问或者修改,即使页面上有些数据并非属于这1000行记录。这就是一种追求并发性能和资源消耗之间的平衡策略。 SQL Server对锁需要的资源十分敏感,也就是说,SQL Server查询优化器检测到可用内存较低时,就会使用页锁来 替代多个行锁。锁信息的标识 锁类型: RID :行标识符。用于在表中单独锁定一行。 
    KEY :键, 索引内部的行锁。用于保护可串行事务中的键范围。 
    PAG :数据或索引页。 
    EXT :相邻的八个数据页或索引页构成的一组。 
    TAB :包括所有数据和索引在内的整个表。 
    DB :数据库。 ^_^,说是简单介绍,其实我觉得已经对锁介绍也蛮多了,也许有写得不对的地方,有心人帮忙指点一下。词汇的中文翻
      

  9.   

    数据库锁表的分析与解决 
    上面介绍了内存溢出的原因和处理方法,下面再介绍一下数据库锁表及阻塞的原因和处理办法。 
    数据库和操作系统一样,是一个多用户使用的共享资源。当多个用户并发地存取数据时,在数据库中就会产生多个事务同时存取同一数据的情况。若对并发操作不加控制就可能会读取和存储不正确的数据,破坏数据库的一致性。加锁是实现数据库并发控制的一个非常重要的技术。在实际应用中经常会遇到的与锁相关的异常情况,当两个事务需要一组有冲突的锁,而不能将事务继续下去的话,就会出现死锁,严重影响应用的正常执行。 
    在数据库中有两种基本的锁类型:排它锁(Exclusive Locks,即X锁)和共享锁(Share Locks,即S锁)。当数据对象被加上排它锁时,其他的事务不能对它读取和修改。加了共享锁的数据对象可以被其他事务读取,但不能修改。数据库利用这两种基本的锁类型来对数据库的事务进行并发控制。 
    死锁的第一种情况 
    一个用户A 访问表A(锁住了表A),然后又访问表B;另一个用户B 访问表B(锁住了表B),然后企图访问表A;这时用户A由于用户B已经锁住表B,它必须等待用户B释放表B才能继续,同样用户B要等用户A释放表A才能继续,这就死锁就产生了。 
    解决方法: 
    这种死锁比较常见,是由于程序的BUG产生的,除了调整的程序的逻辑没有其它的办法。仔细分析程序的逻辑,对于数据库的多表操作时,尽量按照相同的顺序进行处理,尽量避免同时锁定两个资源,如操作A和B两张表时,总是按先A后B的顺序处理, 必须同时锁定两个资源时,要保证在任何时刻都应该按照相同的顺序来锁定资源。 
    死锁的第二种情况 
    用户A查询一条纪录,然后修改该条纪录;这时用户B修改该条纪录,这时用户A的事务里锁的性质由查询的共享锁企图上升到独占锁,而用户B里的独占锁由于A有共享锁存在所以必须等A释放掉共享锁,而A由于B的独占锁而无法上升的独占锁也就不可能释放共享锁,于是出现了死锁。这种死锁比较隐蔽,但在稍大点的项目中经常发生。如在某项目中,页面上的按钮点击后,没有使按钮立刻失效,使得用户会多次快速点击同一按钮,这样同一段代码对数据库同一条记录进行多次操作,很容易就出现这种死锁的情况。 
    解决方法: 
    1、对于按钮等控件,点击后使其立刻失效,不让用户重复点击,避免对同时对同一条记录操作。 
    2、使用乐观锁进行控制。乐观锁大多是基于数据版本(Version)记录机制实现。即为数据增加一个版本标识,在基于数据库表的版本解决方案中,一般是通过为数据库表增加一个“version”字段来实现。读取出数据时,将此版本号一同读出,之后更新时,对此版本号加一。此时,将提交数据的版本数据与数据库表对应记录的当前版本信息进行比对,如果提交的数据版本号大于数据库表当前版本号,则予以更新,否则认为是过期数据。乐观锁机制避免了长事务中的数据库加锁开销(用户A和用户B操作过程中,都没有对数据库数据加锁),大大提升了大并发量下的系统整体性能表现。Hibernate 在其数据访问引擎中内置了乐观锁实现。需要注意的是,由于乐观锁机制是在我们的系统中实现,来自外部系统的用户更新操作不受我们系统的控制,因此可能会造成脏数据被更新到数据库中。 
    3、使用悲观锁进行控制。悲观锁大多数情况下依靠数据库的锁机制实现,如Oracle的Select … for update语句,以保证操作最大程度的独占性。但随之而来的就是数据库性能的大量开销,特别是对长事务而言,这样的开销往往无法承受。如一个金融系统,当某个操作员读取用户的数据,并在读出的用户数据的基础上进行修改时(如更改用户账户余额),如果采用悲观锁机制,也就意味着整个操作过程中(从操作员读出数据、开始修改直至提交修改结果的全过程,甚至还包括操作员中途去煮咖啡的时间),数据库记录始终处于加锁状态,可以想见,如果面对成百上千个并发,这样的情况将导致灾难性的后果。所以,采用悲观锁进行控制时一定要考虑清楚。 
    死锁的第三种情况 
    如果在事务中执行了一条不满足条件的update语句,则执行全表扫描,把行级锁上升为表级锁,多个这样的事务执行后,就很容易产生死锁和阻塞。类似的情况还有当表中的数据量非常庞大而索引建的过少或不合适的时候,使得经常发生全表扫描,最终应用系统会越来越慢,最终发生阻塞或死锁。 
    解决方法: 
    SQL语句中不要使用太复杂的关联多表的查询;使用“执行计划”对SQL语句进行分析,对于有全表扫描的SQL语句,建立相应的索引进行优化。 
    5.小结 
    总体上来说,产生内存溢出与锁表都是由于代码写的不好造成的,因此提高代码的质量是最根本的解决办法。有的人认为先把功能实现,有BUG时再在测试阶段进行修正,这种想法是错误的。正如一件产品的质量是在生产制造的过程中决定的,而不是质量检测时决定的,软件的质量在设计与编码阶段就已经决定了,测试只是对软件质量的一个验证,因为测试不可能找出软件中所有的BUG。