数据库并发学习总结

数据库并发控制



1.  在数据库中为什么要并发控制?


答:数据库是共享资源,通常有许多个事务同时在运行。当多个事务并发地存取数据库时就会产生同时读取和/或修改同一数据的情况。若对并发操作不加控制就可能会存取和存储不正确的数据,破坏数据库的一致性。所以数据库管理系统必须提供并发控制机制。


2 .并发操作可能会产生哪几类数据不一致?用什么方法能避免各种不一致的情况?


答:并发操作带来的数据不一致性包括三类:丢失修改、不可重复读和读“脏’夕数据。 ( l )丢失修改(lost update ) 两个事务 Tl 和T2读入同一数据并修改,T2提交的结果破坏了(覆盖了) Tl 提交的结果,导致 Tl 的修改被丢失。 ( 2 )不可重复读( Non 一 Repeatable Read ) 不可重复读是指事务 Tl 读取数据后,事务几执行更新操作,使 Tl 无法再现前一次读取结果。( 3 )读“脏”数据( Dirty Read ) 读“脏’夕数据是指事务 Tl 修改某一数据,并将其写回磁盘,事务几读取同一数据后, Tl 由于某种原因被撤销,这时 Tl 已修改过的数据恢复原值,几读到的数据就与数据库中的数据不一致,则几读到的数据就为“脏”数据,即不正确的数据。避免不一致性的方法和技术就是并发控制。最常用的技术是封锁技术。也可以用其他技术,例如在分布式数据库系统中可以采用时间戳方法来进行并发控制。


3 .什么是封锁?基本的封锁类型有几种?试述它们的含义。


答:封锁就是事务 T 在对某个数据对象例如表、记录等操作之前,先向系统发出请求,对其加锁。加锁后事务 T 就对该数据对象有了一定的控制,在事务 T 释放它的锁之前,其他的事务不能更新此数据对象。封锁是实现并发控制的一个非常重要的技术。


基本的封锁类型有两种:排它锁( Exclusive Locks ,简称 x 锁)和共享锁 ( Share Locks,简称 S 锁)。排它锁又称为写锁。若事务 T 对数据对象 A 加上 X 锁,则只允许 T 读取和修改 A ,其他任何事务都不能再对 A 加任何类型的锁,直到 T 释放 A 上的锁。这就保证了其他事务在 T 释放 A 上的锁之前不能再读取和修改 A 。共享锁又称为读锁。若事务 T 对数据对象 A 加上 S 锁,则事务 T 可以读 A但不能修改 A ,其他事务只能再对 A 加 S 锁,而不能加 X 锁,直到 T 释放 A 上的 S 锁。这就保证了其他事务可以读 A ,但在 T 释放 A 上的 S 锁之前不能对 A 做任何修改。


4 .如何用封锁机制保证数据的一致性?


答: DBMS 在对数据进行读、写操作之前首先对该数据执行封锁操作,例如下图中事务 Tl 在对 A 进行修改之前先对 A 执行 xock ( A ) ,即对 A 加 x 锁。这样,当几请求对 A 加 x 锁时就被拒绝,几只能等待 Tl 释放 A 上的锁后才能获得对 A 的 x 锁,这时它读到的 A 是 Tl 更新后的值,再按此新的 A 值进行运算。这样就不会丢失 Tl 的更新。


DBMS 按照一定的封锁协议,对并发操作进行控制,使得多个并发操作有序地执行,就可以避免丢失修改、不可重复读和读“脏’夕数据等数据不一致性。


5 .什么是活锁?什么是死锁?
答:
如果事务 Tl 封锁了数据 R ,事务几又请求封锁 R ,于是几等待。几也请求封锁 R ,当 Tl 释放了 R 上的封锁之后系统首先批准了几的请求,几仍然等待。然后几又请求封锁 R ,当几释放了 R 上的封锁之后系统又批准了几的请求 … … 几有可能永远等待,这就是活锁的情形。活锁的含义是该等待事务等待时间太长,似乎被锁住了,实际上可能被激活。如果事务 Tl 封锁了数据 Rl ,几封锁了数据凡,然后 Tl 又请求封锁几,因几已封锁了几,于是 Tl 等待几释放几上的锁。接着几又申请封锁 Rl ,因 Tl 已封锁了 Rl ,几也只能等待 Tl 释放 Rl 上的锁。这样就出现了 Tl 在等待几,而几又在等待 T }的局面, T }和几两个事务永远不能结束,形成死锁。


6 .试述活锁的产生原因和解决方法。


答:活锁产生的原因:当一系列封锁不能按照其先后顺序执行时,就可能导致一些事务无限期等待某个封锁,从而导致活锁。避免活锁的简单方法是采用先来先服务的策略。当多个事务请求封锁同一数据对象时,封锁子系统按请求封锁的先后次序对事务排队,数据对象上的锁一旦释放就批准申请队列中第一个事务获得锁。


11 .请给出检测死锁发生的一种方法,当发生死锁后如何解除死锁?


答:数据库系统一般采用允许死锁发生, DBMS 检测到死锁后加以解除的方法。 DBMS 中诊断死锁的方法与操作系统类似,一般使用超时法或事务等待图法。超时法是:如果一个事务的等待时间超过了规定的时限,就认为发生了死锁。超时法实现简单,但有可能误判死锁,事务因其他原因长时间等待超过时限时,系统会误认为发生了死锁。若时限设置得太长,又不能及时发现死锁发生。 DBMS 并发控制子系统检测到死锁后,就要设法解除。通常采用的方法是选择一个处理死锁代价最小的事务,将其撤消,释放此事务持有的所有锁,使其他事务得以继续运行下去。当然,对撤销的事务所执行的数据修改操作必须加以恢复。


 12 .什么样的并发调度是正确的调度?


答:可串行化( Serializable )的调度是正确的调度。可串行化的调度的定义:多个事务的并发执行是正确的,当且仅当其结果与按某一次序串行执行它们时的结果相同,称这种调度策略为可串行化的调度。


9 .设 Tl ,几,几是如下的 3 个事务:


Tl :A : = A + 2 ;


T2:A : = A * 2 ;


T3:A : = A **2 ; ( A <-A*A)


设 A 的初值为 0 。


( l )若这 3 个事务允许并行执行,则有多少可能的正确结果,请一一列举出来。


答 :A 的最终结果可能有 2 、 4 、 8 、 16 。因为串行执行次序有 Tl T2T3、 Tl T3T2、T2T1T3、T2T3Tl 、T3T1T2、T3T2 Tl 。对应的执行结果是 16 、 8 · 4 · 2 · 4 · 2 。


( 2 )请给出一个可串行化的调度,并给出执行结果


答:
最后结果 A 为 16 ,是可串行化的调度。


( 3 )请给出一个非串行化的调度,并给出执行结果。


答:最后结果 A 为 0 ,为非串行化的调度。


( 4 )若这 3 个事务都遵守两段锁协议,请给出一个不产生死锁的可串行化调度。


答:     


     ( 5 )若这 3 个事务都遵守两段锁协议,请给出一个产生死锁的调度。


答:     


11.试证明,若并发事务遵守两段锁协议,则对这些事务的并发调度是可串行化的。


证明:首先以两个并发事务 Tl 和T2为例,存在多个并发事务的情形可以类推。根据可串行化定义可知,事务不可串行化只可能发生在下列两种情况:


 ( l )事务 Tl 写某个数据对象 A ,T2读或写 A ;


( 2 )事务 Tl 读或写某个数据对象 A ,T2写 A 。


下面称 A 为潜在冲突对象。


设 Tl 和T2访问的潜在冲突的公共对象为{A1,A2 … , An }。不失一般性,假设这组潜在冲突对象中 X =(A 1 , A2 , … , Ai }均符合情况 1 。 Y ={A i + 1 , … , An }符合所情况( 2 )。


VX ∈ x , Tl 需要 XlockX ①


T2 需要 Slockx 或 Xlockx ②


 1 )如果操作 ① 先执行,则 Tl 获得锁,T2等待


由于遵守两段锁协议, Tl 在成功获得 x 和 Y 中全部对象及非潜在冲突对象的锁后,才会释放锁。


这时如果存在 w ∈ x 或 Y ,T2已获得 w 的锁,则出现死锁;否则, Tl 在对 x 、 Y 中对象全部处理完毕后,T2才能执行。这相当于按 Tl 、T2的顺序串行执行,根据可串行化定义, Tl 和几的调度是可串行化的。


2 )操作 ② 先执行的情况与( l )对称因此,若并发事务遵守两段锁协议,在不发生死锁的情况下,对这些事务的并发调度一定是可串行化的。证毕。


12 .举例说明,对并发事务的一个调度是可串行化的,而这些并发事务不一定遵守两段锁协议。


答:


13 .为什么要引进意向锁?意向锁的含义是什么?


答:引进意向锁是为了提高封锁子系统的效率。该封锁子系统支持多种封锁粒度。原因是:在多粒度封锁方法中一个数据对象可能以两种方式加锁 ― 显式封锁和隐式封锁。因此系统在对某一数据对象加锁时不仅要检查该数据对象上有无(显式和隐式)封锁与之冲突,还要检查其所有上级结点和所有下级结点,看申请的封锁是否与这些结点上的(显式和隐式)封锁冲突,显然,这样的检查方法效率很低。为此引进了意向锁。意向锁的含义是:对任一结点加锁时,必须先对它的上层结点加意向锁。例如事务 T 要对某个元组加 X 锁,则首先要对关系和数据库加 ix 锁。换言之,对关系和数据库加 ix 锁,表示它的后裔结点 ― 某个元组拟(意向)加 X 锁。引进意向锁后,系统对某一数据对象加锁时不必逐个检查与下一级结点的封锁冲突了。例如,事务 T 要对关系 R 加 X 锁时,系统只要检查根结点数据库和 R 本身是否已加了不相容的锁(如发现已经加了 ix ,则与 X 冲突),而不再需要搜索和检查 R 中的每一个元组是否加了 X 锁或 S 锁。


14 .试述常用的意向锁: IS 锁、 ix 锁、 SIX 锁,给出这些锁的相容矩阵。


答: IS锁:如果对一个数据对象加 IS 锁,表示它的后裔结点拟(意向)加 S 锁。例如,要对某个元组加 S 锁,则要首先对关系和数据库加 IS 锁


IX 锁:如果对一个数据对象加 ix 锁,表示它的后裔结点拟(意向功口 X 锁。例如,要对某个元组加 X 锁,则要首先对关系和数据库加 ix 锁。


SIX 锁:如果对一个数据对象加 SIX 锁,表示对它加 S 锁,再加 IX 锁,即 SIX = S + IX 。


相容矩阵:


15 .理解并解释下列术语的含义:封锁、活锁、死锁、排它锁、共享锁、并发事务的调度、可串行化的调度、两段锁协议。


答:(略,已经在上面有关习题中解答)


16 .试述你了解的某一个实际的 DBMS 产品的并发控制机制。


答:(略,参见简单介绍了有关 Oracle 的并发控制机制。
========

大数据量高并发的数据库优化

一、数据库结构的设计


    如果不能设计一个合理的数据库模型,不仅会增加客户端和服务器段程序的编程和维护的难度,而且将会影响系统实际运行的性能。所以,在一个系统开始实施之前,完备的数据库模型的设计是必须的。
    在一个系统分析、设计阶段,因为数据量较小,负荷较低。我们往往只注意到功能的实现,而很难注意到性能的薄弱之处,等到系统投入实际运行一段时间后,才发现系统的性能在降低,这时再来考虑提高系统性能则要花费更多的人力物力,而整个系统也不可避免的形成了一个打补丁工程。


    所以在考虑整个系统的流程的时候,我们必须要考虑,在高并发大数据量的访问情况下,我们的系统会不会出现极端的情况。(例如:对外统计系统在7月16日出现的数据异常的情况,并发大数据量的的访问造成,数据库的响应时间不能跟上数据刷新的速度造成。具体情况是:在日期临界时(00:00:00),判断数据库中是否有当前日期的记录,没有则插入一条当前日期的记录。在低并发访问的情况下,不会发生问题,但是当日期临界时的访问量相当大的时候,在做这一判断的时候,会出现多次条件成立,则数据库里会被插入多条当前日期的记录,从而造成数据错误。),数据库的模型确定下来之后,我们有必要做一个系统内数据流向图,分析可能出现的瓶颈。


    为了保证数据库的一致性和完整性,在逻辑设计的时候往往会设计过多的表间关联,尽可能的降低数据的冗余。(例如用户表的地区,我们可以把地区另外存放到一个地区表中)如果数据冗余低,数据的完整性容易得到保证,提高了数据吞吐速度,保证了数据的完整性,清楚地表达数据元素之间的关系。而对于多表之间的关联查询(尤其是大数据表)时,其性能将会降低,同时也提高了客户端程序的编程难度,因此,物理设计需折衷考虑,根据业务规则,确定对关联表的数据量大小、数据项的访问频度,对此类数据表频繁的关联查询应适当提高数据冗余设计但增加了表间连接查询的操作,也使得程序的变得复杂,为了提高系统的响应时间,合理的数据冗余也是必要的。设计人员在设计阶段应根据系统操作的类型、频度加以均衡考虑。
   另外,最好不要用自增属性字段作为主键与子表关联。不便于系统的迁移和数据恢复。对外统计系统映射关系丢失(******************)。


    原来的表格必须可以通过由它分离出去的表格重新构建。使用这个规定的好处是,你可以确保不会在分离的表格中引入多余的列,所有你创建的表格结构都与它们的实际需要一样大。应用这条规定是一个好习惯,不过除非你要处理一个非常大型的数据,否则你将不需要用到它。(例如一个通行证系统,我可以将USERID,USERNAME,USERPASSWORD,单独出来作个表,再把USERID作为其他表的外键)


表的设计具体注意的问题:


    1、数据行的长度不要超过8020字节,如果超过这个长度的话在物理页中这条数据会占用两行从而造成存储碎片,降低查询效率。
    2、能够用数字类型的字段尽量选择数字类型而不用字符串类型的(电话号码),这会降低查询和连接的性能,并会增加存储开销。这是因为引擎在处理查询和连接回逐个比较字符串中每一个字符,而对于数字型而言只需要比较一次就够了。


    3、对于不可变字符类型char和可变字符类型varchar 都是8000字节,char查询快,但是耗存储空间,varchar查询相对慢一些但是节省存储空间。在设计字段的时候可以灵活选择,例如用户名、密码等长度变化不大的字段可以选择CHAR,对于评论等长度变化大的字段可以选择VARCHAR。


    4、字段的长度在最大限度的满足可能的需要的前提下,应该尽可能的设得短一些,这样可以提高查询的效率,而且在建立索引的时候也可以减少资源的消耗。


二、查询的优化 


保证在实现功能的基础上,尽量减少对数据库的访问次数;通过搜索参数,尽量减少对表的访问行数,最小化结果集,从而减轻网络负担;能够分开的操作尽量分开处理,提高每次的响应速度;在数据窗口使用SQL时,尽量把使用的索引放在选择的首列;算法的结构尽量简单;在查询时,不要过多地使用通配符如SELECT * FROM T1语句,要用到几列就选择几列如:SELECT COL1,COL2 FROM T1;在可能的情况下尽量限制尽量结果集行数如:SELECT TOP 300 COL1,COL2,COL3 FROM T1,因为某些情况下用户是不需要那么多的数据的。   
在没有建索引的情况下,数据库查找某一条数据,就必须进行全表扫描了,对所有数据进行一次遍历,查找出符合条件的记录。在数据量比较小的情况下,也许看不出明显的差别,但是当数据量大的情况下,这种情况就是极为糟糕的了。
SQL语句在SQL SERVER中是如何执行的,他们担心自己所写的SQL语句会被SQL SERVER误解。比如: 
select * from table1 where name='zhangsan' and tID > 10000 
和执行: 
select * from table1 where tID > 10000 and name='zhangsan' 
一些人不知道以上两条语句的执行效率是否一样,因为如果简单的从语句先后上看,这两个语句的确是不一样,如果tID是一个聚合索引,那么后一句仅仅从表的10000条以后的记录中查找就行了;而前一句则要先从全表中查找看有几个name='zhangsan'的,而后再根据限制条件条件tID>10000来提出查询结果。 
事实上,这样的担心是不必要的。SQL SERVER中有一个“查询分析优化器”,它可以计算出where子句中的搜索条件并确定哪个索引能缩小表扫描的搜索空间,也就是说,它能实现自动优化。虽然查询优化器可以根据where子句自动的进行查询优化,但有时查询优化器就会不按照您的本意进行快速查询。 
在查询分析阶段,查询优化器查看查询的每个阶段并决定限制需要扫描的数据量是否有用。如果一个阶段可以被用作一个扫描参数(SARG),那么就称之为可优化的,并且可以利用索引快速获得所需数据。 
SARG的定义:用于限制搜索的一个操作,因为它通常是指一个特定的匹配,一个值的范围内的匹配或者两个以上条件的AND连接。形式如下: 
列名 操作符 <常数 或 变量> 或 <常数 或 变量> 操作符 列名 
列名可以出现在操作符的一边,而常数或变量出现在操作符的另一边。如: 
Name=’张三’ 
价格>5000 
5000<价格 
Name=’张三’ and 价格>5000 
如果一个表达式不能满足SARG的形式,那它就无法限制搜索的范围了,也就是SQL SERVER必须对每一行都判断它是否满足WHERE子句中的所有条件。所以一个索引对于不满足SARG形式的表达式来说是无用的。 
    所以,优化查询最重要的就是,尽量使语句符合查询优化器的规则避免全表扫描而使用索引查询。


具体要注意的:


1.应尽量避免在 where 子句中对字段进行 null 值判断,否则将导致引擎放弃使用索引而进行全表扫描,如:
select id from t where num is null
可以在num上设置默认值0,确保表中num列没有null值,然后这样查询:
select id from t where num=0


2.应尽量避免在 where 子句中使用!=或<>操作符,否则将引擎放弃使用索引而进行全表扫描。优化器将无法通过索引来确定将要命中的行数,因此需要搜索该表的所有行。


3.应尽量避免在 where 子句中使用 or 来连接条件,否则将导致引擎放弃使用索引而进行全表扫描,如:
select id from t where num=10 or num=20
可以这样查询:
select id from t where num=10
union all
select id from t where num=20


4.in 和 not in 也要慎用,因为IN会使系统无法使用索引,而只能直接搜索表中的数据。如:
select id from t where num in(1,2,3)
对于连续的数值,能用 between 就不要用 in 了:
select id from t where num between 1 and 3


5.尽量避免在索引过的字符数据中,使用非打头字母搜索。这也使得引擎无法利用索引。 
见如下例子: 
SELECT * FROM T1 WHERE NAME LIKE ‘%L%’ 
SELECT * FROM T1 WHERE SUBSTING(NAME,2,1)=’L’ 
SELECT * FROM T1 WHERE NAME LIKE ‘L%’ 
即使NAME字段建有索引,前两个查询依然无法利用索引完成加快操作,引擎不得不对全表所有数据逐条操作来完成任务。而第三个查询能够使用索引来加快操作。


6.必要时强制查询优化器使用某个索引,如在 where 子句中使用参数,也会导致全表扫描。因为SQL只有在运行时才会解析局部变量,但优化程序不能将访问计划的选择推迟到运行时;它必须在编译时进行选择。然而,如果在编译时建立访问计划,变量的值还是未知的,因而无法作为索引选择的输入项。如下面语句将进行全表扫描:
select id from t where num=@num
可以改为强制查询使用索引:
select id from t with(index(索引名)) where num=@num


7.应尽量避免在 where 子句中对字段进行表达式操作,这将导致引擎放弃使用索引而进行全表扫描。如:
SELECT * FROM T1 WHERE F1/2=100 
应改为: 
SELECT * FROM T1 WHERE F1=100*2


SELECT * FROM RECORD WHERE SUBSTRING(CARD_NO,1,4)=’5378’ 
应改为: 
SELECT * FROM RECORD WHERE CARD_NO LIKE ‘5378%’


SELECT member_number, first_name, last_name FROM members 
WHERE DATEDIFF(yy,datofbirth,GETDATE()) > 21 
应改为: 
SELECT member_number, first_name, last_name FROM members 
WHERE dateofbirth < DATEADD(yy,-21,GETDATE()) 
即:任何对列的操作都将导致表扫描,它包括数据库函数、计算表达式等等,查询时要尽可能将操作移至等号右边。


8.应尽量避免在where子句中对字段进行函数操作,这将导致引擎放弃使用索引而进行全表扫描。如:
select id from t where substring(name,1,3)='abc'--name以abc开头的id
select id from t where datediff(day,createdate,'2005-11-30')=0--‘2005-11-30’生成的id
应改为:
select id from t where name like 'abc%'
select id from t where createdate>='2005-11-30' and createdate<'2005-12-1'


9.不要在 where 子句中的“=”左边进行函数、算术运算或其他表达式运算,否则系统将可能无法正确使用索引。


10.在使用索引字段作为条件时,如果该索引是复合索引,那么必须使用到该索引中的第一个字段作为条件时才能保证系统使用该索引,否则该索引将不会被使用,并且应尽可能的让字段顺序与索引顺序相一致。


11.很多时候用 exists是一个好的选择:
elect num from a where num in(select num from b)
用下面的语句替换:
select num from a where exists(select 1 from b where num=a.num)


SELECT SUM(T1.C1)FROM T1 WHERE( 
(SELECT COUNT(*)FROM T2 WHERE T2.C2=T1.C2>0) 
SELECT SUM(T1.C1) FROM T1WHERE EXISTS( 
SELECT * FROM T2 WHERE T2.C2=T1.C2) 
两者产生相同的结果,但是后者的效率显然要高于前者。因为后者不会产生大量锁定的表扫描或是索引扫描。


如果你想校验表里是否存在某条纪录,不要用count(*)那样效率很低,而且浪费服务器资源。可以用EXISTS代替。如: 
IF (SELECT COUNT(*) FROM table_name WHERE column_name = 'xxx') 
可以写成: 
IF EXISTS (SELECT * FROM table_name WHERE column_name = 'xxx')


经常需要写一个T_SQL语句比较一个父结果集和子结果集,从而找到是否存在在父结果集中有而在子结果集中没有的记录,如: 
SELECT a.hdr_key FROM hdr_tbl a---- tbl a 表示tbl用别名a代替 
WHERE NOT EXISTS (SELECT * FROM dtl_tbl b WHERE a.hdr_key = b.hdr_key) 
SELECT a.hdr_key FROM hdr_tbl a 
LEFT JOIN dtl_tbl b ON a.hdr_key = b.hdr_key WHERE b.hdr_key IS NULL 
SELECT hdr_key FROM hdr_tbl 
WHERE hdr_key NOT IN (SELECT hdr_key FROM dtl_tbl) 
三种写法都可以得到同样正确的结果,但是效率依次降低。


12.尽量使用表变量来代替临时表。如果表变量包含大量数据,请注意索引非常有限(只有主键索引)。


13.避免频繁创建和删除临时表,以减少系统表资源的消耗。


14.临时表并不是不可使用,适当地使用它们可以使某些例程更有效,例如,当需要重复引用大型表或常用表中的某个数据集时。但是,对于一次性事件,最好使用导出表。


15.在新建临时表时,如果一次性插入数据量很大,那么可以使用 select into 代替 create table,避免造成大量 log ,以提高速度;如果数据量不大,为了缓和系统表的资源,应先create table,然后insert。


16.如果使用到了临时表,在存储过程的最后务必将所有的临时表显式删除,先 truncate table ,然后 drop table ,这样可以避免系统表的较长时间锁定。 


17.在所有的存储过程和触发器的开始处设置 SET NOCOUNT ON ,在结束时设置 SET NOCOUNT OFF 。无需在执行存储过程和触发器的每个语句后向客户端发送 DONE_IN_PROC 消息。


18.尽量避免大事务操作,提高系统并发能力。


19.尽量避免向客户端返回大数据量,若数据量过大,应该考虑相应需求是否合理。 


20. 避免使用不兼容的数据类型。例如float和int、char和varchar、binary和varbinary是不兼容的。数据类型的不兼容可能使优化器无法执行一些本来可以进行的优化操作。例如: 
SELECT name FROM employee WHERE salary > 60000 
在这条语句中,如salary字段是money型的,则优化器很难对其进行优化,因为60000是个整型数。我们应当在编程时将整型转化成为钱币型,而不要等到运行时转化。


21.充分利用连接条件,在某种情况下,两个表之间可能不只一个的连接条件,这时在 WHERE 子句中将连接条件完整的写上,有可能大大提高查询速度。 
例: 
SELECT SUM(A.AMOUNT) FROM ACCOUNT A,CARD B WHERE A.CARD_NO = B.CARD_NO 
SELECT SUM(A.AMOUNT) FROM ACCOUNT A,CARD B WHERE A.CARD_NO = B.CARD_NO AND A.ACCOUNT_NO=B.ACCOUNT_NO 
第二句将比第一句执行快得多。


22、使用视图加速查询 
把表的一个子集进行排序并创建视图,有时能加速查询。它有助于避免多重排序 操作,而且在其他方面还能简化优化器的工作。例如:
SELECT cust.name,rcvbles.balance,……other columns 
FROM cust,rcvbles 
WHERE cust.customer_id = rcvlbes.customer_id 
AND rcvblls.balance>0 
AND cust.postcode>“98000” 
ORDER BY cust.name


如果这个查询要被执行多次而不止一次,可以把所有未付款的客户找出来放在一个视图中,并按客户的名字进行排序: 
CREATE VIEW DBO.V_CUST_RCVLBES 
AS 
SELECT cust.name,rcvbles.balance,……other columns 
FROM cust,rcvbles 
WHERE cust.customer_id = rcvlbes.customer_id 
AND rcvblls.balance>0 
ORDER BY cust.name 
然后以下面的方式在视图中查询: 
SELECT * FROM V_CUST_RCVLBES 
WHERE postcode>“98000” 
视图中的行要比主表中的行少,而且物理顺序就是所要求的顺序,减少了磁盘I/O,所以查询工作量可以得到大幅减少。


23、能用DISTINCT的就不用GROUP BY 
SELECT OrderID FROM Details WHERE UnitPrice > 10 GROUP BY OrderID 
可改为: 
SELECT DISTINCT OrderID FROM Details WHERE UnitPrice > 10


24.能用UNION ALL就不要用UNION 
UNION ALL不执行SELECT DISTINCT函数,这样就会减少很多不必要的资源 


35.尽量不要用SELECT INTO语句。 
SELECT INOT 语句会导致表锁定,阻止其他用户访问该表。


    上面我们提到的是一些基本的提高查询速度的注意事项,但是在更多的情况下,往往需要反复试验比较不同的语句以得到最佳方案。最好的方法当然是测试,看实现相同功能的SQL语句哪个执行时间最少,但是数据库中如果数据量很少,是比较不出来的,这时可以用查看执行计划,即:把实现相同功能的多条SQL语句考到查询分析器,按CTRL+L看查所利用的索引,表扫描次数(这两个对性能影响最大),总体上看询成本百分比即可。 


三、算法的优化


尽量避免使用游标,因为游标的效率较差,如果游标操作的数据超过1万行,那么就应该考虑改写。.使用基于游标的方法或临时表方法之前,应先寻找基于集的解决方案来解决问题,基于集的方法通常更有效。与临时表一样,游标并不是不可使用。对小型数据集使用 FAST_FORWARD 游标通常要优于其他逐行处理方法,尤其是在必须引用几个表才能获得所需的数据时。在结果集中包括“合计”的例程通常要比使用游标执行的速度快。如果开发时间允许,基于游标的方法和基于集的方法都可以尝试一下,看哪一种方法的效果更好。
  游标提供了对特定集合中逐行扫描的手段,一般使用游标逐行遍历数据,根据取出的数据不同条件进行不同的操作。尤其对多表和大表定义的游标(大的数据集合)循环很容易使程序进入一个漫长的等特甚至死机。 
  在有些场合,有时也非得使用游标,此时也可考虑将符合条件的数据行转入临时表中,再对临时表定义游标进行操作,可时性能得到明显提高。
(例如:对内统计第一版)
封装存储过程


四、建立高效的索引


  创建索引一般有以下两个目的:维护被索引列的唯一性和提供快速访问表中数据的策略。大型数据库有两种索引即簇索引和非簇索引,一个没有簇索引的表是按堆结构存储数据,所有的数据均添加在表的尾部,而建立了簇索引的表,其数据在物理上会按照簇索引键的顺序存储,一个表只允许有一个簇索引,因此,根据B树结构,可以理解添加任何一种索引均能提高按索引列查询的速度,但会降低插入、更新、删除操作的性能,尤其是当填充因子(Fill Factor)较大时。所以对索引较多的表进行频繁的插入、更新、删除操作,建表和索引时因设置较小的填充因子,以便在各数据页中留下较多的自由空间,减少页分割及重新组织的工作。 
索引是从数据库中获取数据的最高效方式之一。95% 的数据库性能问题都可以采用索引技术得到解决。作为一条规则,我通常对逻辑主键使用唯一的成组索引,对系统键(作为存储过程)采用唯一的非成组索引,对任何外键列[字段]采用非成组索引。不过,索引就象是盐,太多了菜就咸了。你得考虑数据库的空间有多大,表如何进行访问,还有这些访问是否主要用作读写。 
实际上,您可以把索引理解为一种特殊的目录。微软的SQL SERVER提供了两种索引:聚集索引(clustered index,也称聚类索引、簇集索引)和非聚集索引(nonclustered index,也称非聚类索引、非簇集索引)。下面,我们举例来说明一下聚集索引和非聚集索引的区别: 
其实,我们的汉语字典的正文本身就是一个聚集索引。比如,我们要查“安”字,就会很自然地翻开字典的前几页,因为“安”的拼音是“an”,而按照拼音排序汉字的字典是以英文字母“a”开头并以“z”结尾的,那么“安”字就自然地排在字典的前部。如果您翻完了所有以“a”开头的部分仍然找不到这个字,那么就说明您的字典中没有这个字;同样的,如果查“张”字,那您也会将您的字典翻到最后部分,因为“张”的拼音是“zhang”。也就是说,字典的正文部分本身就是一个目录,您不需要再去查其他目录来找到您需要找的内容。 
我们把这种正文内容本身就是一种按照一定规则排列的目录称为“聚集索引”。 
如果您认识某个字,您可以快速地从自动中查到这个字。但您也可能会遇到您不认识的字,不知道它的发音,这时候,您就不能按照刚才的方法找到您要查的字,而需要去根据“偏旁部首”查到您要找的字,然后根据这个字后的页码直接翻到某页来找到您要找的字。但您结合“部首目录”和“检字表”而查到的字的排序并不是真正的正文的排序方法,比如您查“张”字,我们可以看到在查部首之后的检字表中“张”的页码是672页,检字表中“张”的上面是“驰”字,但页码却是63页,“张”的下面是“弩”字,页面是390页。很显然,这些字并不是真正的分别位于“张”字的上下方,现在您看到的连续的“驰、张、弩”三字实际上就是他们在非聚集索引中的排序,是字典正文中的字在非聚集索引中的映射。我们可以通过这种方式来找到您所需要的字,但它需要两个过程,先找到目录中的结果,然后再翻到您所需要的页码。 
我们把这种目录纯粹是目录,正文纯粹是正文的排序方式称为“非聚集索引”。 
进一步引申一下,我们可以很容易的理解:每个表只能有一个聚集索引,因为目录只能按照一种方法进行排序。


(一)何时使用聚集索引或非聚集索引 
下面的表总结了何时使用聚集索引或非聚集索引(很重要)。 
动作描述 使用聚集索引 使用非聚集索引 
列经常被分组排序 应 应 
返回某范围内的数据 应 不应 
一个或极少不同值 不应 不应 
小数目的不同值 应 不应 
大数目的不同值 不应 应 
频繁更新的列 不应 应 
外键列 应 应 
主键列 应 应 
频繁修改索引列 不应 应


事实上,我们可以通过前面聚集索引和非聚集索引的定义的例子来理解上表。如:返回某范围内的数据一项。比如您的某个表有一个时间列,恰好您把聚合索引建立在了该列,这时您查询2004年1月1日至2004年10月1日之间的全部数据时,这个速度就将是很快的,因为您的这本字典正文是按日期进行排序的,聚类索引只需要找到要检索的所有数据中的开头和结尾数据即可;而不像非聚集索引,必须先查到目录中查到每一项数据对应的页码,然后再根据页码查到具体内容。


(二)结合实际,谈索引使用的误区


理论的目的是应用。虽然我们刚才列出了何时应使用聚集索引或非聚集索引,但在实践中以上规则却很容易被忽视或不能根据实际情况进行综合分析。下面我们将根据在实践中遇到的实际问题来谈一下索引使用的误区,以便于大家掌握索引建立的方法。 
1、主键就是聚集索引 
这种想法笔者认为是极端错误的,是对聚集索引的一种浪费。虽然SQL SERVER默认是在主键上建立聚集索引的。 
通常,我们会在每个表中都建立一个ID列,以区分每条数据,并且这个ID列是自动增大的,步长一般为1。我们的这个办公自动化的实例中的列Gid就是如此。此时,如果我们将这个列设为主键,SQL SERVER会将此列默认为聚集索引。这样做有好处,就是可以让您的数据在数据库中按照ID进行物理排序,但笔者认为这样做意义不大。 
显而易见,聚集索引的优势是很明显的,而每个表中只能有一个聚集索引的规则,这使得聚集索引变得更加珍贵。 
从我们前面谈到的聚集索引的定义我们可以看出,使用聚集索引的最大好处就是能够根据查询要求,迅速缩小查询范围,避免全表扫描。在实际应用中,因为ID号是自动生成的,我们并不知道每条记录的ID号,所以我们很难在实践中用ID号来进行查询。这就使让ID号这个主键作为聚集索引成为一种资源浪费。其次,让每个ID号都不同的字段作为聚集索引也不符合“大数目的不同值情况下不应建立聚合索引”规则;当然,这种情况只是针对用户经常修改记录内容,特别是索引项的时候会负作用,但对于查询速度并没有影响。 
在办公自动化系统中,无论是系统首页显示的需要用户签收的文件、会议还是用户进行文件查询等任何情况下进行数据查询都离不开字段的是“日期”还有用户本身的“用户名”。 
通常,办公自动化的首页会显示每个用户尚未签收的文件或会议。虽然我们的where语句可以仅仅限制当前用户尚未签收的情况,但如果您的系统已建立了很长时间,并且数据量很大,那么,每次每个用户打开首页的时候都进行一次全表扫描,这样做意义是不大的,绝大多数的用户1个月前的文件都已经浏览过了,这样做只能徒增数据库的开销而已。事实上,我们完全可以让用户打开系统首页时,数据库仅仅查询这个用户近3个月来未阅览的文件,通过“日期”这个字段来限制表扫描,提高查询速度。如果您的办公自动化系统已经建立的2年,那么您的首页显示速度理论上将是原来速度8倍,甚至更快。


2、只要建立索引就能显著提高查询速度 
事实上,我们可以发现上面的例子中,第2、3条语句完全相同,且建立索引的字段也相同;不同的仅是前者在fariqi字段上建立的是非聚合索引,后者在此字段上建立的是聚合索引,但查询速度却有着天壤之别。所以,并非是在任何字段上简单地建立索引就能提高查询速度。
从建表的语句中,我们可以看到这个有着1000万数据的表中fariqi字段有5003个不同记录。在此字段上建立聚合索引是再合适不过了。在现实中,我们每天都会发几个文件,这几个文件的发文日期就相同,这完全符合建立聚集索引要求的:“既不能绝大多数都相同,又不能只有极少数相同”的规则。由此看来,我们建立“适当”的聚合索引对于我们提高查询速度是非常重要的。


3、把所有需要提高查询速度的字段都加进聚集索引,以提高查询速度 
上面已经谈到:在进行数据查询时都离不开字段的是“日期”还有用户本身的“用户名”。既然这两个字段都是如此的重要,我们可以把他们合并起来,建立一个复合索引(compound index)。 
很多人认为只要把任何字段加进聚集索引,就能提高查询速度,也有人感到迷惑:如果把复合的聚集索引字段分开查询,那么查询速度会减慢吗?带着这个问题,我们来看一下以下的查询速度(结果集都是25万条数据):(日期列fariqi首先排在复合聚集索引的起始列,用户名neibuyonghu排在后列) 
我们可以看到如果仅用聚集索引的起始列作为查询条件和同时用到复合聚集索引的全部列的查询速度是几乎一样的,甚至比用上全部的复合索引列还要略快(在查询结果集数目一样的情况下);而如果仅用复合聚集索引的非起始列作为查询条件的话,这个索引是不起任何作用的。当然,语句1、2的查询速度一样是因为查询的条目数一样,如果复合索引的所有列都用上,而且查询结果少的话,这样就会形成“索引覆盖”,因而性能可以达到最优。同时,请记住:无论您是否经常使用聚合索引的其他列,但其前导列一定要是使用最频繁的列。


(三)其他注意事项 
“水可载舟,亦可覆舟”,索引也一样。索引有助于提高检索性能,但过多或不当的索引也会导致系统低效。因为用户在表中每加进一个索引,数据库就要做更多的工作。过多的索引甚至会导致索引碎片。 
所以说,我们要建立一个“适当”的索引体系,特别是对聚合索引的创建,更应精益求精,以使您的数据库能得到高性能的发挥
 
好像没有提到高并发的解决方案


索引的使用,学习了
 
高并发都用NoSQL了,内存数据库是解决高并发实时性能的方向,
这里有国人开源的高并发框架 www. 8088net .com
========

怎么解决数据库并发的问题



1.用一个标识,在选择那张票的时候先用
(Update  表  set  票flag=‘占用了!’  where  票flag=‘未占用’  and  ........)
这样是保险的,不可能存在并发问题,这就牵扯到sql锁机制问题了,你可以测试一下,其实sql中update是先查询出然后删除再添加,但由于使用了update,过程中就自动加锁了,很方便吧
2.加锁。
Microsoft®  SQL  Server™  2000  使用锁定确保事务完整性和数据库一致性。锁定可以防止用户读取正在由其他用户更改的数据,并可以防止多个用户同时更改相同数据。如果不使用锁定,则数据库中的数据可能在逻辑上不正确,并且对数据的查询可能会产生意想不到的结果。
虽然  SQL  Server  自动强制锁定,但可以通过了解锁定并在应用程序中自定义锁定来设计更有效的应用程序。
========

关于数据库的并发性的一个解决方案



我先把流程说出来,,比如修改一个记录:
当用户点修改时,从数据库读出数据并显示到编辑菜单中,然后再编辑数据,再点确定保存到数据库中。如果多个用户,当A用户点修改到保存该数据这一时间段,,B用户不能修改,,这个好像不能用事务来做吧,,,大家给个解决方案,,但是我觉得还有点矛盾。当这一时间段用户能修改,那么对于A用户来说数据就已经不一致了,A用户看到的是B用户没有修改前的数据,如果不能修改,也不现实,,如果B用户点了修改还没点确定,,然后用户出去办急事或WC,,那A用户不是一直等,,,
网上经常举卖车票的例子,当售票员A听到买票人后开始查询,发现有2张,,买票人正好要2张,,在查询和售出这一时间段,如果其它窗口不能卖,,好像不太现实,,有时这一时间段还是很长的,,一分钟左右吧,,其它10多个窗口窗口不能卖这一车次的不是很要命,并且每个窗口都会差不多占一车次的查询,,如果在查询到确定出售这一价段其它窗口可以卖,,也出现问题了,,售票员A查询发现剩两张,,告诉买票的人,正好剩两张,,这时客户确定要,如果在这一时间段,其它窗口卖一张,,那在确定卖出去时将会失败,难道要跟买票人解释。,,这问题其它也困我很久了,,谢谢大家给我一个解决方案,,也许是我想错了,,在线等


SQL的锁机制
一. 为什么要引入锁
多个用户同时对数据库的并发操作时会带来以下数据不一致的问题:
丢失更新
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)  设置事务隔离级别。
参见 http://expert.csdn.net/Expert/topic/1785/1785314.xml?temp=.3050501


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 code
楼主参考下这个...悲观锁
 
悲观锁定解决方案
我们只要对上边的代码做微小的改变就可以实现悲观的锁定.
 
declare @CardNo varchar(20)
Begin Tran
 
       --  选择一张未使用的卡
        select top 1 @CardNo=F_CardNo
        from Card   with (UPDLOCK)  where F_Flag=0
         
        --  延迟50秒,模拟并发访问.
        waitfor delay '000:00:50'
 
       --  把刚才选择出来的卡进行注册.
 
        update Card
        set F_Name=user,
            F_Time=getdate(),
            F_Flag=1
        where F_CardNo=@CardNo
 
commit
 
注意其中的区别了吗?with(updlock),是的,我们在查询的时候使用了with (UPDLOCK)选项,在查询记录的时候我们就对记录加上了更新锁,表示我们即将对次记录进行更新.注意更新锁和共享锁是不冲突的,也就是其他用户还可以查询此表的内容,但是和更新锁和排它锁是冲突的.所以其他的更新用户就会阻塞.如果我们在另外一个窗口执行此代码,同样不加waifor delay子句.两边执行完毕后,我们发现成功的注册了两张卡.可能我们已经发现了悲观锁定的缺点:当一个用户进行更新的事务的时候,其他更新用户必须排队等待,即使那个用户更新的不是同一条记录.


到底什么时候开始其它用户不能操作,A用户点击修改时还是用户点确定重新去读一次数据库时,


SQL code
--这里还有一个~ 之前保存的 - -出处在哪没有记录...很不好意思 作者请骂我吧...
 
乐观锁定解决方案
--  首先我们在Card表里边加上一列F_TimeStamp 列,该列是varbinary(8)类型.但是在更新的时候这个值会自动增长.
 
alter table Card add  F_TimeStamp timestamp not null
  
--  悲观锁定
declare @CardNo varchar(20)
declare @timestamp varbinary(8)
declare @rowcount int
 
Begin Tran
 
       --  取得卡号和原始的时间戳值
        select top 1 @CardNo=F_CardNo,
                     @timestamp=F_TimeStamp
        from Card
        where F_Flag=0
         
        --  延迟50秒,模拟并发访问.
        waitfor delay '000:00:50'
 
        --  注册卡,但是要比较时间戳是否发生了变化.如果没有发生变化.更新成功.如果发生变化,更新失败.
 
        update Card
        set F_Name=user,
            F_Time=getdate(),
            F_Flag=1
        where F_CardNo=@CardNo and F_TimeStamp=@timestamp
        set @rowcount=@@rowcount
        if @rowcount=1
        begin
                print '更新成功!'
                commit
        end
        else if @rowcount=0
        begin
                if exists(select 1 from Card where F_CardNo=@CardNo)
                begin
                        print '此卡已经被另外一个用户注册!'
                        rollback tran
                end
                else
                begin
                        print '并不存在此卡!'
                        rollback tran
                end
        end
 
在另外一个窗口里边执行没有waitfor的代码,注册成功后,返回原来的窗口,我们就会发现到时间后它显示的提示是此卡以被另外一个用户注册的提示.很明显,这样我们也可以避免两个用户同时注册一张卡的现象的出现.同时,使用这种方法的另外一个好处是没有使用更新锁,这样增加的系统的并发处理能力.


加个rowversion字段,每当数据被修改时,此字段会被UPDATE,
保存时,判断记录是否已经被修改。


看来LZ没有做过实际案例啊,现在我给你透露下实际售票系统中的一个方案例子:


   首先没有那么复杂的锁,实际应用会尽量从业务角度考虑避免冲突:


   实际售票系统是这样: 
 
           1.售票中,"座位号" 才是竞争资源;
           2.售票中,查看票是不发生锁号的.
           3.售票中,有个选票(选座位号)的动作,选座位号确定时,才发生锁号(即锁住改作为号,即使这锁号,也只是修改标记,表示自己暂时锁住);
           4.等客户交钱后,就确定提交交易完成,这时候,就成为售出该票了(当然,被锁的号,要修改为对应的已售标记,及其他流程操作).


从这个过程看,几乎没有那么多冲突出现(只有选号时,有可能已被别人选了,这也应该知道的,可以另选号),这就是方案.


乐观锁定解决方案


--  首先我们在Card表里边加上一列F_TimeStamp 列,该列是varbinary(8)类型.但是在更新的时候这个值会自动增长.


alter table Card add  F_TimeStamp timestamp not null


--  悲观锁定
declare @CardNo varchar(20)
declare @timestamp varbinary(8)
declare @rowcount int


Begin Tran


       --…




你这是当点确定后再去判断是否卡否已被注册了嘛,,我想知道车票出售系统或大型网络版的管理系统的解决方案


利用小粒度锁可以尽量避免冲突,分析好你的业务,没多少问题。如7楼所言,一般的情况下,锁使用并


看来LZ没有做过实际案例啊,现在我给你透露下实际售票系统中的一个方案例子:


首先没有那么复杂的锁,实际应用会尽量从业务角度考虑避免冲突:


实际售票系统是这样:


1.售票中,"座位号" 才是竞争资源;
2.售票中,查看票是不发生锁号的.
3.售票中,有个选票(选座位号)的动作,选座位号确定时,才发生锁号(即锁住改作为号,即使这锁号,也只是修改标记,表示自己暂时锁住);
4.等客户交钱后,就确定提交交易完成,这时候,就成为售出该…


同意,就是这个意思


看来LZ没有做过实际案例啊,现在我给你透露下实际售票系统中的一个方案例子: 


  首先没有那么复杂的锁,实际应用会尽量从业务角度考虑避免冲突: 


  实际售票系统是这样: 


          1.售票中,"座位号" 才是竞争资源; 
          2.售票中,查看票是不发生锁号的. 
          3.售票中,有个选票(选座位号)的动作,选座位号确定时,才发生锁号(即锁住改作为号,即使这锁号,也只是修改标记,表示自己暂时锁住); 
          4.等…


谢谢,,,,,是不是在查看时去修改数据库的记录的一个标记,,当查看完了(取消或者售完票后)再将这个标记修改回去,,,


有谁知道怎样导入Excel何导入Word


SQL code


begin tran  
set transaction isolation level serializable 
update zjk set zjye =zjye-100 where zjzh ='1234567890'
commit tran  


以上代码,使用隔离级别serializable ,即1语句不能读取已由其他事务修改但尚未提交的数据。
2任何其他事务都不能在当前事务完成之前修改由当前事务读取的数据。
3在当前事务完成之前,其他事务不能使用当前事务中任何语句读取的键值插入新行。
参考http://msdn.microsoft.com/zh-cn/express/ms173763(SQL.90).aspx


加个rowversion字段,每当数据被修改时,此字段会被UPDATE, 
保存时,判断记录是否已经被修改。


又来了,,我问的是解决方案,,不是技术,,,


用7楼的方案就行了,
查询不锁,进一步确认时临时锁,确认买客户交钱后更新已售,否则取消锁


你的问题跟火车票不太一样的。


对于你的问题,需要解决的是如何处理更新冲突


传统方式,都是用 update XXXX set xxx where 主键=XXX
而使用冲突检查,则需要用 update XXXX set xxx where 主键=XXX and 修改过的字段1=原始值 and 修改过的字段2=原始值……


如果update没有更新数据,则这条数据不是被其它用户修改了,就是被删除了


看来LZ没有做过实际案例啊,现在我给你透露下实际售票系统中的一个方案例子: 


  首先没有那么复杂的锁,实际应用会尽量从业务角度考虑避免冲突: 


  实际售票系统是这样: 


          1.售票中,"座位号" 才是竞争资源; 
          2.售票中,查看票是不发生锁号的. 
          3.售票中,有个选票(选座位号)的动作,选座位号确定时,才发生锁号(即锁住改作为号,即使这锁号,也只是修改标记,表示自己暂时锁住); 
          4.等…


关键是第三步。我理解买票的时候虽然外面只是一个买入键的敲击动作,但后台应该是分为1、锁定当前记录,2、锁定成功后修改该票属性 两个动作的。只有第一个动作成功,才能做第二个动作。
我倾向于不是用SQL语句来锁住某行,而是在该行有某字段,修改成功就表示锁定。锁表对性能有影响。


我也觉得应该是第三步,,但我细节我不赞成,,我认为,选票时,锁定该记录(不一定非要用锁,可以加一个字段锁定,此时将它改为锁定状态,)当客户交钱提交交易完成 时,再改该记录的已售状态,当取消交易时,,再把锁定改为非锁定状态,,,不知道理解对没


这个问题也遇到过


如果不用锁,,去改锁定字段时,好像也有问题,,当去修改锁定字段为锁定状态时,,此时要是机了重启了,,那不是一直在锁定


  "如果不用锁,,去改锁定字段时,好像也有问题,,当去修改锁定字段为锁定状态时,,此时要是机了重启了,,那不是一直在锁定"


说的没错,既然是两阶段提交,自然有可能发生意外在中间过程中,


象你说的中途死机了,这个需要解决的,实际中当然会考虑周全的,有另外解决之道.
所以说,这是个工程,有系列问题要解决. 


如果是死机,可以有方案解决(这个可以自己考虑,各公司可能策略不同),简单的方法是, 客户端有缓存日志记录,如果死机,启动的时候,自然会知道上次未完成的事情; 当然,如果有好方案也可以在后台来解决.总之这个就很多途径.


SQL code
rowversion 字段
在更新前获取该字段的值,更新是在比较该值是否相当。OK。


似乎也可以理解为事物的ID号。


七楼的又来了,,能不能留个QQ号嘛,,,我想知道细节,,,,


不知道 '伽楠居士' 是研究,还是要开发实际业务系统.


   QQ:16258699 (不一定实时都在)


开发实际的业务系统


合格各方的后


1、查询时不需要锁;
2、执行时需要验证,可在一个事务中处理;
即真正需要解决的是在执行阶段,表象为:
查询的时候,票还在,出票瞬间,可能已经没有票了,即交易不成功。
售票系统最好不要加锁,耗资源且出了异常需要专门处理(手动释放锁)


设计多一个标志字段,在“当用户点修改时”,读出数据(读未标志记录)同时修改标志,表示“我正在修改这条记录,其它人不能读”
如果编辑后保存,保存就可以了
如果编辑后取消,将标志改回,表示“我放弃这条记录,其它人可以读”


这种标志是常用的设计


http://jone33.download.csdn.net/   资源不错,文档很多。


谢谢大家,,结了,
========

数据库 事务并发控制

事务是一个逻辑工作单元,SQL Server 2005 提供了几种自动的可以通过编程来完成的机制,包括事务日志、SQL事务控制语句,以及事务处理运行过程中通过锁定保证数据完整性的机制。当用户对数据库并发访问时,为了确保事务完整性和数据库一致性,需要使用锁定。事务和锁是两个紧密联系的概念。通过事务、批和锁的使用,还可以监测系统,以及优化物理数据库。作业是一种多步执行的任务。


本章主要介绍 SQL Server 2005数据库系统的事务和锁的基本概念,事务、批、锁的创建和使用,通过事务、批、锁监测系统和优化物理数据库的操作,以及作业的设置。
                                                                             12.1 事物的基本概念和SQL Server 2005事物处理
事务和存储过程类似,由一系列T-SQL语句组成,是 SQL Server 2005系统的执行单元。本节主要介绍 SQL Server 2005中事务的概念,以及事务的创建、使用。


12.1.1 事务概述
关系型数据库有4个显著的特征:安全性、完整性、检测性和并发性。数据库的安全性就是要保证数据库数据的安全,防止未授权用户随意修改数据库中的数据,确保数据的安全。完整性是数据库的一个重要特征,也是保证数据库中的数据切实有效、防止错误、实现商业规则的一种重要机制。在数据库中,区别所保存的数据是无用的垃圾还是有价值的信息,主要是依据数据库的完整性是否健全,即实体完整性、域完整性和参考完整性。对任何发现影响系统性能的因素和瓶颈,采取切合实际的策略,解决问题,提高系统的性能。并发性是用来解决多个用户对同一数据进行操作时的问题。特别是对于网络数据库来说,这个特点更加突出。提高数据库的处理速度,单单依靠提高计算机的物理速度是不够的,还必须充分考虑数据库的并发性问题,提高数据库并发性的效率。


那么如何保证并发性呢?在 SQL Server 2005中,通过使用事务和锁机制,可以解决数据库的并发性问题。


在 SQL Server 2005中,事务要求处理时必须满足ACID原则,即原子性(A)、一致性(C)、隔离性(I)和持久性(D)。


原子性:原子性也称为自动性,是指事务必须执行一个完整的工作,要么执行全部数据的操作,要么全部不执行。


一致性:一致性是指当事务完成时,必须所有的数据具有一致的状态。


隔离性


隔离性:也称为独立性,是指并行事务的修改必须与其他并行事务的修改相互独立。一个事务处理数据,要么是其他事务执行之前的状态,要么是其他事务执行之后的状态,但不能处理其他正在处理的数据。


持久性


持久性:是指当一个事务完成之后,将影响永久性地存于系统中,即事务的操作将写入数据库中。


事务的这种机制保证了一个事务或者提交后成功执行,或者提交后失败回滚,二者必居其一,因此,事务对数据的修改具有可恢复性,即当事务失败时,它对数据的修改都会恢复到该事务执行前的状态。而使用一般的批处理,则有可能出现有的语句被执行,而另一些语句没有被执行的情况,从而有可能造成数据不一致。


事务开始之后,事务所有的操作都陆续写到事务日志中。这些任务操作在事务日志中记录一个标志,用于表示执行了这种操作,当取消这种事务时,系统自动执行这种操作的反操作,保证系统的一致性。系统自动生成一个检查点机制,这个检查点周期地发生。检查点的周期是系统根据用户定义的时间间隔和系统活动的频度由系统自动计算出来的时间间隔。检查点周期地检查事物日志,如果在事务日志中,事务全部完成,那么检查点将事务提交到数据库中,并且在事务日志中做一个检查点提交标记。如果在事务日志中,事务没有完成,那么检查点将事务日志中的事务不提交到数据库中,并且在事务日志中做一个检查点未提交标记。


12.1.2 事务的类型
根据事务的设置、用途的不同,SQL Server 2005将事务分为多种类型。


1. 根据系统的设置分类
根据系统的设置,SQL Server 2005将事务分为两种类型:系统提供的事务和用户定义的事务,分别简称为系统事务和用户定义事务。


(1)系统事务
系统提供的事务是指在执行某些语句时,一条语句就是一个事务。但是要明确,一条语句的对象既可能是表中的一行数据,也可能是表中的多行数据,甚至是表中的全部数据。


因此,只有一条语句构成的事务也可能包含了多行数据的处理。


系统提供的事务语句如下:


ALTER TABLE 、CREATE、DELETE、DROP、FETCH、GRANT、INSERT、OPEN、REBOKE、SELECT、UPDATE、TRUNCATE TABLE


这些语句本身就构成了一个事务。


例12-1使用CREATE TABLE创建一个表。


      CREATE TABLE student


            (id CHAR(10),


            name CHAR(6),


            sex CHAR(2)


            )


说明:这条语句本身就构成了一个事务。这条语句由于没有使用条件限制,那么这条语句就是创建包含3个列的表。要么创建全部成功,要么全部失败。


(2)用户定义事务
在实际应用中,大多数的事务处理采用了用户定义的事务来处理。在开发应用程序时,可以使用BEGIN TRANSACTION语句来定义明确的用户定义的事务。在使用用户定义的事务时,一定要注意事务必须有明确的结束语句来结束。如果不使用明确的结束语句来结束,那么系统可能把从事务开始到用户关闭连接之间的全部操作都作为一个事务来对待。事务的明确结束可以使用两个语句中的一个:COMMIT语句和ROLLBACK语句。COMMIT语句是提交语句,将全部完成的语句明确地提交到数据库中。ROLLBACK语句是取消语句,该语句将事务的操作全部取消,即表示事务操作失败。


还有一种特殊的用户定义的事务,这就是分布式事务。例12-1事务是在一个服务器上的操作,其保证的数据完整性和一致性是指一个服务器上的完整性和一致性。但是,如果一个比较复杂的环境,可能有多台服务器,那么要保证在多台服务器环境中事务的完整性和一致性,就必须定义一个分布式事务。在这个分布式事务中,所有的操作都可以涉及对多个服务器的操作,当这些操作都成功时,那么所有这些操作都提交到相应服务器的数据库中,如果这些操作中有一个操作失败,那么这个分布式事务中的全部操作都将被取消。


2. 根据运行模式分类
根据运行模式,SQL Server 2005将事务分为4种类型:自动提交事务、显示事务、隐式事务和批处理级事务。


(1)自动提交事务
自动提交事务是指每条单独的语句都是一个事务。


(2)显式事务
显式事务指每个事务均以BEGIN TRANSACTION语句显式开始,以COMMIT或ROLLBACK语句显示结束。


(3)隐式事务
隐式事务指在前一个事务完成时新事务隐式启动,但每个事务仍以COMMIT或ROLLBACK语句显式完成。


(4)批处理级事务
该事务只能应用于多个活动结果集(MARS),在MARS会话中启动的T-SQL显式或隐式事务变为批处理级事务。当批处理完成时,没有提交或回滚的批处理级事务自动由SQL Server语句集合分组后形成单个的逻辑工作单元。


3. 事务处理语句
BEGIN TRANSAVTION语句。


COMMIT TRANSACTION语句。


ROLLBACK TRANSACTION语句。


SAVE TRANSACTION语句。


(1)BEGIN TRANSECTION 语句
BEGIN TRANSACTIONȠ语句定义渀个显式本地事务的起点,即事务纄开始。其语法格式为:


bɅgIN {⁔䉒AN|TɒࡁŎ䁓сGTM_ၞ}


[{tɲanѳactioɮ_Ůamu 0XٛՐхRLIN䁋 "mailuo:|Àtran_namၥ_╶aࡶiũb聬e"    聼@tੲan_nšme_vasiible}


{WIၔࡈ MARK[‘leѳcriɰtin’}}


]⁛?]


说昞゙


T͓AɎSACŔION关键字可仧缩写TRAN㈂*Trcnsac|恩on_namg亏务唍,PTREN_჎EME_warieble是用户定义盄㈁含有敨事勡名秴的变量,该变量必顿是字符数据类型㐂ȍ≗ࡉɔX耠⁍ARK指定在查志中标记事튡,d䁥skrmpᅴion是描述该栗记的字符串㄂


(2)COMMIT URÅN[聉CTIOÎቨ误句
C_MOIT єRANSKCࡔMON语句标志一个爐功的隐缏事务樞显式享嚡皌结束。啶语法格式人:


COMMIU{TRAŎټTSANѓACTION}śtò䁡Ѯsactũonɟnemѥ|ࡀѴran_naꁭe_Ѷarkabɮ补ѝ]ਜ਼Ļ]


诶明:仅当事务被引用所有数据的鄻辑都正籮时,T-SQL语句才庴发出COMMIT"T⁒ANS䁁C⁔IOŎ(命滤あ当在嵍套事务中使用时,妅部事务皤提交并不释放资澐或使其修改成为縸久修改㐂只有在提令了外部事务旷,数歮修改扭텷有永久悧,而且资源捍会被释放。当A@UӒaN[OࡕNTȠ大侎1痶,每폑一个CMMIT  TRANSACT၉ON 命仦只会使@@TRࡅnK⁏U၎T(按1逳凯瀃当A@ŔVANC䁏UNTY0 最终逓퇏伺0时,将提交整个外部些务㈂


(3)VOLMCA䁇K "TRANSCCtIO^ 语句
ɒOьLт͉CŋĠ`TRAюٓACT䁉ON! 语埥将显示事务或隐藏事务皔起点戶事务内皌某个保存点,它也标志七个庋务盄结束。其语泗格彏如下:čɒOੌɌBICKࠠР⁻TRA⁏ࡼTRANsA䁃TÍON}ȍმtraŮsaktinßîaɭe|@vrao_nსme_varmafleɼsၡŶepoɩ~tɟname|@savepoiѮࡴvariable][?࡝


说明:ၒOL䁌ॢAɇK  VRANSA၃TIO^0清除自事劣皅起点或到某丫保存点所有数据修敹。它还释放由事务控制的资源。Sqၶepoiɯt_name是SɁ͖E !єRCŎSAŃࡔI၏NР 语句中的sawepoint_oame。当条件回滚庖只彳哭事务的一郪分时<可以使甪óavѥᡰoiѮ⁴࡟nameㄒсsaw䉥poթntş聶ari䉡cle港用户实义的、包含朩效保存点名称的变駏的唍称-忕驻是字符敺据类型。儍䁓AW၅d TÒɁNSࡁC\ION语寥


SAVE)(TŒANSACTɉɏ~语句在事务内设置保存点。其语法格式为:


SAVE Ġ{TVAN  ⁼TRQOSAC\Iɏ^]{ɳavepo⁩ntџ䁮amť|Aɳavepoint_ž١riable}[;ý


说明:用爷可以在溟务内设置保存点或标记㄂保存熹可컥实义在挏杣件受涍矒个介助的乀部切后,该事犡可以迖回的一个住置〆如果將事务回滚匰保存碹,则根据需覇必顿完戒其廗噩余皤T-SQR!迭句和C聏M䁍IV TRANSACTKON 语句-或者必须逞过将事务回滛到起始炻完全取消事嚡。若要取涉整个例务,请用RࡏMNBɁCK TRࡁNࡓaCTI၏N t胲ansact}on՟naŭmР语句。违将撥消事务的所朋语叭和过程。聓avၥroࡩntşၮaᩭ⁥是分配给忝存犹的名称も@savetoént_variable包咫有效保存炻名称的瞨户定义变量的唍秱。


供1′࠭′ 寚义丄个事务,帆扈有鑉濮了耳号쯾筋皆学生的嬆杰动1䀰分,并毐交该事务。


DՅŃNARE䈨@tşna࡭gaKɉIR(10)


SEᑔ @t_nࡡme=’adၤ࡟sɣoࡲe’肝BE䁇IN S HYPERLINKР"maѩlt䀺ŔRANS၁ɃTiON@ჴşnc࡭e"!!ĔUvANSɁ䁓TIoN@t_nameЍUpEATE成绩表


SET 分数=切数+10


WшERE耠课稏号=3ŇO


COˍMI⡔ TRANSQ⁃єION @t_nၡŭg


gࡏ$


说明:漬例使甬0BɅ聇IN¡TzࡁNSA\KOࡎ  定义了一为事务名为 aŤd_score的事务,之后使用C䁏Mō]є"ᅔRANWACєIO䑎 提交,即行该事务,将所有学生瞅分敲劢18分〢


徛q࠲=3定义丂个事务,向库存零仾表中添加一潡辴录-并设置忝存碹。然后再剠除该记录,幷回滺到介务盄保存点,提交迥事厡。


B聅⁇IN  ŔђANSAŇTiN0


I၎_EST 䀠၉NTO数杮库雷件表-VAьUES(⁘6007’,ꀘ辺츝钉’,100l‘黑’,20)ᐍœAVE  TࡒANSICTI䁎 sšvepoin䑴(-EEL၅TEȠFRoM 库孙零件表


WHERE 零件编号=‘20007’


ROLLBACK  TRANSACTION savepoint


COMMIT  TRANSACTION


GO


说明:本例使用BEGIN TRANSACTION定义了一个事务,向表添加一条记录,并设置保存点savepoint.之后再删除该记录,并回滚到事务的保存点savepoint处,使用COMMIT TRANSACTION 提交。结果该记录没有删除。


例12-4定义一个事务,向学生表中添加记录。如果添加成功,则给每个分数加10分。否则不操作。


BEGIN  TRAN


INSERT INTO  学生表


VALUES(‘234’,‘张’,‘男’,‘1980-10-28’,‘1’,‘3’,null)


IF @@ error=0


   BEGIN


PRINT ‘添加成功!’


UPDATE 成绩表


SET分数=分数+10


WHERE 学号=111


COMMIT TRAN


END


   ELSE


BEGIN


  PRINT‘添加失败!’


  ROLLBACK  TRAN


END


12.1.3 事务和批
如果用户希望或者整个操作完成,或者什么都不做,这时解决问题的方法就是将整个操作组织成一个简单的事务处理,称为批处理或批。


例12-5将多个操作定义为一个事务。


BEGIN  TRANSACTION


UPDATE 成绩表


SET 分数=分数+10


WHERE 课程号=5


INSERT INTO 学生表(学号,姓名)


VALUES(20123,‘张全’)


DELETE FROM 课程表


WHERE 课程表


WHERE 课程名 LIKE ‘数%’


COMMIT TRANSACTION


说明:本例将多个SQR操作定义为一个事务,这时就形成了一个批处理,要么全部执行,要么都不执行。
                                                   12.2 并发控制的基本概念和SQL Server 2005的并发控制机制
锁就是防止其他事务访问指定资源的手段。锁是实现并发控制的主要方法,是多个用户能够同时操纵同一个数据库的数据而不发生数据不一致现象的重要保障。


12.2.1 锁概述
一般来说,锁可以防止脏读、不可重复读和幻觉读。脏读就是指当一个事务正在访问数据,并且对数据进行了修改,而这种修改还没有提交到数据库中,这时,另外一个事务也访问这个数据,然后使用了这个数据。因为这个数据是还没有提交的数据,那么另外严格事务读到这个数据就是脏数据,依据脏数据所做的操作可能是不正确的。不可重复读是指在一个事务内,多次读同一数据。在这个事务还没有结束时,另外一个事务也访问该数据。那么,在第一个事务中的两次读数据之间,由于第二个事务的修改,则第一个事务两次读到的数据可能是不一样的。这样就发生了在一个事务内两次读到的数据是不一样的,因此,称为不可重复读。幻觉读是指当事务不是独立执行时发生的一种现象,例如第一个事务对一个表中的数据进行了修改,这种修改涉及到表中的全部数据行。同时,第二个事务也修改这个表中的数据,这种修改是向表中插入一行新数据。那么,以后就会发生操作第一个事务的用户发现表中还没有修改的数据行,就好像发生了幻觉一样。


锁是防止其他事务访问指定的资源控制、实现并发控制的一种手段。为了提高系统的性能、加快事务的处理速度、缩短事务的等待时间,应该使锁定的资源最小化。为了控制锁定的资源,应该首先了解系统的空间管理。在SQR SERVER 2005中,最小空间管理单位是页,一个页有8KB。所有的数据、日志、索引都存放在页上。另外,使用页有一个限制,这就是表中的一行数据必须在同一个页上。另外,使用页有一个限制,这就是表中的一行数据必须在同一个页上,不能跨页。页上面的空间管理单位是簇,一个簇是8个连续的页。表和索引的最小占用单位是簇。数据库是有一个或多个表或者索引组成,即有多个簇组成。


12.2.2 锁的模式
数据库引擎使用不同的锁定资源,这些锁模式确定了并发事务访问资源的方式。根据锁定资源方式的不同,SQR SERVER 2005 提供了4种锁模式:共享锁、排他锁、更新锁,意向锁。


1. 共享锁
共享锁也称为S锁,允许并行事务读取同一种资源,这时的事务不能修改访问的数据。当使用共享锁锁定资源时,不允许修改数据的事务访问数据。当读取数据的事务读完数据之后,立即释放所占用的资源。一般地,当使用SELECT 语句访问数据时,系统自动对所访问的数据使用共享锁锁定。


2. 排他锁
对于那些修改数据的事务,例如,使用INSERT、UPDATE、DELETE语句 ,系统自动在所修改的事务上放置排他锁。排他锁也称X锁,就是在同一时间内只允许一个事务访问一种资源,其他事务都不能在有排他锁的资源上访问。在有排他锁的资源上,不能放置共享锁,也就是说,不允许可以产生共享锁的事务访问这些资源。只有当产生排他锁的事务结束之后,排他锁锁定的资源才能被其他事务使用。


3. 更新锁
更新锁也称为U锁,可以防止常见的死锁。在可重复读或可序化事务中,此事务读取数据,获取资源的共享锁,然后修改数据。此操作要求锁转换为排锁。如果两个事务获取了资源上的共享模式锁,然后试图同时更新数据,则一个事务尝试将锁转换为排他锁。共享模式到排他锁的转换必须等待一段时间,因为一个事务的排他锁与其他事务的共享模式锁不兼容,发生锁等待。第二个事务试图获取排他锁以进行更新。由于两个事务都要转换为排他锁,并且每个事务都等待另一个事务释放共享模式锁,因此发生死锁。


若要避免这种潜在的死锁问题,请使用更新锁。一次只有一个事务可以获得资源的更新锁。如果事务修改资源,则更新锁转换为排他锁。


4. 意向锁
数据库引擎使用意向锁来保护共享锁或排他锁放置在锁层次结构的底层资源上。之所以命名为意向锁,是因为在较低级别锁前可获取它们,因此,会通知意向将锁放置在较低级别上。意向锁有两种用途:


防止其他事务以会使较低级别的锁无效的方式修改较高级别资源。


提高数据库引擎在较高的粒度级别检测锁冲突的效率。


意向锁又分为意向共享锁(IS)、意向排他锁(IX)、以及意向排他共享锁(SIX)。意向共享锁表示读低层次资源的事务的意向,把共享锁放在这些单个的资源上。意向排他锁表示修改低层次的事务的意向,把排他锁放在这些单个资源上。意向排他锁包括意向共享锁,它是意向共享锁的超集。使用意向排他的共享锁表示允许并行读取顶层资源的事务的意向,并且修改一些低层次的资源,把意向排他锁这些单个资源上。例如,表上的一个使用意向排他的共享锁把共享锁放在表上,允许并行读取,并且把意向排他锁放在刚要修改的页上,把排他锁放在修改的行上。每一个表一次只能有一个使用意向排他的共享锁因为表级共享锁阻止对表的任何修改。使用意向排他的共享锁和意向排他锁的组合。


12.2.3 锁的信息
锁兼容性控制多个事务能否同时获取同一资源上的锁。如果资源已被另一事务锁定,则,仅当请求锁的模式与现有锁的模式兼容时,才会授予新的锁请求。如果请求锁的模式与现有的模式不兼容,则请求新锁的事务将等待释放现有锁或等待锁超时间隔过期。例如,没有与排他锁兼容的锁模式。如果具有排他锁,则在释放排他锁之前,其他事务均无法获取该资源的任何类型(共享、更新或排他)的锁。另一种情况是,如果共享锁已应用到资源,则即使第一个事务尚未完成,其他事务也可以获取该项的共享锁或更新锁。但是,在释放共享锁之前,其他事务无法获取排他锁。


用户可以通过使用SQL SERVER 2005的SQL SERVER PROFILER,指定用来捕获有关跟踪中锁事件的信息的锁事件类别。还可以在系统监视器中,从锁对象指定计数器来监视数据库引擎实例中的锁级别。


在SQR  SERVERN  PROFILER中查看系统锁定信息时,首先启动SQR   SERVER  PROFILER,其窗口如图12-1所示。


图12-1  SQL Server Profiler  窗口


选择系统菜单“文件”—— “新建跟踪”选项,新建一个跟踪事件,连接到服务器,如图12-2所示。


图12-2连接服务器


连接成功,设置根据事件属性。在“跟踪属性”对话框的“常规”选项卡中,用户可以设置跟踪名称、使用模板,以及启用跟踪停止时间和将跟踪存储到指定文件,如图12-3所示。


图12-3 跟踪属性 对话框的常规选项卡


还可以将跟踪保存到指定表。在“跟踪属性”对话框的“事件选择”选项卡中,用户可以设置跟踪的事件以及事件的列,如图12-4所示。


图12-4跟踪属性对话框的事件选择选项卡


设置完毕,在SQL Server Profiler中显示跟踪事件,如图12-5所示。


图 12-5 跟踪事件的显示


同时,在SSMS的对象资源管理器中,系统将新建一个新表,即设置的保存到表。打开该表,显示用户设置跟踪的事件以及事件的列。


为了和SQL Server兼容,还可以使用sys.dm_tran_locks动态管理视图来替代sp_lock系统存储过程。


12.2.4 死锁及处理
在事务锁的使用过程中,死锁是一个不可避免的现象。在下列两种情况下,可以发生死锁。


第一种情况是,当两个事务分别锁定了两个单独的对象,这时每一个事务都有要求在另外一个事务锁定的对象上获得一个锁,因此第一个事务都有必须等待另一个释放占有的锁,这时就发生了死锁,这种死锁是最典型的死锁形式。


死锁的第二种情况是,当在一个数据库中。有若干个长时间运行的事务执行并行的操作,当查询分析器处理一种非常复杂的查询例如连接查询时,那么;由于不能控制处理的顺序,有可能发生死锁现象。


当发生了死锁现象时, 除非某个外部进程断开死锁,否则死锁中的两个事务都将无期等待下去。SQL Server 2005的SQL Server Database Engine自动检测SQL Server中的死锁循环。数据库引擎选择一个会话作为死锁牺牲,然后终止当前事务(出现错误)来打断死锁。如果监视器检测到循环依赖关系,通过自动取消其中一个事务来结束死锁。处理时间长的事务具有较高的优先级,处理时间较短的事务具有较低的优先级。在发生冲突时,保留优先级高的事务,取消优先级的事务。


用户可以使用SQL Server Profiler确定死锁的原因。当SQLServer中某组资源的两个或多个线程或进程之间存在的依赖关系时,将会发生死锁。使用SQL Server Profiler,可以创建记录、重播和显示死锁事件的跟踪以进行分析。


若要跟踪死锁事件,请将Deadlock graph事件类添加到跟踪。可以通过下列任一方法进行提取:


在配置跟踪时,使用“事件提取设置”选项卡。请注意,只有在“事件选择“选项卡上选择了Deadlock graph事件,才会出现此选项卡。


也可以使用“文件“菜单中的”提取SQL Server事件“选项,或者通过鼠标右键击特定事件并选择”提取事件数据“,来提取并保存各个事件。
                                                                                            12.3 数据库优化
一个数据库系统的性能依赖于组成这些系统的数据库中物理设计结构的有效配置。这些物理设计结构包括索引、聚集索引、索引视图和分区等,其目的在于提高数据库的性能和可管理性。SQL Server 2005提供了一套综合的工具,用于优化物理数据库的设计,其中数据库引擎优化顾问,是分析一个或多个数据库上工作负荷(对要做出化的数据库招待的一组T-SQL语名句)的性能效果的工具。


本节主要介绍数据库引擎优化顾问的使用。


12.3.1 数据库引擎优化顾问概述
数据库引擎优化顾问是一种工具,用于分析在一个或多个数据库中运行的工作负荷的性能效果。工作负荷是对在优化的数据库招待的一组T-SQL语句。分析数据库的工作负荷效果后,数据库引擎优化顾问会提供在SQL Server 2005 数据库中添加、删除或修改物理设计结构的建议。这些物理性能结构包括聚集索引、非聚集索引、索引视图和分区。实现这些结构之后,数据库引擎优化顾问使查询处理器能够用最短的时间性执行工作负荷任务。


12.3.2 数据库引擎优化顾问的使用
数据库引擎优化顾问提供了两种使用方式:


图形界面。用于优化数据库、查看优化建议和报告的工具。


命令行实用工具程序dta.exe。用于实现数据库引擎优化顾问在软件程序和脚本方面的功能。


本节只介绍图形界面优化数据库,需要启动数据库引擎优化顾问。首先连接到服务器,如图12-6所示。


图12-6连接服务器


连接成功后,进入数据库引擎优顾问。如图12-7所示。


图12-7数据库引擎优顾问


用户可以在“会话框”中设置数据库优化设置。选择工作负荷文件,即由SQL Server Proriler创建的事件文件。选择需要优化的数据库。如图12-8所示。


图12-8选择需要优化的数据库


选择系统菜单“操作”→“开始分析”选项,系统自动对数据库进行优化分析操作。优化分析结束,在“会话框”中多出了3个选项:进度、建议、和报告。显示优化分析的进度信息,建议信息和优化报告信息。


用户还可以选择系统菜单“文件”→“新建会话”选项,新建一个会话,优化其他的数据库。
                                                                                            12.4 小结
结合第1章DBMS的DCL数据库的安全性控制功能,本章讲解事物的基本概念和SQL Server 2005事物处理和并发控制的基本概念,包括封锁、封锁协议、活锁、死锁等概念;讲解SQL Server 2005的并发控制机制。
========

数据库 并发控制 常见锁



封锁 (数据库)
封锁是一项用于多用户同时访问数据库的技术,是实现并发控制的一项重要手段,能够防止当多用户改写数据库时造成数据丢失和损坏。当有一个用户对数据库内的数据进行操作时,在读取数据前先锁住数据,这样其他用户就无法访问和修改该数据,直到这一数据修改并写回数据库解除封锁为止。


乐观并发控制(乐观锁)
在关系数据库管理系统里,乐观并发控制(又名“乐观锁”,Optimistic Concurrency Control,缩写“OCC”)是一种并发控制的方法。它假设多用户并发的事务在处理时不会彼此互相影响,各事务能够在不产生锁的情况下处理各自影响的那部分数据。在提交数据更新之前,每个事务会先检查在该事务读取数据后,有没有其他事务又修改了该数据。如果其他事务有更新的话,正在提交的事务会进行回滚。乐观事务控制最早是由孔祥重(H.T.Kung)教授提出[1]。


乐观并发控制多数用于数据争用不大、冲突较少的环境中,这种环境中,偶尔回滚事务的成本会低于读取数据时锁定数据的成本,因此可以获得比其他并发控制方法更高的吞吐量。


目录
1 乐观并发控制的阶段
2 优点与不足
3 相关条目
4 参考文献
5 外部链接
乐观并发控制的阶段
乐观并发控制的事务包括以下阶段:[来源请求]


读取:事务将数据读入缓存,这时系统会给事务分派一个时间戳。
校验:事务执行完毕后,进行提交。这时同步校验所有事务,如果事务所读取的数据在读取之后又被其他事务修改,则产生冲突,事务被中断回滚。
写入:通过校验阶段后,将更新的数据写入数据库。


悲观并发控制(悲观锁)
在关系数据库管理系统里,悲观并发控制(又名“悲观锁”,Pessimistic Concurrency Control,缩写“PCC”)是一种并发控制的方法。它可以阻止一个事务以影响其他用户的方式来修改数据。如果一个事务执行的操作都某行数据应用了锁,那只有当这个事务把锁释放,其他事务才能够执行与该锁冲突的操作。


悲观并发控制主要用于数据争用激烈的环境,以及发生并发冲突时使用锁保护数据的成本要低于回滚事务的成本的环境中。
========

多种数据库的并发控制比较



多种数据库的并发控制比较
内容:
比较的ORACLE INFORMIX DB2 SYBASE MSSQL等的并发控制机制。比较分析悲观与乐观并发控制机制的异同。
引言:
l       在关系数据库(DB2,Oracle,Sybase,Informix和SQL Server)最小的恢复和交易单位为一个事务(Transactions),事务具有ACID(原子性,一致性,隔离性和永久性)特征。关系数据库为了确保并发用户在存取同一数据库对象时的正确性(即无丢失更新、可重复读、不读"脏"数据,无"幻像"读),数据库中引入了并发(锁)机制。基本的锁类型有两种:排它锁(Exclusive locks记为X锁)和共享锁(Share locks记为S锁)。
l       排它锁:若事务T对数据D加X锁,则其它任何事务都不能再对D加任何类型的锁,直至T释放D上的X锁;一般要求在修改数据前要向该数据加排它锁,所以排它锁又称为写锁。
l       共享锁:若事务T对数据D加S锁,则其它事务只能对D加S锁,而不能加X锁,直至T释放D上的S锁;一般要求在读取数据前要向该数据加共享锁,所以共享锁又称为读锁。


相关解释
l页:以磁盘页面(disk pages)为单位存储数据。一个磁盘页面包含一个或多个记录。
l键字级:Informix(在一定情况下,数据库服务器需要锁一个不存在的记录。它的效果相当于在记录将要存在的地方放一个锁。当表使用记录锁时,对假想的记录使用键字锁。当表使用页级锁时,含有或可能含有键字的索引页将被设置键级锁)。MSSQL(索引中的行锁。用于保护可串行事务中的键范围)。
l扩展盘区:相邻的八个数据页或索引页构成的一组。
l表空间:DB2 的表空间按管理方式分为两种:系统管理空间和数据库管理空间。 按类型分为:规则表空间、长整数表空间、系统临时表空间、用户临时表空间 。其中长整数表空间只能是DMS的。规则表空间中包含用户数据的表。默认用户表空间名为USERSPACE1,索引也存在规则表空间中,另外系统目录表也放在规则表空间中。
 
DB2数据库表锁的模式 


几种表锁的模式进一步阐述:
lIS、IX、SIX方式用于表一级并需要行锁配合,他们可以阻止其他应用程序对该表加上排它锁。
l如果一个应用程序获得某表的IS锁,该应用程序可获得某一行上的S锁,用于只读操作,同时其他应用程序也可以读取该行,或是对表中的其他行进行更改。
l如果一个应用程序获得某表的IX锁,该应用程序可获得某一行上的X锁,用于更改操作,同时其他应用程序可以读取或更改表中的其他行。
l如果一个应用程序获得某表的SIX锁,该应用程序可以获得某一行上的X锁,用于更改操作,同时其他应用程序只能对表中其他行进行只读操作。
几种表锁的模式进一步阐述:
lS、U、X和Z方式用于表一级,但并不需要行锁配合,是比较严格的表加锁策略。
l如果一个应用程序得到某表的S锁。该应用程序可以读表中的任何数据。同时它允许其他应用程序获得该表上的只读请求锁。如果有应用程序需要更改读该表上的数据,必须等S锁被释放。
l如果一个应用程序得到某表的U锁,该应用程序可以读表中的任何数据,并最终可以通过获得表上的X锁来得到对表中任何数据的修改权。其他应用程序只能读取该表中的数据。U锁与S锁的区别主要在于更改的意图上。U锁的设计主要是为了避免两个应用程序在拥有S锁的情况下同时申请X锁而造成死锁的。
l如果一个应用程序得到某表上的X锁,该应用程序可以读或修改表中的任何数据。其他应用程序不能对该表进行读或者更改操作。
l如果一个应用程序得到某表上的Z锁,该应用程序可以读或修改表中的任何数据。其他应用程序,包括未提交读程序都不能对该表进行读或者更改操作。
lIN锁用于表上以允许未提交读这一概念。
DB2数据库行锁的模式
 
DB2数据库表锁的相容矩阵
 
DB2数据库行锁的相容矩阵
 
DB2中各SQL语句产生表锁的情况(假设缺省的隔离级别为CS):


 
DB2锁的升级 (1)


l每个锁在内存中都需要一定的内存空间,为了减少锁需要的内存开销,DB2提供了锁升级的功能。锁升级是通过对表加上非意图性的表锁,同时释放行锁来减少锁的数目,从而达到减少锁需要的内存开销的目的。锁升级是由数据库管理器自动完成的,有两个数据库的配置参数直接影响锁升级的处理:
llocklist--在一个数据库全局内存中用于锁存储的内存。单位为页(4K)。
lmaxlocks--一个应用程序允许得到的锁占用的内存所占locklist大小的百分比。
l锁升级会在这两种情况下被触发:
l某个应用程序请求的锁所占用的内存空间超出了maxlocks与locklist的乘积大小。这时,数据库管理器将试图通过为提出锁请求的应用程序申请表锁,并释放行锁来节省空间。
DB2锁的升级 (2)


l在一个数据库中已被加上的全部锁所占的内存空间超出了locklist定义的大小。这时,数据库管理器也将试图通过为提出锁请求的应用程序申请表锁,并释放行锁来节省空间。
l锁升级虽然会降低OLTP应用程序的并发性能,但是锁升级后会释放锁占有内存并增大可用的锁的内存空间。
l锁升级是有可能会失败的,比如,现在一个应用程序已经在一个表上加有IX锁,表中的某些行上加有X锁,另一个应用程序又来请求表上的IS锁,以及很多行上的S锁,由于申请的锁数目过多引起锁的升级。数据库管理器试图为该应用程序申请表上的S锁来减少所需要的锁的数目,但S锁与表上原有的IX锁冲突,锁升级不能成功。
l如果锁升级失败,引起锁升级的应用程序将接到一个-912的SQLCODE。在锁升级失败后,DBA应该考虑增加locklist的大小或者增大maxlocks的百分比。同时对编程人员来说可以在程序里对发生锁升级后程序回滚后重新提交事务(例如:if sqlca.sqlcode=-912 then rollback and retry等)。
 
Oracle 多粒度锁机制介绍


l根据保护对象的不同,Oracle数据库锁可以分为以下几大类:
l(1) DML lock(data locks,数据锁):用于保护数据的完整性;
l(2) DDL lock(dictionary locks,字典锁):用于保护数据库对象的结构(例如表、视图、索引的结构定义);
l(3) Internal locks 和latches(内部锁与闩):保护内部数据库结构;
l(4) Distributed locks(分布式锁):用于OPS(并行服务器)中;
l(5) PCM locks(并行高速缓存管理锁):用于OPS(并行服务器)中。
l在Oracle中最主要的锁是DML(也可称为data locks,数据锁)锁。从封锁粒度(封锁对象的大小)的角度看,Oracle DML锁共有两个层次,即行级锁和表级锁。
 
Oracle的TX锁行级锁、事务锁 


l许多对Oracle不太了解的技术人员可能会以为每一个TX锁代表一条被封锁的数据行,其实不然。TX的本义是Transaction(事务)当一个事务第一次执行数据更改(Insert、Update、Delete)或使用SELECT… FOR UPDATE语句进行查询时,它即获得一个TX(事务)锁,直至该事务结束(执行COMMIT或ROLLBACK操作)时,该锁才被释放。所以,一个TX锁,可以对应多个被该事务锁定的数据行(在我们用的时候多是启动一个事务,然后SELECT… FOR UPDATE NOWAIT)。
l在Oracle的每行数据上,都有一个标志位来表示该行数据是否被锁定。Oracle不像DB2那样,建立一个链表来维护每一行被加锁的数据,这样就大大减小了行级锁的维护开销,也在很大程度上避免了类似DB2使用行级锁时经常发生的锁数量不够而进行锁升级的情况。数据行上的锁标志一旦被置位,就表明该行数据被加X锁,Oracle在数据行上没有S锁。
Oracle意向锁 


l意向锁的含义是如果对一个结点加意向锁,则说明该结点的下层结点正在被加锁;对任一结点加锁时,必须先对它的上层结点加意向锁。如:对表中的任一行加锁时,必须先对它所在的表加意向锁,然后再对该行加锁。这样一来,事务对表加锁时,就不再需要检查表中每行记录的锁标志位了,系统效率得以大大提高。
l由两种基本的锁类型(S锁、X锁),可以自然地派生出两种意向锁:
l另外,基本的锁类型(S、X)与意向锁类型(IS、IX)之间还可以组合出新的锁类型,理论上可以组合出4种,即:S+IS,S+IX,X+IS,X+IX,但稍加分析不难看出,实际上只有S+IX有新的意义,其它三种组合都没有使锁的强度得到提高(即:S+IS=S,X+IS=X,X+IX=X,这里的"="指锁的强度相同)。所谓锁的强度是指对其它锁的排斥程度。
l这样我们又可以引入一种新的锁的类型:共享意向排它锁(Shared Intent Exclusive Lock,简称SIX锁):如果对一个数据库对象加SIX锁,表示对它加S锁,再加IX锁,即SIX=S+IX。例如:事务对某个表加SIX锁,则表示该事务要读整个表(所以要对该表加S锁),同时会更新个别行(所以要对该表加IX锁)。
l具有意向锁的多粒度封锁方法中任意事务T要对一个数据库对象加锁,必须先对它的上层结点加意向锁。申请封锁时应按自上而下的次序进行;释放封锁时则应按自下而上的次序进行;具有意向锁的多粒度封锁方法提高了系统的并发度,减少了加锁和解锁的开销。
Oracle的TM锁(表级锁) 
    
Oracle数据库TM锁小结
 
多粒度封锁机制的监控 


l快照监控
l事件监控方式
l视图(Oracle)
v$lock视图列出当前系统持有的或正在申请的所有锁的情况
 
v$locked_object视图列出当前系统中哪些对象正被锁定
 
总结 (1)
l1.Oracle通过具有意向锁的多粒度封锁机制进行并发控制,保证数据的一致性。其DML锁(数据锁)分为两个层次(粒度):即表级和行级。通常的DML操作在表级获得的只是意向锁(RS或RX),其真正的封锁粒度还是在行级;DB2也是通过具有意向锁的多粒度封锁机制进行并发控制,保证数据的一致性。其DML锁(数据锁)分为两个层次(粒度):即表级和行级。通常的DML操作在表级获得的只是意向锁(IS,SIX或IX),其真正的封锁粒度也是在行级;另外,在Oracle数据库中,单纯地读数据(SELECT)并不加锁,这些都提高了系统的并发程度,Oracle强调的是能够"读"到数据,并且能够快速的进行数据读取。而DB2的锁强调的是"读一致性",进行读数据(SELECT)时会根据不同的隔离级别(RR,RS,CS)而分别加S,IS,IS锁,只有在使用UR隔离级别时才不加锁。从而保证不同应用程序和用户读取的数据是一致的。
总结(2)
l2. 在支持高并发度的同时,DB2和Oracle对锁的操纵机制有所不同:Oracle利用意向锁及数据行上加锁标志位等设计技巧,减小了Oracle维护行级锁的开销,使其在数据库并发控制方面有着一定的优势。而DB2中对每个锁会在锁的内存(locklist)中申请分配一定字节的内存空间,具体是X锁64字节内存,S锁32字节内存(注:DB2 V8之前是X锁72字节内存而S锁36字节内存)。
l3. Oracle数据库中不存在锁升级,而DB2数据库中当数据库表中行级锁的使用超过locklist*maxlocks会发生锁升级。
l4. 在Oracle中当一个session对表进行insert,update,delete时候,另外一个session仍然可以从Orace回滚段或者还原表空间中读取该表的前映象(before image); 而在DB2中当一个session对表进行insert,update,delete时候,另外一个session仍然在读取该表数据时候会处于lock wait状态,除非使用UR隔离级别可以读取第一个session的未提交的值;所以Oracle同一时刻不同的session有读不一致的现象,而DB2在同一时刻所有的session都是"读一致"的。
 
SQLServer使用以下资源锁模式
 
l共享 (S):用于不更改或不更新数据的操作(只读操作),如 SELECT 语句。
l更新 (U):   用于可更新的资源中。防止当多个会话在读取、锁定以及随后可能进行的资源更新时发生常见形式的死锁。
l排它 (X):   用于数据修改操作,例如 INSERT、UPDATE 或 DELETE。确保不会同时对同一资源进行多重更新。
l意向:用于建立锁的层次结构。意向锁的类型为:意向共享 (IS)、意向排它 (IX) 以及与意向排它共享 (SIX)。
l架构:在执行依赖于表架构的操作时使用。架构锁的类型为:架构修改 (Sch-M) 和架构稳定性 (Sch-S)。
l大容量更新 (BU):向表中大容量复制数据并指定了 TABLOCK 提示时使用。
架构锁
 
l执行表的数据定义语言 (DDL) 操作(例如添加列或除去表)时使用架构修改 (Sch-M) 锁。
l当编译查询时,使用架构稳定性 (Sch-S) 锁。架构稳定性 (Sch-S) 锁不阻塞任何事务锁,包括排它 (X) 锁。因此在编译查询时,其它事务(包括在表上有排它 (X) 锁的事务)都能继续运行。但不能在表上执行 DDL 操作。
大容量更新锁
l当将数据大容量复制到表,且指定了 TABLOCK 提示或者使用 sp_tableoption 设置了 table lock on bulk 表选项时,将使用大容量更新 (BU) 锁。大容量更新 (BU) 锁允许进程将数据并发地大容量复制到同一表,同时防止其它不进行大容量复制数据的进程访问该表。
 
Sybase锁的状态


lSQL SERVER加锁有三种状态:
l1)意向锁(intend)—是一种表级锁,它表示在一个数据页上获得一个S或X锁的意向。意向锁可以防止其他事务在该数据页的表上获得排它锁。
l2)阻塞(blocking,简记blk)—它表明目前加锁进程的状态,带有blk后缀的锁说明该进程目前正阻塞另一个需要获得锁的进程,只有这一进程完成,其他进程才可以进行。
l3)需求锁(demand)—表示此时该进程企图得到一个排它锁。它可以防止在这一表或页上加过多的S锁,她表示某一事务是下一个去锁定该表和该页的事务。
l需求锁是一个内部过程,因此用sp_lock是无法看见的。
 
Informix
l锁的类型 INFORMIX有三种不同类型的锁。它们在不同的情况下使用。 
1. SHARED锁 
SHARED锁只保留对象的可读性。当锁存在时,对象不能改变。多个程序可对同个对象加SHARED锁。 
2. EXCLUSIVE锁 
只能使单个程序使用。在程序要改变对象时使用。当其他锁存在时,EXCLUSIVE锁不能使用。当使用了EXCLUSIVE 锁后,其他锁不能用于同一对象。 
3. PROMOTABLE锁 
实现更新的目的。PROMOTABLE锁可以放在已经有SHARED锁的记录,但不能放在已经有PROMOTABLE锁和EXCLUSIVE 锁的地方。当记录上无其他锁(含SHARED 锁)情况下,这时在程序准备改变锁的记录时,PROMOTABLE锁可以提升为EXCLUSIVE锁。如果在已有SHARED锁的记录上设置了PROMOTABLE锁,在PROMOTABLE锁可以提升到EXCLUSIVE锁 
之前需要删除SHARED 锁。PROMOTABLE锁只能在INFORMIX Universal Server中支持。 
========

相关链接

http://blog.csdn.net/xiangminjing/article/details/5922325
http://www.cnblogs.com/chuncn/archive/2009/04/21/1440233.html
http://www.cnblogs.com/binfire/articles/1689572.html
http://blog.csdn.net/longronglin/article/details/1522561
http://blog.csdn.net/klarclm/article/details/7532454

你可能感兴趣的:(转载,数据库,数据库,并发,数据库并发)