精华内容
下载资源
问答
  • 数据库并发控制技术

    万次阅读 热门讨论 2007-06-28 13:04:00
    数据库是一个共享资源,可以提供多个用户使用。这些用户程序可以一个一个地串行执行,每个时刻只有一个用户程序运行,执行对数据库的存取,其他用户程序必须等到这个用户程序结束...但这样就会产生多个用户程序并发存取
    数据库是一个共享资源,可以提供多个用户使用。这些用户程序可以一个一个地串行执行,每个时刻只有一个用户程序运行,执行对数据库的存取,其他用户程序必须等到这个用户程序结束以后方能对数据库存取。但是如果一个用户程序涉及大量数据的输入/输出交换,则数据库系统的大部分时间处于闲置状态。因此,为了充分利用数据库资源,发挥数据库共享资源的特点,应该允许多个用户并行地存取数据库。但这样就会产生多个用户程序并发存取同一数据的情况,若对并发操作不加控制就可能会存取和存储不正确的数据,破坏数据库的一致性,所以数据库管理系统必须提供并发控制机制。并发控制机制的好坏是衡量一个数据库管理系统性能的重要标志之一。

    DM用封锁机制来解决并发问题。它可以保证任何时候都可以有多个正在运行的用户程序,但是所有用户程序都在彼此完全隔离的环境中运行。

    一、  并发控制的预备知识

    (一)  并发控制概述

    并发控制是以事务(transaction)为单位进行的。

    1.  并发控制的单位――事务

    事务是数据库的逻辑工作单位,它是用户定义的一组操作序列。一个事务可以是一组SQL语句、一条SQL语句或整个程序。

    事务的开始和结束都可以由用户显示的控制,如果用户没有显式地定义事务,则由数据库系统按缺省规定自动划分事务。

    事务应该具有4种属性:原子性、一致性、隔离性和持久性。

    (1)原子性

    事务的原子性保证事务包含的一组更新操作是原子不可分的,也就是说这些操作是一个整体,对数据库而言全做或者全不做,不能部分的完成。这一性质即使在系统崩溃之后仍能得到保证,在系统崩溃之后将进行数据库恢复,用来恢复和撤销系统崩溃处于活动状态的事务对数据库的影响,从而保证事务的原子性。系统对磁盘上的任何实际数据的修改之前都会将修改操作信息本身的信息记录到磁盘上。当发生崩溃时,系统能根据这些操作记录当时该事务处于何种状态,以此确定是撤销该事务所做出的所有修改操作,还是将修改的操作重新执行。

    (2)一致性

    一致性要求事务执行完成后,将数据库从一个一致状态转变到另一个一致状态。它是一种以一致性规则为基础的逻辑属性,例如在转账的操作中,各账户金额必须平衡,这一条规则对于程序员而言是一个强制的规定,由此可见,一致性与原子性是密切相关的。事务的一致性属性要求事务在并发执行的情况下事务的一致性仍然满足。它在逻辑上不是独立的,它由事务的隔离性来表示。

    (3) 隔离性

    隔离性意味着一个事务的执行不能被其他事务干扰。即一个事务内部的操作及使用的数据对并发的其他事务是隔离的,并发执行的各个事务之间不能互相干扰。它要求即使有多个事务并发执行,看上去每个成功事务按串行调度执行一样。这一性质的另一种称法为可串行性,也就是说系统允许的任何交错操作调度等价于一个串行调度。串行调度的意思是每次调度一个事务,在一个事务的所有操作没有结束之前,另外的事务操作不能开始。由于性能原因,我们需要进行交错操作的调度,但我们也希望这些交错操作的调度的效果和某一个串行调度是一致的。 DM实现该机制是通过对事务的数据访问对象加适当的锁,从而排斥其他的事务对同一数据库对象的并发操作。

    (4)持久性

    系统提供的持久性保证要求一旦事务提交,那么对数据库所做的修改将是持久的,无论发生何种机器和系统故障都不应该对其有任何影响。例如,自动柜员机( ATM)在向客户支付一笔钱时,就不用担心丢失客户的取款记录。事务的持久性保证事务对数据库的影响是持久的,即使系统崩溃。正如在讲原子性时所提到的那样,系统通过做记录来提供这一保证。

    DM没有提供显式定义事务开始的语句,第一个可执行的SQL语句(除CONNECT语句外)隐含事务的开始,但事务的结束可以由用户显式的控制。在DM中以下几种情况都结束 (正常,非正常)某一事务:

    (1)当某一连接的属性设置为自动提交,每执行一条语句都会提交;

    (2)遇到COMMIT/ROLLBACK语句,便提交/回滚一事务;

    (3)当系统的 DDL自动提交开关打开时(缺省为打开),遇到DDL语句则自动提交该DDL语句和以前的DML和DDL操作;

    (4)事务所在的程序正常结束和用户退出;

    (5)系统非正常终止时;

    说明:DM在配置文件中提供了DDL语句的自动提交开关DDL_AUTO_COMMIT。 当此配置项的值为 1(缺省情况)时,所有DDL语句自动提交;当此配置项的值为0时,除CREATEDATABASE、ALTERDATABASE和CREATESCHEMA语句外的所有DDL语句都不自动提交。

    DM中的一致性是以事务为基础的。DM通过提交和回滚分别用于将对数据库的修改永久化和废除,但是无论是提交和回滚,DM保证数据库在每个事务开始前、结束后是一致的。为了提高事务管理的灵活性,DM提供了设置保存点(SAVEPOINT)语句和回滚到保存点语句。保存点提供了一种灵活的回滚,事务在执行中可以回滚到某个保存点,在该保存点以前的操作有效,而以后的操作被回滚掉。

    DM中的事务同样具有上述4个属性:原子性、一致性、隔离性和持久性。

    2. 并发操作与数据的不一致性

    如果没有锁定且多个用户同时访问一个数据库,则当他们的事务同时使用相同的数据时可能会发生问题,导致数据库中的数据的不一致性。

    一个最常见的并发操作的例子是火车/飞机订票系统中的订票操作。例如,在该系统中的一个活动序列:

    ① 甲售票员读出某航班的机票张数余额A,设A=16;

    ②  乙售票员读出同一航班的机票张数余额A,也是16;

    ③  甲售票员卖出一张机票,修改机票张数余额A=A-1=15,把A写回数据库;

    ④  乙售票员也卖出一张机票,修改机票张数余额A=A-1=15,把A写回数据库。

    结果明明卖出两张机票,数据库中机票余额只减少1。

    这种情况称为数据库的不一致性。这种不一致性是由甲、乙两个售票员并发操作引起的。在并发操作情况下,对甲、乙两个事务操作序列的调度是随机的。若按上面的调度序列行,甲事务的修改就被丢失。这是由于第4步中乙事务修改A并写回覆盖了甲事务的修改。

    并发操作带来的数据库不一致性可以分为四类:丢失或覆盖更新、脏读、不可重复读和幻像读,上例只是并发问题的一种。

    (1)       丢失或覆盖更新(lost update

    当两个或多个事务选择同一数据,并且基于最初选定的值更新该数据时,会发生丢失更新问题。每个事务都不知道其它事务的存在。最后的更新将重写由其它事务所做的更新,这将导致数据丢失。上面预定飞机票的例子就属于这种并发问题。事务1与事务2先后读入同一数据A=16,事务1执行A-1,并将结果A=15写回,事务2执行A-1,并将结果A=15写回。事务2提交的结果覆盖了事务1对数据库的修改,从而使事务1对数据库的修改丢失了。



     
    (2)    脏读
    一个事务读取了另一个未提交的并行事务写的数据。当第二个事务选择其它事务正在更新的行时,会发生未确认的相关性问题。第二个事务正在读取的数据还没有确认并且可能由更新此行的事务所更改。换句话说,当事务1修改某一数据,并将其写回磁盘,事务2读取同一数据后,事务1由于某种原因被撤销,这时事务1已修改过的数据恢复原值,事务2读到的数据就与数据库中的数据不一致,是不正确的数据,称为脏读。
    例如,在下图中,事务1将C值修改为200,事务2读到C为200,而事务1由于某种原因撤销,其修改作废,C恢复原值100,这时事务2读到的就是不正确的“脏“数据了。



    (3)    不可重复读(nonrepeatable read)
    一个事务重新读取前面读取过的数据,发现该数据已经被另一个已提交的事务修改过。即事务1读取某一数据后,事务2对其做了修改,当事务1再次读数据时,得到的与第一次不同的值。
    例如,在下图中,事务1读取B=100进行运算,事务2读取同一数据B,对其进行修改后将B=200写回数据库。事务1为了对读取值校对重读B,B已为200,与第一次读取值不一致。



    (4)    幻像读
    如果一个事务在提交查询结果之前,另一个事务可以更改该结果,就会发生这种情况。这句话也可以这样解释,事务1按一定条件从数据库中读取某些数据记录后未提交查询结果,事务2删除了其中部分记录,事务1再次按相同条件读取数据时,发现某些记录神秘地消失了;或者事务1按一定条件从数据库中读取某些数据记录后未提交查询结果,事务2插入了一些记录,当事务1再次按相同条件读取数据时,发现多了一些记录。
    产生上述四类数据不一致性的主要原因是并发操作破坏了事务的隔离性。并发控制就是要用正确的方式调度并发操作,使一个用户事务的执行不受其他事务的干扰,从而避免造成数据的不一致性。
    3.    并发场景列举
    结合SQL语句,列举各种并发情况(包括可能导致数据不一致性和对数据一致性不产生影响的情况)。A表示某一条数据,b和c都表示满足某一个标准的两条或多条数据,^表示“非”的意思,∈表示属于或包含于的意思,1表示第一个事务,2表示第二个事务。



    (二)    并发操作的调度
    计算机系统对并行事务中并行操作的调度是随机的,而不同的调度可能会产生不同的结果,那么哪个结果是正确的,哪个是不正确的呢?
    如果一个事务运行过程中没有其他事务在同时运行,也就是说没有受到其他事务的干扰,那么就可能认为该事务的运行结果是正常的或者预想的,因此将所有事务串行起来的调度策略是正确的调度策略。虽然以不同的顺序串行执行事务也可能会产生不同的结果,但由于不会将数据库置于不一致状态,所以都可以认为是正确的。由此可以得到如下结论:几个事务的并行执行是正确的,当且仅当其结果与按某一次序串行地执行它们的结果相同。我们称这种并行调度策略为可串行化(serializable)的调度。可串行性(serializability)是并行事务正确性的唯一准则。
    例如,现在有两个事务,分别包含下列操作:
    事务1:读B;A=B+1;写回A;
    事务2:读A;B=A+1;写回B;
    假设A的初值为10,B的初值为2。下图给出了对这两个事务的三种不同的调度策略,(a)和(b)为两种不同的串行调度策略,虽然执行结果不同,但他们都是正确的调度。(c)中两个事务是交错执行的,由于执行结果与(a)、(b)的结果都不同,所以是错误的调度。(d)中的两个事务也是交错执行的,由于执行结果与串行调度1(图(a))的执行结果相同,所以是正确的调度。



    为了保证并行操作的正确性,DBMS的并行控制机制必须提供一定的手段来保证调度是可串行化的。
    从理论上讲,在某一事务执行时禁止其他事务执行的调度策略一定是可串行化的调度,这也是最简单的调度策略,但这种方法实际上是不可行的,因为它使用户不能充分共享数据库资源。
    目前DBMS普遍采用封锁方法(悲观方法,DM采用的就是这种方法,SQL Server也是采用的这种方法)来保证调度的正确性;即保证并行操作调度的可串行性。除此之外还有其他一些方法,如时标方法、乐观方法等。

    •    悲观并发控制
    锁定系统阻止用户以影响其它用户的方式修改数据。如果用户执行的操作导致应用了某个锁,则直到这个锁的所有者释放该锁,其它用户才能执行与该锁冲突的操作。该方法主要用在数据争夺激烈的环境中,以及出现并发冲突时用锁保护数据的成本比回滚事务的成本低的环境中,因此称该方法为悲观并发控制。

    •    乐观并发控制
    在乐观并发控制中,用户读数据时不锁定数据。在执行更新时,系统进行检查,查看另一个用户读过数据后是否更改了数据。如果另一个用户更新了数据,将产生一个错误。一般情况下,接收错误信息的用户将回滚事务并重新开始。该方法主要用在数据争夺少的环境内,以及偶尔回滚事务的成本超过读数据时锁定数据的成本的环境内,因此称该方法为乐观并发控制。

    •    时标并发控制
    时标和封锁技术之间的基本区别是封锁是使一组事务的并发执行(即交叉执行)同步,使用它等价于这些事务的某一串行操作;时标法也是使用一组事务的交叉执行同步,但是使它等价于这些事务的一个特定的串行执行,即由时标的时序所确定的一个执行。如果发生冲突,是通过撤销并重新启动一个事务解决的。事务重新启动,则赋予新的时标。

    (三)    封锁
    封锁是事项并发控制的一个非常重要的技术。所谓封锁就是事务T在对某个数据对象,例如,在标、记录等操作之前,先向系统发出请求,对其加锁。加锁后事务T就对数据库对象有了一定的控制,在事务T释放它的锁之前,其他事务不能更新此数据对象。
    1.    封锁类型
    DBMS通常提供了多种数据类型的封锁。一个事务对某个数据对象加锁后究竟拥有什么样的控制是由封锁类型决定的。基本的封锁类型有两种:排他锁(exclusive lock,简记为X锁)和共享锁(share lock简记为S锁)
    排他锁又称为写锁。若事务T对数据对象A加上X锁,则只允许T读取和修改A,其他任何事务都不能再对A加任何类型的锁,直到T释放A上的锁。这就保证了其他事务在T释放A上的锁之前不能再读取和修改A。
    共享锁又称为读锁。若事务T对数据对象A加上S锁,则其他事务只能再对A加S锁,而不能加X锁,直到T释放A上的锁。这就保证了其他事务可以读A,但在T释放A上的S锁之前不能对A做任何修改。
    排他锁与共享锁的控制方式可以用下图的相容矩阵来表示。 
    在下图的封锁类型相容矩阵中,最左边一列表示事务T1已经获得的数据对象上的锁的类型,其中横线表示没有加锁。最上面一行表示另一事务T2对同一数据对象发出的封锁请求。T2的封锁请求能否被满足用Y和N表示,其中Y表示事务T2的封锁要求与T1已持有的锁相容,封锁请求可以满足。N表示T2的封锁请求与T1已持有的锁冲突,T2请求被拒绝。



    2.    封锁粒度

    X锁和S锁都是加在某一个数据对象上的。封锁的对象可以是逻辑单元,也可以是物理单元。例如,在关系数据库中,封锁对象可以是属性值、属性值集合、元组、关系、索引项、整个索引、整个数据库等逻辑单元;也可以是页(数据页或索引页)、块等物理单元。封锁对象可以很大,比如对整个数据库加锁,也可以很小,比如只对某个属性值加锁。封锁对象的大小称为封锁的粒度(granularity)。

    封锁粒度与系统的并发度和并发控制的开销密切相关。封锁的粒度越大,系统中能够被封锁的对象就越小,并发度也就越小,但同时系统开销也越小;相反,封锁的粒度越小,并发度越高,但系统开销也就越大。
    因此,如果在一个系统中同时存在不同大小的封锁单元供不同的事务选择使用是比较理想的。而选择封锁粒度时必须同时考虑封锁机构和并发度两个因素,对系统开销与并发度进行权衡,以求得最优的效果。一般说来,需要处理大量元组的用户事务可以以关系为封锁单元;需要处理多个关系的大量元组的用户事务可以以数据库为封锁单位;而对于一个处理少量元组的用户事务,可以以元组为封锁单位以提高并发度。

    3.    封锁协议

    封锁的目的是为了保证能够正确地调度并发操作。为此,在运用X锁和S锁这两种基本封锁,对一定粒度的数据对象加锁时,还需要约定一些规则,例如,应何时申请X锁或S锁、持锁时间、何时释放等。我们称这些规则为封锁协议(locking protocol)。对封锁方式规定不同的规则,就形成了各种不同的封锁协议,它们分别在不同的程度上为并发操作的正确调度提供一定的保证。本节介绍保证数据一致性的三级封锁协议和保证并行调度可串行性的两段锁协议,下一节将介绍避免死锁的封锁协议。

    (5)    保证数据一致性的封锁协议――三级封锁协议

    对并发操作的不正确调度可能会带来四种数据不一致性:丢失或覆盖更新、脏读、不可重复读和幻想读。三级封锁协议分别在不同程度上解决了这一问题。

    ①    1级封锁协议

    1级封锁协议的内容是:事务T在修改数据R之前必须先对其加X锁,直到事务结束才释放。事务结束包括正常结束(commit)和非正常结束(rollback)。

    1级封锁协议可以防止丢失或覆盖更新,并保证事务T是可以恢复的。例如,下图使用1级封锁协议解决了定飞机票例子的丢失更新问题。



    上图中,事务1在读A进行修改之前先对A加X锁,当事务2再请求对A加X锁时被拒绝,只能等事务1释放A上的锁。事务1修改值A=15写回磁盘,释放A上的X锁后,事务2获得对A的X锁,这时他读到的A已经是事务1更新过的值15,再按此新的A值进行运算,并将结果值A=14回到磁盘。这样就避免了丢失事务1的更新。

    在1级封锁协议中,如果仅仅是读数据不对其进行修改,是不需要加锁的,所以它不能保证可重复读和脏读。

    ②  2级封锁协议

    2级封锁协议的内容是:1级封锁协议加上事务T在读取数据R之前必须先对其加S锁,读完后即可释放S锁。
    2级封锁协议除防止了丢失或覆盖更新,还可进一步防止脏读。例如,下图使用2级封锁协议解决了脏读的问题。
    |
    下图中,事务1在对C进行修改之前,先对C加X锁,修改其值后写回磁盘。这时事务2请求C加上S锁,因T1已在C上加了X锁,事务2只能等待事务1释放它。之后事务1因某种原因被撤销,C恢复为原值100,并释放C上的X锁。事务2获得C上的S锁,读C=100。这就避免了事务2脏读数据。

    在2级封锁协议中,由于读完数据后即可释放S锁,所以它不能保证可重复读。



    ③    3级封锁协议

    3级封锁协议的内容是:1级封锁协议加上事务T在读取数据之前必须先对其加S锁,直到事务结束才释放。
    3级封锁协议除防止丢失或覆盖更新和不脏读数据外,还进一步防止了不可重复读和幻想读。例如下图,使用3级封锁协议解决了不可重复读和幻像读问题。



    上图中,事务1在读A,B之前,先对A,B加S锁,这样其他事务只能再对A,B加S锁,而不能加X锁,即其他事务只能读A,B,而不能修改它们。所以当事务2为修改B而申请对B的X锁时被拒绝,使其他无法执行修改操作,只能等待事务1释放B上的锁。接着事务1为验算再读A,B,这时读出的B仍是100,求和结果仍为150,即可重复读。

    上述三级协议的主要区别在于什么操作需要申请封锁以及何时释放锁(即持锁时间)。三级封锁协议可以总结为下表。



    (6)    保证并行调度可串行性的封锁协议――两段封锁协议

    可串行性是并行调度正确性的唯一准则,两段锁(two-phase locking,简称2PL)协议是为保证并行调度可串行性而提供的封锁协议。

    两段封锁协议规定:

    ①在对任何数据进行读、写操作之前,事务首先要获得对该数据的封锁,而且②在释放一个封锁之后,事务不再获得任何其他封锁。

    所谓“两段”锁的含义是,事务分为两个阶段,第一阶段是获得封锁,也称为扩展阶段,第二阶段是释放封锁,也称为收缩阶段。

    例如,事务1的封锁序列是:

    Slock A... Slock B… Xlock C… Unlock B… Unlock A… Unlock C;

    事务2的封锁序列是:

    Slock A... Unlock A… Slock B… Xlock C… Unlock C… Unlock B;

    则事务1遵守两段封锁协议,而事务2不遵守两段封锁协议。

    可以证明,若并行执行的所有事务均遵守两段锁协议,则对这些事务的所有并行调度策略都是可串行化的。因此我们得出如下结论:所有遵守两段锁协议的事务,其并行的结果一定是正确的。

    需要说明的是,事务遵守两段锁协议是可串行化调度的充分条件,而不是必要条件。即可串行化的调度中,不一定所有事务都必须符合两段封锁协议。例如,在下图中,(a)和(b)都是可串行化的调度,但(a)遵守两段锁协议,(b)不遵守两段锁协议。



    4.    死锁和活锁

    封锁技术可以有效地解决并行操作的一致性问题,但也带来一些新的问题,即死锁和活锁的问题。

    (1)    活锁

    如果事务T1封锁了数据对象R后,事务T2也请求封锁R,于是T2等待。接着T3也请求封锁R。T1释放R上的锁后,系统首先批准了T3的请求,T2只得继续等待。接着T4也请求封锁R,T3释放R上的锁后,系统又批准了T4的请求……,T2有可能就这样永远等待下去。这就是活锁的情形,如下图所示。



    避免活锁的简单方法是采用先来先服务的策略。当多个事务请求封锁同一数据对象时,封锁子系统按请求封锁的先后次序对这些事务排队,该数据对象上的锁一旦释放,首先批准申请队列中第一个事务获得锁。

    (2)    死锁

    如果事务T1封锁了数据A,事务T2封锁了数据B。之后T1又申请封锁数据B,因T2已封锁了B,于是T1等待T2释放B上的锁。接着T2又申请封锁A,因T1已封锁了A,T2也只能等待T1释放A上的锁。这样就出现了T1在等待T2,而T2又在等待T1的局面,T1和T2两个事务永远不能结束,形成死锁。如下图所示。



    死锁问题在操作系统和一般并行处理中已做了深入研究,但数据库系统有其自己的特点,操作系统中解决死锁的方法并不一定合适数据库系统。

    目前在数据库中解决死锁问题主要有两类方法,一类方法是采取一定措施来预防死锁的发生,另一类方法是允许发生死锁,采用一定手段定期诊断系统中有无死锁,若有则解除之。

    ①    死锁的预防
    在数据库系统中,产生死锁的原因是两个或多个事务都已封锁了一些数据对象,然后又都请求对已为其他事务封锁的数据对象加锁,从而出现死锁等待。防止死锁的发生其实就是要破坏产生死锁的条件。预防死锁通常有两种方法。

    ◆ 一次封锁法
    一次封锁法要求每个事务必须一次将所有要使用的数据全部加锁,否则就不能继续执行。例如,在上图的例子中,如果事务T1将数据对象A和B一次加锁,T1就可以执行下去,而T2等待。T1执行完后释放A,B上的锁,T2继续执行。这样就不会发生死锁。

    一次封锁法虽然可以有效地防止死锁的发生,但也存在问题。第一,一次就将以后要用到的全部数据加锁,势必扩大了封锁的范围,从而降低了系统的并发度。第二,数据库中数据是不断变化的,原来不要求封锁的数据,在执行过程中可能会变成封锁对象,所以很难实现精确地确定每个事务所要封锁的数据对象,只能采取扩大封锁范围,将事务在执行过程中可能要封锁的数据对象全部加锁,这就进一步降低了并发度。

    ◆    顺序封锁法
    顺序封锁法是预先对数据对象规定一个封锁顺序,所有事务都按这个顺序执行封锁。在上例中,我们规定封锁顺是A,B,T1和T2都按此顺序封锁,即T2也必须先封锁A。当T2请求A的封锁时,由于T1已经封锁住A,T2就只能等待。T1释放A,B上的锁后,T2继续运行。这样就不会发生死锁。

    顺序封锁法同样可以有效地防止死锁,但也同样存在问题。第一,数据库系统中可封锁的数据对象及其众多,并且随数据的插入、删除等操作而不断地变化,要维护这样极多而且变化的资源的封锁顺序非常困难,成本很高。

    第二,事务的封锁请求可以随着事务的执行而动态地决定,很难事先确定每一个事务要封锁哪些对象,因此也就很难按规定的顺序取施加封锁。例如,规定数据对象的封锁顺序为A,B,C,D,E。事务T3起初要求封锁数据对象B,C,E,但当它封锁B,C后,才发现还需要封锁A,这样就破坏了封锁顺序。

    可见,在操作系统中广为采用的预防死锁的策略并不很适合数据库的特点,因此DBMS在解决死锁的问题上更普遍采用的是诊断并解除死锁的方法。
    ②    死锁的诊断与解除
    数据库系统中诊断死锁的方法与操作系统类似,即使用一个事务等待图,它动态地反映所有事务的等待状况。并发控制子系统周期性地(比如每隔1分钟)检测事务等待图,如果发现图中存在回路,则表示系统中出现了死锁。关于诊断死锁的详细讨论请参阅操作系统的有关书籍。
    DBMS的并发控制子系统一旦检测到系统中存在死锁,就要设法解除。通常采用的方法是选择一个处理死锁代价最小的事务,将其撤销,释放此事务持有的所有的锁,使其他事务能继续运行下去。
    二、    DM的并发控制
    (一)    事务隔离级
    事务的隔离级描述了给定事务的行为对其它并发执行事务的暴露程度。 SQL-92共规定了四种隔离级别,通过选择四个隔离级中的一个,用户能增加对其它未提交事务的暴露程度,获得更高的并发度。隔离级别是一个事务必须与其它事务进行隔离的程度。
    SQL-92的四种隔离级别如下所示,DM支持所有这些隔离级别:
    (1)脏读(READ UNCOMMITTED):事务隔离的最低级别,事务可能查询到其它事务未提交的数据, 仅可保证不读取物理损坏的数据)。
    (2)读提交(READ COMMITTED):DM默认级别,保证不读脏数据。 
    (3)可重复读(REPEATABLE READ):保证不可重复读,但有可能读入幻像数据。
    (4)可串行化(SERIALIZABLE):事务隔离的最高级别,事务之间完全隔离。
    DM允许用户改变未启动的事务的隔离级和读写特性 ,而且设置的选项将一直对那个连接保持有效,直到显式更改该选项为止。设置事务隔离级别虽然使程序员承担了某些完整性问题所带来的风险,但可以换取对数据更大的并发访问权。与以前的隔离级别相比,每个隔离级别都提供了更大的隔离性,但这是通过在更长的时间内占用更多限制锁换来的。DM还提供设置事务只读属性的语句,使用该语句后该事务只能做查询操作,不能更新数据库。
    需要注意的是,事务的隔离级别并不影响事务查看本身对数据的修改,也就是说,事务总可以查看自己对数据的修改。事务的隔离级别需要根据实际需要设定,较低的隔离级别可以增加并发,但代价是降低数据的正确性。相反,较高的隔离级别可以确保数据的正确性,但可能对并发产生负面影响。应用程序要求的隔离级别确定了DM使用的锁定行为。
    下表中列出四种隔离级别允许不同类型的现象



    注意:丢失或覆盖更新在所有的标准SQL隔离级中都是禁止的。

    (二)    并发处理

    1.    数据锁定机制

    DM用数据锁定机制来解决并发问题。它可以保证任何时候都可以有多个正在运行的事务,但是所有事务都在彼此完全隔离的环境中运行。

    DM的封锁对象为表和元组。封锁的实施有自动和手动两种,即隐式上锁和显式上锁。隐式封锁动作的封锁根据事务的隔离级有所不同。同时, DM提供给用户4种手动上锁语句,用以适应用户定义的应用系统。

    一般而言, DM的隐式封锁足以保证数据的一致性,但用户可以根据自己的需要改变对表的封锁。 DM提供给用户四种表锁:意向共享锁(IS:INTENSIVE SHARE)、共享锁(S:SHARE)、意向排它锁(IX:INTENSIVE EXCLUSIVE)和排它锁(X:EXCLUSIVE)。例如,在读提交隔离级下,系统缺省的表锁是 IS或IX ,在这两种表锁下,在访问元组前还需对元组进行封锁,为了提高系统的效率,用户可以手动对表进行 X封锁,这样,就不需对访问元组封锁。

    封锁机制要达到以下目的:

    (1)一致性:保证用户正在查看时,改变的数据并未从根本上发生变化。

    (2)完整性:保证数据库的基本结构以正确的顺序,准确地反映对它们的所有改变。

    一个“ 锁定” 可以认为是当某一进程需要防止其它进程做某事时获得的某种东西,当该进程不再关心此事时就 “释放 ”此锁定,通常一个锁定是加在某个 “资源 ”(某些客体,如表 )上的。

    DM的内部锁定是自动完成的。当某一进程要查看一个客体但不允许其他人修改它时,就获得一个共享方式的锁定。当某一进程要修改一客体,并且防止任何其它进程修改它时,就获得更新方式的锁定。当某一进程要修改一客体,并且防止任何其它进程修改它或以共享方式封锁它时,就获得独占方式的锁定。

    2.    锁定类型

    DM中的锁有三种,表锁、行锁和键范围锁。

    ◆   表锁
    表锁用来封锁表对象,在对表进行检索和更新时,DM会对表对象进行封锁,但是DM为用户提供手动的表锁语句,用户可以根据自己的需要改变对表的封锁类型。表锁的模式:意向共享锁 IS,意向排它锁 IX,共享锁 S,排它锁 X,共四种,其相容矩阵可定义如下表。



    ◆   行锁
    行锁封锁元组,在存取元组和更新元组前, DM会对元组上行锁,系统不提供手动的行封锁语句。行锁有两种模式:共享锁(S)、排它锁(X),其相容矩阵定义如下表。



    ◆     键范围锁
    键范围锁用在可串行事务上,主要解决了幻像读并发问题。键范围锁覆盖单个记录以及记录之间的范围,可以防止对事务访问的记录集进行幻像插入或删除。键范围锁仅用于在可串行隔离级别上操作的事务。
    可串行性要求,如果任意一个查询在一个事务中后面的某一时刻再次执行,其所获取的行集应与该查询在同一事务中以前执行时所获得的行集相同。如果本查询试图提取的行不存在,则在试图访问该行的事务完成之前,其它事务不能插入该行。如果允许另一个事务插入该行,则它将以幻像出现。
    如果另一个事务试图插入驻留在锁定数据页上的行,页级锁定可以防止添加幻像行,并维护可串行性。但是,如果该行要添加到未被第一个事务锁定的数据页,应设定锁定机制防止添加该行。
    键范围锁通过覆盖索引行和索引行之间的范围来工作(而不是锁定整个基础表的行)。因为第二个事务在该范围内进行任何行插入、更新或删除操作时均需要修改索引,而键范围锁覆盖了索引项,所以在第一个事务完成之前会阻塞第二个事务的进行。
    键范围锁由系统自行执行,执行的条件是: (1) 事务隔离级为可串行化级; (2) 查询结果通过某个索引得出。
    用户上锁成功后锁将一直有效,直到当前事务结束时,该锁被系统自动解除。

    3.    锁定类型比较



    4.    SQL语句锁定分析
    DM对各种 DDL和GRANT 等非DML 语句都分解为增、删、改。下表为DM对各种DML语句和查询语句的封锁策略。

    表:SQL语句封锁策略



    注:S* 表示瞬时锁,在语句结束后释放;Range表示键范围锁。

    上表只是系统在一般情况下的处理,当系统检测到有锁升级的可能,则会升级锁。一般而言,IS锁升级为 S锁,IX锁升级为 X锁,同时,不再进行行封锁。

    5.    自定义锁定提高系统效率

    DM也提供了两个函数 SET_TABLE_OPTION([db.][sch.]tablename, option, value) 、SET_INDEX_OPTION([db.]indexname, option, value)(具体语法参见《 DM_SQL语言使用手册》第 8 章)供用户自行定义锁定类型,以增强系统并发度,提高系统效率。这两个函数是为那些清楚地知道特定类型的锁适用于何种情况的专家级用户提供的。

    函数SET_TABLE_OPTION() 用于禁用指定表上的页级锁、行级锁或同时禁用二者,这一设置对该表上的所有索引都生效。函数 SET_INDEX_OPTION() 则用于禁用某一索引上的页级锁、行级锁或同时禁用二者。

    例如,当用户只需要修改索引中某定长字段时,修改操作不会造成 B 树的分裂与合并,此时就可以禁用该索引的页级锁。又如,当所有的用户都只做插入操作时,用户之间并不会对同一元组进行操作,此时就可以禁用行级锁。当用户能保证不对表进行增、删、改,而只是进行查询时,则可以同时禁用该表上的页级锁和行级锁,此时并发度最高。

    6.    死锁处理

    解决死锁问题的三种方法:预防死锁、检测死锁及避免死锁。死锁预防要求用户进程事先申报所需的资源或按严格的规程申请资源,而死锁检测原则上应允许死锁发生,在适当的时机检查,若发生死锁,则设法排除之。与预防死锁相比,后者过于放手,致使死锁频繁。而避免死锁则以事务撤消为前提,当不能获得资源批准时,立刻进行死锁检测。它既不象预防死锁那样过于保守,也不象死锁检测那样过于放开,由于检测及时,由归纳法可知,在已获准等待的事务中,不可能存在死锁,所以检测算法比较简单。

    DM4系统采用的是避免死锁方法。每当一个事务所申请占有的资源不能被立即获得时,便进行死锁检测,不存在死锁,则该事务入等待队列。否则,DM4视为产生运行时错误,将当前语句回滚。采用这种机制,从用户的角度看,DM4不存在解锁问题。

    7.    加索引和不加索引的封锁区别

    加索引和不加索引的情况下,DM的封锁机制会影响到实际的封锁范围。索引的作用就在于,可以在查询中减少对无关数据的扫描。而在一般的隔离级中,总是要对扫描到的数据进行封锁。所以,利用索引可以减少封锁的数量,冲突的可能性也会大大减少。



    展开全文
  • 数据库的并发控制技术

    千次阅读 2011-05-10 15:53:00
    数据库的基本概念总结 1. 数据库定义:数据库是长期储存在计算机内、有组织的、...主要功能:1,数据定义功能。2,数据组织、存储和管理。3,数据操纵功能。4,数据库的事务管理和运行管理。5,数据库的建立和维护功能
    数据库的基本概念总结
    1. 数据库定义:数据库是长期储存在计算机内、有组织的、可共享的大量数据的集合。数据库中的数据按一定的数据模型组织、描述和储存,具有较小的冗余度、较高的数据独立性和易扩展性,并可为各种用户共享。
    2. 数据库管理技术发展的三个阶段:人工管理阶段,文件系统阶段,数据库系统阶段。
    3. DBMS(数据库管理系统)是位于用户与操作系统之间的一层数据管理软件。主要功能:1,数据定义功能。2,数据组织、存储和管理。3,数据操纵功能。4,数据库的事务管理和运行管理。5,数据库的建立和维护功能。6,其他功能。
    4. 什么是数据模型及其要素? (设计题): 数据模型是数据库中用来对现实世界进行抽象的工具,是数据库中用于提供信息表示和操作手段的形式构架。一般地讲,数据模型是严格定义的概念的集合。这些概 念精确地描述系统的静态特性、动态特性和完整性约束条件。因此数据模型通常由数据结构、数据操作和完整性约束三部分组成。 (1)数据结构:是所研究的对象类型的集合,是对系统的静态特性的描述。 (2)数据操作:是指对数据库中各种对象(型)的实例(值)允许进行的操作的集合,包括操作及有关的操作规则,是对系统动态特性的描述。 (3)数据的约束条件:是完整性规则的集合,完整性规则是给定的数据模型中数据及其联系所具有的制约和依存规则,用以限定符合数据模型的数据库状态以及状态的变化,以保证数据的正确、有效、相容。
    最常用的数据模型:层次模型,网状模型,关系模型,面积对象模型,对象关系模型。
    5.常用的数据模型有哪些(逻辑模型是主要的),各有什么特征,数据结构是什么样的。
    答:数据模型可分为两类:
    第一类是概念模型,也称信息模型,它是按用户的观点来地数据和信息建模,主要用于数据库设计。
    第二类是逻辑模型和物理模型。
    其中逻辑模型主要包括层次模型、层次模型、关系模型、面向对象模型和对象关系模型等。它是按计算机系统的观点对数据建模,主要用于DBMS的实现。
    物理模型是对数据最低层的抽象,它描述数据在系统内部的表示方式和存取方法,在磁盘或磁带上的存储方式和存取方法,是面向计算机系统的。物理模型是具体实现是DBMS的任务,数据库设计人员要了解和选择物理醋,一般用户则不必考虑物理级的细节。
    层次数据模型的数据结构特点:一是:有且只有一个结点没有双亲结点,这个结点称为根结点。二是:根 以外的其他结点有且只有一个双亲结点。优点是:1.层次 数据结构比较简单清晰。2.层次数据库的查询效率高。3.层次数据模型提供了良好的完整性支持。缺点主要有:1.现实世界中很多联系是非层次性的,如结点之间具有多对多联系。2.一个结点具有多个双亲等 ,层次模型表示这类联系的方法很笨拙,只能通过引入冗余数据或创建非自然的数据结构来解决。对插入和删除操作的限制比较多,因此应用程序的编写比较复杂。3.查询子女结点必须通过双亲结点。4.由于结构严密,层次命令趋于程序化。可见用层次模型对具有一对多的层次联系的部门描述非常自然,直观容易理解,这是层次数据库的突出优点。
    网状模型:特点:1.允许一个以上的结点无双亲2.一个结点可以有多于一个的双亲。网状数据模型的优点主要有:1.能够更为直接地描述现实世界,如一个结点可以有多个双亲。结点
    之间可以有多种上联第。2.具有良好的性能,存取效率较高。缺点主要有:1.结构比较复杂,而且随着应用环境的扩大,数据库的结构就变得越来越复杂,不利于最终 用户掌握。2.网状模型的DDL,DML复杂,并且要嵌入某一种高级语言中,用户不容易掌握,不容易使用。
    关系数据模型具有下列优点:1.关系模型与非关系模型不同,它是建立在严格的数学概念的基础上的。2.关系模型的概念单一。。3.关系模型的存取路径对用户透明,从而具有更高的数据独立性,更好的安全保密性,也简化了程序员的工作和数据库开发的建立 的工作。。主要的缺点是:由于存取路径房租明,查询效率往往不如非关系数据模型。因此为了提高性能,DBMS必须对用户的查询请求进行优化。因此增加 了开发DBMS的难度,不过用户不必考虑这些系统内部的优化技术细节。
    6.三级体系结构,外模式,模式 ,内模式定义是什么?
    模式也称逻辑模式,是数据库中全体数据的逻辑结构和牲的描述,是所有用户的公共数据视图。 外模式也称子模式或用户模式,它是数据库用户能够看见和使用的局部数据的逻辑结构和特征的描述,是数据库用户的数据视图是与某一应用有关的数据的逻辑表示。 内模式也称存储模式 ,是一个数据库只有一个内模式。它是数据物理结构和存储方式的描述,是数据在数据库内部的表示方式。
    7.两级映像和两级独立性,为什么叫物理独立性和逻辑独立性。
    当模式改变时由数据库管理员对各个外模式、模式的映像亻相应改变,可以使外模式保持不变。应用程序是依据数据的外模式编写的,从而应用程序不必修改,保证了数据与程序的逻辑独立生,简称数据的逻辑独立性。
    当数据库的存储结构改变了,由数据库管理员对模式、内模式映像作 相应改变,可以使模式保持不变,从而应用程序也不必改变。保证了数据与程序的物理独立性,简称数据的物理独立性。
    8.数据库系统一般由数据库、数据库管理系统 (及其开发工具)、应用系统和数据库管理员构成。
    9.关系的完整性(实体完整性、参照完整性、和用户定义的完整性)三部分内容,其中前二者是系统自动支持的,DBMS完整性控制子系统的三个主要功能?:提供定义完整性约束条件的机制,提供完整性检查的方法,违约处理。
    16.SQL的定义;即结构化查询语言,是关系数据库的标准语言,是一个通用的、功能极强的关系数据库语言。分类(交互式和嵌入式)17.group by 和having子句的作用
    20.视图的概念:视图是从一个或几个基本表导出的表。及相关操作:定义视图,查询视图,更新视图。视图更新有什么操作:插入,删除,和修改。
    22.数据库规范化的方法函数依赖的定义什么叫1NF2NF3NF BCNF定义:关系数据库中的关系是要满足一定要求的,满足不同程度要求的为不同范式。满足最低要求的叫第一范式,简称1NF。在第一范式中满足进一步要求的为第二范式,其余以此类推。各种范式之间的联系有:5NF(4NF(BCNF(3NF(2NF(1NF。
    25.数据库设计的几个阶段,每个阶段常用的方法和简要的内容:六个阶段:需求分析、概念结构设计、罗织结构设计、物理设计、数据库实施、数据库运行和维护。
    28.事务的概念?事务有哪些基本属性commit roll back含义:事务:是用户定义的一个数据库操作序列,这些操作要么全做,要么全不做,是一个不可分割的单位。四个特性:原子性,一致性,隔离性,持续性。Commit(提交:提交事务的所有操作) rollback(回滚:在事务运行的过程中发生了某种故障,事务不能继续执行,系统将事务中对数据库的所有已完成的操作全部撤销,回滚到事务开始时的状态。
    29.什么叫数据库系统的可恢复性?:数据库管理系统具有把数据库从错误状态恢复到某一已知的正确状态的功能,这就是数据库系统的可恢复性。数据库故障的种类:事务内部的故障,系统故障(软故障),介质故障(硬故障),计算机病毒。
    30.不进行并发控制可能产生的问题?:多个事务对数据库并发操作可能造成事务ACID特点遭到在破坏。如何解决(三个):1,丢失修改 2,不可重复读 3,读“脏”数据。
    31.三级封锁协议?能解决什么问题?:一级封锁协议:事务T在修改数据R之前必须先对其加X锁,直到事务结束才释放。事务结束包括正常结束(COMMIT)和非正常结束(ROLLBACK)。一级封锁协议中,如果是读数据不修改,是不需要加锁的,可防止丢失修改。二级封锁协议:在一级封锁协议基础上,加上事务T在读数据R之前必须先对其加上S锁,读完后即可释放S锁。在二级封锁协议中,由于读完数据后即可释放S锁,所以它不能保证可重复读。三级封锁协议:一级封锁协议加上事务T在读取数据R之前必须先对其加S锁,直到事务结束才释放。三级封锁协议除了防止了丢失修改和不读“脏”数据外,还进一步防止了不可重复读。
    上述三级协议的主要区别在于:什么操作需要申请封锁,以及何时释放锁。
    一般采取哪三种措施?插入呢?删除呢?:1,拒绝执行(不允许该操作执行),2,级连操作(当删除或修改被参照表的一个元组造成了与参照表的不一致,则删除或修改参照表中的所有造成不一致的元组),3,设置为空值(当删除或修改被参照表的一个元组时造成了不一致,则将参照表中的所有不造成不一致的元组的对应属性设置为空值)。
    38.视图对数据库安全性的作用?:1,视图能够简化用户的操作,2,视力使用户能以多种角度看待同一数据,3,视图对重构数据库提供了一定程度的逻辑独立性,4,视图能够对机密数据提供安全保护,5,适当的利用视图可以更清晰的表达查询。
    数据库:储存在计算机内,永久存储、有组织、有共享的大量数据的集合。
    数据管理技术的发展阶段:1.人工管理阶段:数据不保存,应用程序管理数据,数据不共享,数据不具有独立性。2.文件系统阶段:数据可以长期保存,由文件系统管理数据;数据共享性太差,冗余度大,数据独立性差。3.数据库系统阶段:出现数据库管理系统。
    数据库系统的特点:数据结构化(本质区别);数据共享性高、冗余度低、易扩充;数据独立性高;数据有DBMS统一管理和控制。
    数据库管理系统:1.定义:DBMS,是位于用户与操作系统之间的一层数据管理软件。2.功能:数据定义功能;数据组织、存储和管理;数据操纵功能;数据库的事务管理和运行管理;数据库的建立和维护功能;通信功能、数据转换功能、互访和互操作功能。
    数据库系统:1.概念:DBS,是指在计算机系统中引入数据库后的系统。2.组成:一般由数据库、数据库管理系统、应用系统、数据库管理员构成。3.分类:集中式,C/S式,并行式,分布式。
    数据模型:1.定义:现实世界数据特征的抽象。2.组成,三要素:数据结构、数据操作、数据的完整性约束。两类数据模型为1)概念模型2)逻辑模型和物理模型。
    数据结构:描述数据库的组成对象以及对象之间的联系,主要描述与对象的类型、内容、性质有关的对象和与数据之间联系有关的对象。
    常用的数据模型:1.层次模型,用树形结构表示各类实体以及实体间的联系。2.网状模型,允许一个以上的结点无双亲,允许一个结点可以有多于一个的双亲。3.关系模型,包含单一数据结构
    
    
    数据库的并发控制技术
    
    数据库是一个共享资源,可以供许多用户使用。各个用户程序(通常为一个事务)可以一个一个地串行执行,即每个时刻只有一个用户程序运行,执行数据库的存取操作,其它用户程序必须等到这个用户程序结束后方能对数据库存取。但是,由于用户程序在执行过程中随着时间的不同需要不同的资源,有时需要CPU,有时需要访问磁盘,有时需要I/O,有时需要通讯等,如果只让一个用户程序运行,而其它用户闲置等待,则导致许多系统资源在大部分时间内处于闲置状态。 因此,为了充分利用系统资源,发挥数据库共享资源的特点,应该允许各个用户并行地存取数据。但这样就会产生多个用户程序并发存取同一数据的情况。若对并发操作不加控制就可能导致存取和存储不正确的数据,从而破坏数据库的一致性。
    所以DBMS必须提供并发控制机制,且并发控制机制的好坏是衡量一个DBMS性能的重要指标之一。
    
    一.事务及其特性
    DBMS的并发控制是以事务(transaction)为单位进行的,因此,本节先介绍事务的概念及其特性。
    在多用户环境中用户程序不断访问数据库中的数据,构成了若干操作序列,在这些操作序列中,有些操作必须作为一个整体来执行,这些作为一个整体来执行的操作序列称为一个事务(Transaction)。即事务是用户定义的一组操作序列的集合,是数据恢复和并发控制的基本单位。数据库系统在执行事务时,要么执行事务中全部操作,要么一个操作都不执行。一个应用程序往往由若干个独立的事务组成。在应用程序中,事务的开始与结束可以由用户显示地控制。如果用户没有显示地定义事务,则由DBMS自动地按照缺省方式划分事务。在SQL中,用户显示定义事务的语句有如下3条:
    1、BEGIN TRANSACTION,该语句显示地定义一个事务的开始。
    2、COMMIT,该语句显示地提交一个事务,并表示该事务已正常结束。
    3、ROLLBACK,该语句显示地回滚一个事务,且表示事务因执行失败而结束。 
    所谓提交事务,就是将该事务对数据库的所有更新操作结果永久地保存到磁盘上的物理数据库中去。
    所谓回滚事务,就是撤消该事务对数据库的所有更新操作,使数据库恢复到该事务开始时的状态。
    由以上可知,用户若要显示地定义事务,必须以BEGIN TRANSATION开始,而以COMMIT 或 ROLLBACK结束。 
    事务具有四个特性,即原子性、一致性、隔离性和持续性,又常简称为ACID特性:
    1、原子性 :即一个事务是不可分割的数据库逻辑工作单位。
    2、一致性 :事务的执行结果必须使数据库从一个一致性状态变到另一个一致性状态。
    3、隔离性 :一个事务的执行不能被其它事务干扰。
    4、持续性 :持续性也称为永久性,指一个事务一旦提交,它对数据库中数据的改变应该是永久性的,其它操作或故障不对其产生任何影响。
    
    二.数据库的并发控制
    DBMS的并发控制是以事务为单位进行的。数据库在执行事务时,要么执行事务中的全部操作,要么一个操作都不执行。
    当有多个事务对数据库进行操作时,如果对数据库进行操作的各个事务按顺序执行,即一个事务执行完全结束后,另一个事务才开始,则称这种执行方式为串行访问(Serial access)。
    如果DBMS可以同时接纳多个事务,事务可以在时间上重叠执行,则称这种执行方式为并发访问(Concurrent access)。
    在单CPU系统中,同一时间只能有一个事务占用CPU,若各个事务交叉使用CPU,则称这种并发方式为交叉并发。
    在多CPU系统中,可以允许多个事务同时占用CPU,这种并发方式称为同时并发。
    
    三.并发的目的
    在数据库管理系统中对事务采用并发机制的主要目的有两个:
    1、改善系统的资源利用率
    对于一个事务来讲,在不同的执行阶段需要的资源不同,有时需要CPU,有时需要访问磁盘,有时需要I/O、有时需要通信如果事务串行执行,有些资源可能会空闲;如果事务并发执行,则可以交叉利用这些资源,有利于提高系统资源的利用率
    2、改善短事务的响应时间
    设有两个事务T1 和T2,其中T1是长事务,交付系统在先;T2是短事务,交付系统比T1稍后。如果串行执行,则须等T1执行完毕后才能执行T2。而T2的响应时间会很长。一个长事务的响应时间长一些还可以得到用户的理解,而一个短事务的响应时间过长,用户一般难以接受。
    如果T1 和T2并发执行,则T2可以和T1重叠执行,可以较快地结束,明显地改善其响应时间。
    
    四.并发所引起的问题
    数据库中的数据是共享的,即多个用户可以同时使用数据库中的数据,这就是并发操作。
    但是当多个用户存取同一组数据时,由于相互的干扰和影响,并发操作可能引发错误的结果,从而导致数据的不一致性问题我们将用下面的例子予以说明。
    例5.5 设有一个飞机机票订票系统,考虑如下售票活动的并发操作问题:
    1、售票员A通过网络在数据库中读出某航班的机票余额为y张,设y=15;
    2、售票员B通过网络在数据库中读出某航班的机票余额为y张,设y=15;
    3、售票员A卖出一张机票,然后修改余额为y=y?1,此时,y=14,将14写会数据库;
    4、售票员B卖出一张机票,然后修改余额为y=y?1,此时,y=14,将14写会数据库;
    这个售票活动并发操作的结果是:实际已经卖出2张机票,但在数据库中机票余额仅减少1,从而导致错误。这就是著名的飞机机票订票系统问题。
    这个问题之所以会产生错误,其根本原因在于两个用户反复交叉地使用同一个数据库的结果。 
    如果事务不加控制地并发执行,会产生以下几个问题: 
    1、丢失修改(Lost update) 
    2、脏读(Dirty read)。 
    3、不能重读(Unrepeatable read)
    
    五.并发控制方法
    实现现数据库并发控制的方法有多种,常用的有封锁技术、时标技术和版本更新技术等,而封锁技术在商品化数据库管理系统中使用得最为普遍。
    本节中介绍:
    (1)封锁技术
    (2)时标技术 
    
    1.封锁技术
    封锁的定义
    锁是为了防止其它事务访问指定资源的一种手段。封锁(locking)是实现并发控制的一个非常重要的技术。
    所谓封锁即是在一段时间内禁止某些用户对数据对象做某些操作,以避免产生数据的不一致性问题。即事务T在对某个数据对象,如表、元组等进行操作之前,先向系统发出请求,并对其加锁。加锁成功后,事务T就对数据对象有了一定的控制权,在事务T释放它的锁之前,其它的事务就不能更新此数据对象。
    封锁的类型
    基本的封锁一般有排它锁和共享锁两种类型。
    (1)排它锁:又称X锁,如果某事务T对某数据建立了排它锁,则该事务能对该数据对象进行读、修改、插入和删除等操作,而其它事务则不能。
    (2)共享锁:又称S锁,如果某事务对某数据建立了共享锁,则此时该事务能对该数据对象进行读操作,但不能进行修改等更新操作;而其它事务只能对该数据对象加S锁,而不能加X锁,即其它事务只能对该数据对象进行读操作。
    
    封锁粒度
    所谓封锁的粒度,即是被封锁数据对象的大小。在关系数据库中,封锁的粒度有如下几种:属性值、属性值集、元组、关系、索引项、整个索引或整个数据库。还可以是一些物理单元:页(数据页或索引页)、块等等。
    封锁的粒度与系统的并发度和并发控制的开销密切相关。封锁粒度越大,系统中能够被封锁的对象就越少,并发度也就越小,但同时系统开销也越少;封锁粒度越小,并发度越高,但系统开销也就越大。因此,如果在一个系统中能同时存在不同大小的封锁粒度对象供不同的事务选择使用,是比较理想的。
    
    封锁协议
    封锁的目的是为了保证能够正确地调度和控制并发操作。为此,在运用X锁和S锁这两种基本锁对一定粒度的数据对象加锁时,还需约定一些规则。
    例如,一个事务何时该申请X锁或S锁、事务持锁时间的长短如何控制、何时释放所获得的锁等等。我们称这些规则为封锁协议 (Locking protocol)。 
    下面介绍三级封锁协议:
    1、一级封锁协议:某事务T若要修改某个数据对象,则必须先对该数据对象加X锁,直到事务结束才释放。此种封锁协议可防止“丢失修改”所产生的数据不一致性问题。
    2、二级封锁协议:一级封锁协议加上某事务T若要读取某个数据对象之前,则必须先对该数据对象加S锁,读完后即可释放S锁,这样可进一步防止“读脏数据”的问题。
    3、三级封锁协议:一级封锁协议加上某事务T若要读取某个数据对象之前,则必须先对该数据对象加S锁,且知道该事务结束后才可释放S锁,这样可进一步防止数据“不可重复读”的问题。
    
    死锁和活锁
    活锁:如果事务T1封锁了数据对象R后,事务T2也请求封锁R,于是T2等待。接着T3也请求封锁R。当T1释放了加在R上的锁后,系统首先批准了T3的请求,T2只得继续等待。接着T4也请求封锁R,T3释放R上的锁后,系统又批准了T4的请求,……,因此,事务T2就有可能这样永远地等待下去。以上这种情况就称为活锁。
    避免活锁的简单办法是采用先来先服务的策略。当多个事务请求封锁同一数据对象时,封锁子系统按封锁请求的先后次序对这些事务排队,该数据对象上的锁一旦释放,首先批准申请队列中的第一个事务获得锁。
    
    死锁:如果事务T1封锁了数据对象A,T2封锁了数据对象B之后,T1又申请封锁数据对象B,且T2又申请封锁数据对象A。因T2已封锁了B,于是T1等待T2释放加在B上的锁。因T1已封锁了A,T2也只能等待T1释放加在A上的锁。这样就形成了T1在等待T2结束,而T2又在等待T1结束的局面。T1和T2这两个事务永远不能结束,这就是死锁问题。
    死锁的另一情况是数据库系统有若干个长时间运行的事务在执行并行的操作,当查询分析器处理一种非常复杂的连接查询时,由于不能控制处理的顺序,有可能发生死锁现象。 
    目前,解决死锁问题的方法主要有两类:
    预防法:
    此类方法中常用的又有:
    (1)一次封锁法
    (2)顺序封锁法
    诊断解除法
    
    2.时标技术
    (1).时标的定义
    (2).时标的原理
    (3).基于时标技术的并发控制方法所采用的协议
    (4).基于时标技术的并发调度实例
    (5).时标技术与封锁技术的基本区别
    (6).时标技术的优缺点
    
    数据库 并发调度技术 四种问题 三级封锁协议
    
    数据库是一个共享资源,可以提供多个用户使用。这些用户程序可以一个一个地串行执行,每个时刻只有一个用户程序运行,执行对数据库的存取,其他用户程序必须等到这个用户程序结束以后方能对数据库存取。但是如果一个用户程序涉及大量数据的输入/输出交换,则数据库系统的大部分时间处于闲置状态。因此,为了充分利用数据库资源,发挥数据库共享资源的特点,应该允许多个用户并行地存取数据库。但这样就会产生多个用户程序并发存取同一数据的情况,若对并发操作不加控制就可能会存取和存储不正确的数据,破坏数据库的一致性,所以数据库管理系统必须提供并发控制机制。并发控制机制的好坏是衡量一个数据库管理系统性能的重要标志之一。
    
    DM用封锁机制来解决并发问题。它可以保证任何时候都可以有多个正在运行的用户程序,但是所有用户程序都在彼此完全隔离的环境中运行。
    
    一、  并发控制的预备知识
    
    (一)  并发控制概述
    
    并发控制是以事务(transaction)为单位进行的。
    
    1.  并发控制的单位――事务
    
    事务是数据库的逻辑工作单位,它是用户定义的一组操作序列。一个事务可以是一组SQL语句、一条SQL语句或整个程序。
    
    事务的开始和结束都可以由用户显示的控制,如果用户没有显式地定义事务,则由数据库系统按缺省规定自动划分事务。
    
    事务应该具有4种属性:原子性、一致性、隔离性和持久性。
    
    (1)原子性
    
    事务的原子性保证事务包含的一组更新操作是原子不可分的,也就是说这些操作是一个整体,对数据库而言全做或者全不做,不能部分的完成。这一性质即使在系统崩溃之后仍能得到保证,在系统崩溃之后将进行数据库恢复,用来恢复和撤销系统崩溃处于活动状态的事务对数据库的影响,从而保证事务的原子性。系统对磁盘上的任何实际数据的修改之前都会将修改操作信息本身的信息记录到磁盘上。当发生崩溃时,系统能根据这些操作记录当时该事务处于何种状态,以此确定是撤销该事务所做出的所有修改操作,还是将修改的操作重新执行。
    
    (2)一致性
    
    一致性要求事务执行完成后,将数据库从一个一致状态转变到另一个一致状态。它是一种以一致性规则为基础的逻辑属性,例如在转账的操作中,各账户金额必须平衡,这一条规则对于程序员而言是一个强制的规定,由此可见,一致性与原子性是密切相关的。事务的一致性属性要求事务在并发执行的情况下事务的一致性仍然满足。它在逻辑上不是独立的,它由事务的隔离性来表示。
    
    (3) 隔离性
    
    隔离性意味着一个事务的执行不能被其他事务干扰。即一个事务内部的操作及使用的数据对并发的其他事务是隔离的,并发执行的各个事务之间不能互相干扰。它要求即使有多个事务并发执行,看上去每个成功事务按串行调度执行一样。这一性质的另一种称法为可串行性,也就是说系统允许的任何交错操作调度等价于一个串行调度。串行调度的意思是每次调度一个事务,在一个事务的所有操作没有结束之前,另外的事务操作不能开始。由于性能原因,我们需要进行交错操作的调度,但我们也希望这些交错操作的调度的效果和某一个串行调度是一致的。 DM实现该机制是通过对事务的数据访问对象加适当的锁,从而排斥其他的事务对同一数据库对象的并发操作。
    
    (4)持久性
    
    系统提供的持久性保证要求一旦事务提交,那么对数据库所做的修改将是持久的,无论发生何种机器和系统故障都不应该对其有任何影响。例如,自动柜员机( ATM)在向客户支付一笔钱时,就不用担心丢失客户的取款记录。事务的持久性保证事务对数据库的影响是持久的,即使系统崩溃。正如在讲原子性时所提到的那样,系统通过做记录来提供这一保证。
    
    DM没有提供显式定义事务开始的语句,第一个可执行的SQL语句(除CONNECT语句外)隐含事务的开始,但事务的结束可以由用户显式的控制。在DM中以下几种情况都结束 (正常,非正常)某一事务:
    
    (1)当某一连接的属性设置为自动提交,每执行一条语句都会提交;
    
    (2)遇到COMMIT/ROLLBACK语句,便提交/回滚一事务;
    
    (3)当系统的 DDL自动提交开关打开时(缺省为打开),遇到DDL语句则自动提交该DDL语句和以前的DML和DDL操作;
    
    (4)事务所在的程序正常结束和用户退出;
    
    (5)系统非正常终止时;
    
    说明:DM在配置文件中提供了DDL语句的自动提交开关DDL_AUTO_COMMIT。 当此配置项的值为 1(缺省情况)时,所有DDL语句自动提交;当此配置项的值为0时,除CREATEDATABASE、ALTERDATABASE和CREATESCHEMA语句外的所有DDL语句都不自动提交。
    
    DM中的一致性是以事务为基础的。DM通过提交和回滚分别用于将对数据库的修改永久化和废除,但是无论是提交和回滚,DM保证数据库在每个事务开始前、结束后是一致的。为了提高事务管理的灵活性,DM提供了设置保存点(SAVEPOINT)语句和回滚到保存点语句。保存点提供了一种灵活的回滚,事务在执行中可以回滚到某个保存点,在该保存点以前的操作有效,而以后的操作被回滚掉。
    
    DM中的事务同样具有上述4个属性:原子性、一致性、隔离性和持久性。
    
    2. 并发操作与数据的不一致性
    
    如果没有锁定且多个用户同时访问一个数据库,则当他们的事务同时使用相同的数据时可能会发生问题,导致数据库中的数据的不一致性。
    
    一个最常见的并发操作的例子是火车/飞机订票系统中的订票操作。例如,在该系统中的一个活动序列:
    
    ① 甲售票员读出某航班的机票张数余额A,设A=16;
    
    ②  乙售票员读出同一航班的机票张数余额A,也是16;
    
    ③  甲售票员卖出一张机票,修改机票张数余额A=A-1=15,把A写回数据库;
    
    ④  乙售票员也卖出一张机票,修改机票张数余额A=A-1=15,把A写回数据库。
    
    结果明明卖出两张机票,数据库中机票余额只减少1。
    
    这种情况称为数据库的不一致性。这种不一致性是由甲、乙两个售票员并发操作引起的。在并发操作情况下,对甲、乙两个事务操作序列的调度是随机的。若按上面的调度序列行,甲事务的修改就被丢失。这是由于第4步中乙事务修改A并写回覆盖了甲事务的修改。
    
    并发操作带来的数据库不一致性可以分为四类:丢失或覆盖更新、脏读、不可重复读和幻像读,上例只是并发问题的一种。
    
    (1)       丢失或覆盖更新(lost update)
    
    当两个或多个事务选择同一数据,并且基于最初选定的值更新该数据时,会发生丢失更新问题。每个事务都不知道其它事务的存在。最后的更新将重写由其它事务所做的更新,这将导致数据丢失。上面预定飞机票的例子就属于这种并发问题。事务1与事务2先后读入同一数据A=16,事务1执行A-1,并将结果A=15写回,事务2执行A-1,并将结果A=15写回。事务2提交的结果覆盖了事务1对数据库的修改,从而使事务1对数据库的修改丢失了。
    
    
    
    
    (2)    脏读
    一个事务读取了另一个未提交的并行事务写的数据。当第二个事务选择其它事务正在更新的行时,会发生未确认的相关性问题。第二个事务正在读取的数据还没有确认并且可能由更新此行的事务所更改。换句话说,当事务1修改某一数据,并将其写回磁盘,事务2读取同一数据后,事务1由于某种原因被撤销,这时事务1已修改过的数据恢复原值,事务2读到的数据就与数据库中的数据不一致,是不正确的数据,称为脏读。
    例如,在下图中,事务1将C值修改为200,事务2读到C为200,而事务1由于某种原因撤销,其修改作废,C恢复原值100,这时事务2读到的就是不正确的“脏“数据了。
    
    
    
    (3)    不可重复读(nonrepeatable read)
    一个事务重新读取前面读取过的数据,发现该数据已经被另一个已提交的事务修改过。即事务1读取某一数据后,事务2对其做了修改,当事务1再次读数据时,得到的与第一次不同的值。
    例如,在下图中,事务1读取B=100进行运算,事务2读取同一数据B,对其进行修改后将B=200写回数据库。事务1为了对读取值校对重读B,B已为200,与第一次读取值不一致。
    
    
    
    (4)    幻像读
    如果一个事务在提交查询结果之前,另一个事务可以更改该结果,就会发生这种情况。这句话也可以这样解释,事务1按一定条件从数据库中读取某些数据记录后未提交查询结果,事务2删除了其中部分记录,事务1再次按相同条件读取数据时,发现某些记录神秘地消失了;或者事务1按一定条件从数据库中读取某些数据记录后未提交查询结果,事务2插入了一些记录,当事务1再次按相同条件读取数据时,发现多了一些记录。
    产生上述四类数据不一致性的主要原因是并发操作破坏了事务的隔离性。并发控制就是要用正确的方式调度并发操作,使一个用户事务的执行不受其他事务的干扰,从而避免造成数据的不一致性。
    3.    并发场景列举
    结合SQL语句,列举各种并发情况(包括可能导致数据不一致性和对数据一致性不产生影响的情况)。A表示某一条数据,b和c都表示满足某一个标准的两条或多条数据,^表示“非”的意思,∈表示属于或包含于的意思,1表示第一个事务,2表示第二个事务。
    
    
    
    (二)    并发操作的调度
    计算机系统对并行事务中并行操作的调度是随机的,而不同的调度可能会产生不同的结果,那么哪个结果是正确的,哪个是不正确的呢?
    如果一个事务运行过程中没有其他事务在同时运行,也就是说没有受到其他事务的干扰,那么就可能认为该事务的运行结果是正常的或者预想的,因此将所有事务串行起来的调度策略是正确的调度策略。虽然以不同的顺序串行执行事务也可能会产生不同的结果,但由于不会将数据库置于不一致状态,所以都可以认为是正确的。由此可以得到如下结论:几个事务的并行执行是正确的,当且仅当其结果与按某一次序串行地执行它们的结果相同。我们称这种并行调度策略为可串行化(serializable)的调度。可串行性(serializability)是并行事务正确性的唯一准则。
    例如,现在有两个事务,分别包含下列操作:
    事务1:读B;A=B+1;写回A;
    事务2:读A;B=A+1;写回B;
    假设A的初值为10,B的初值为2。下图给出了对这两个事务的三种不同的调度策略,(a)和(b)为两种不同的串行调度策略,虽然执行结果不同,但他们都是正确的调度。(c)中两个事务是交错执行的,由于执行结果与(a)、(b)的结果都不同,所以是错误的调度。(d)中的两个事务也是交错执行的,由于执行结果与串行调度1(图(a))的执行结果相同,所以是正确的调度。
    
    
    
    为了保证并行操作的正确性,DBMS的并行控制机制必须提供一定的手段来保证调度是可串行化的。
    从理论上讲,在某一事务执行时禁止其他事务执行的调度策略一定是可串行化的调度,这也是最简单的调度策略,但这种方法实际上是不可行的,因为它使用户不能充分共享数据库资源。
    目前DBMS普遍采用封锁方法(悲观方法,DM采用的就是这种方法,SQL Server也是采用的这种方法)来保证调度的正确性;即保证并行操作调度的可串行性。除此之外还有其他一些方法,如时标方法、乐观方法等。
    
    ?    悲观并发控制 
    锁定系统阻止用户以影响其它用户的方式修改数据。如果用户执行的操作导致应用了某个锁,则直到这个锁的所有者释放该锁,其它用户才能执行与该锁冲突的操作。该方法主要用在数据争夺激烈的环境中,以及出现并发冲突时用锁保护数据的成本比回滚事务的成本低的环境中,因此称该方法为悲观并发控制。
    
    ?    乐观并发控制
    在乐观并发控制中,用户读数据时不锁定数据。在执行更新时,系统进行检查,查看另一个用户读过数据后是否更改了数据。如果另一个用户更新了数据,将产生一个错误。一般情况下,接收错误信息的用户将回滚事务并重新开始。该方法主要用在数据争夺少的环境内,以及偶尔回滚事务的成本超过读数据时锁定数据的成本的环境内,因此称该方法为乐观并发控制。
    
    ?    时标并发控制
    时标和封锁技术之间的基本区别是封锁是使一组事务的并发执行(即交叉执行)同步,使用它等价于这些事务的某一串行操作;时标法也是使用一组事务的交叉执行同步,但是使它等价于这些事务的一个特定的串行执行,即由时标的时序所确定的一个执行。如果发生冲突,是通过撤销并重新启动一个事务解决的。事务重新启动,则赋予新的时标。
    
    (三)    封锁
    封锁是事项并发控制的一个非常重要的技术。所谓封锁就是事务T在对某个数据对象,例如,在标、记录等操作之前,先向系统发出请求,对其加锁。加锁后事务T就对数据库对象有了一定的控制,在事务T释放它的锁之前,其他事务不能更新此数据对象。
    1.    封锁类型
    DBMS通常提供了多种数据类型的封锁。一个事务对某个数据对象加锁后究竟拥有什么样的控制是由封锁类型决定的。基本的封锁类型有两种:排他锁(exclusive lock,简记为X锁)和共享锁(share lock简记为S锁)
    排他锁又称为写锁。若事务T对数据对象A加上X锁,则只允许T读取和修改A,其他任何事务都不能再对A加任何类型的锁,直到T释放A上的锁。这就保证了其他事务在T释放A上的锁之前不能再读取和修改A。
    共享锁又称为读锁。若事务T对数据对象A加上S锁,则其他事务只能再对A加S锁,而不能加X锁,直到T释放A上的锁。这就保证了其他事务可以读A,但在T释放A上的S锁之前不能对A做任何修改。
    排他锁与共享锁的控制方式可以用下图的相容矩阵来表示。  
    在下图的封锁类型相容矩阵中,最左边一列表示事务T1已经获得的数据对象上的锁的类型,其中横线表示没有加锁。最上面一行表示另一事务T2对同一数据对象发出的封锁请求。T2的封锁请求能否被满足用Y和N表示,其中Y表示事务T2的封锁要求与T1已持有的锁相容,封锁请求可以满足。N表示T2的封锁请求与T1已持有的锁冲突,T2请求被拒绝。
    
    
    
    2.    封锁粒度
    
    X锁和S锁都是加在某一个数据对象上的。封锁的对象可以是逻辑单元,也可以是物理单元。例如,在关系数据库中,封锁对象可以是属性值、属性值集合、元组、关系、索引项、整个索引、整个数据库等逻辑单元;也可以是页(数据页或索引页)、块等物理单元。封锁对象可以很大,比如对整个数据库加锁,也可以很小,比如只对某个属性值加锁。封锁对象的大小称为封锁的粒度(granularity)。
    
    封锁粒度与系统的并发度和并发控制的开销密切相关。封锁的粒度越大,系统中能够被封锁的对象就越小,并发度也就越小,但同时系统开销也越小;相反,封锁的粒度越小,并发度越高,但系统开销也就越大。
    因此,如果在一个系统中同时存在不同大小的封锁单元供不同的事务选择使用是比较理想的。而选择封锁粒度时必须同时考虑封锁机构和并发度两个因素,对系统开销与并发度进行权衡,以求得最优的效果。一般说来,需要处理大量元组的用户事务可以以关系为封锁单元;需要处理多个关系的大量元组的用户事务可以以数据库为封锁单位;而对于一个处理少量元组的用户事务,可以以元组为封锁单位以提高并发度。
    
    3.    封锁协议
    
    封锁的目的是为了保证能够正确地调度并发操作。为此,在运用X锁和S锁这两种基本封锁,对一定粒度的数据对象加锁时,还需要约定一些规则,例如,应何时申请X锁或S锁、持锁时间、何时释放等。我们称这些规则为封锁协议(locking protocol)。对封锁方式规定不同的规则,就形成了各种不同的封锁协议,它们分别在不同的程度上为并发操作的正确调度提供一定的保证。本节介绍保证数据一致性的三级封锁协议和保证并行调度可串行性的两段锁协议,下一节将介绍避免死锁的封锁协议。
    
    (5)    保证数据一致性的封锁协议――三级封锁协议
    
    对并发操作的不正确调度可能会带来四种数据不一致性:丢失或覆盖更新、脏读、不可重复读和幻想读。三级封锁协议分别在不同程度上解决了这一问题。
    
    ①    1级封锁协议
    
    1级封锁协议的内容是:事务T在修改数据R之前必须先对其加X锁,直到事务结束才释放。事务结束包括正常结束(commit)和非正常结束(rollback)。
    
    1级封锁协议可以防止丢失或覆盖更新,并保证事务T是可以恢复的。例如,下图使用1级封锁协议解决了定飞机票例子的丢失更新问题。
    
    
    
    上图中,事务1在读A进行修改之前先对A加X锁,当事务2再请求对A加X锁时被拒绝,只能等事务1释放A上的锁。事务1修改值A=15写回磁盘,释放A上的X锁后,事务2获得对A的X锁,这时他读到的A已经是事务1更新过的值15,再按此新的A值进行运算,并将结果值A=14回到磁盘。这样就避免了丢失事务1的更新。
    
    在1级封锁协议中,如果仅仅是读数据不对其进行修改,是不需要加锁的,所以它不能保证可重复读和脏读。
    
    ②  2级封锁协议
    
    2级封锁协议的内容是:1级封锁协议加上事务T在读取数据R之前必须先对其加S锁,读完后即可释放S锁。
    2级封锁协议除防止了丢失或覆盖更新,还可进一步防止脏读。例如,下图使用2级封锁协议解决了脏读的问题。
    |
    下图中,事务1在对C进行修改之前,先对C加X锁,修改其值后写回磁盘。这时事务2请求C加上S锁,因T1已在C上加了X锁,事务2只能等待事务1释放它。之后事务1因某种原因被撤销,C恢复为原值100,并释放C上的X锁。事务2获得C上的S锁,读C=100。这就避免了事务2脏读数据。
    
    在2级封锁协议中,由于读完数据后即可释放S锁,所以它不能保证可重复读。
    
    
    
    ③    3级封锁协议
    
    3级封锁协议的内容是:1级封锁协议加上事务T在读取数据之前必须先对其加S锁,直到事务结束才释放。
    3级封锁协议除防止丢失或覆盖更新和不脏读数据外,还进一步防止了不可重复读和幻想读。例如下图,使用3级封锁协议解决了不可重复读和幻像读问题。
    
    
    
    上图中,事务1在读A,B之前,先对A,B加S锁,这样其他事务只能再对A,B加S锁,而不能加X锁,即其他事务只能读A,B,而不能修改它们。所以当事务2为修改B而申请对B的X锁时被拒绝,使其他无法执行修改操作,只能等待事务1释放B上的锁。接着事务1为验算再读A,B,这时读出的B仍是100,求和结果仍为150,即可重复读。
    
    上述三级协议的主要区别在于什么操作需要申请封锁以及何时释放锁(即持锁时间)。三级封锁协议可以总结为下表。
    
    
    
    (6)    保证并行调度可串行性的封锁协议――两段封锁协议
    
    可串行性是并行调度正确性的唯一准则,两段锁(two-phase locking,简称2PL)协议是为保证并行调度可串行性而提供的封锁协议。
    
    两段封锁协议规定:
    
    ①在对任何数据进行读、写操作之前,事务首先要获得对该数据的封锁,而且②在释放一个封锁之后,事务不再获得任何其他封锁。
    
    所谓“两段”锁的含义是,事务分为两个阶段,第一阶段是获得封锁,也称为扩展阶段,第二阶段是释放封锁,也称为收缩阶段。
    
    例如,事务1的封锁序列是:
    
    Slock A... Slock B… Xlock C… Unlock B… Unlock A… Unlock C;
    
    事务2的封锁序列是:
    
    Slock A... Unlock A… Slock B… Xlock C… Unlock C… Unlock B;
    
    则事务1遵守两段封锁协议,而事务2不遵守两段封锁协议。
    
    可以证明,若并行执行的所有事务均遵守两段锁协议,则对这些事务的所有并行调度策略都是可串行化的。因此我们得出如下结论:所有遵守两段锁协议的事务,其并行的结果一定是正确的。
    
    需要说明的是,事务遵守两段锁协议是可串行化调度的充分条件,而不是必要条件。即可串行化的调度中,不一定所有事务都必须符合两段封锁协议。例如,在下图中,(a)和(b)都是可串行化的调度,但(a)遵守两段锁协议,(b)不遵守两段锁协议。
    
    
    
    4.    死锁和活锁
    
    封锁技术可以有效地解决并行操作的一致性问题,但也带来一些新的问题,即死锁和活锁的问题。
    
    (1)    活锁
    
    如果事务T1封锁了数据对象R后,事务T2也请求封锁R,于是T2等待。接着T3也请求封锁R。T1释放R上的锁后,系统首先批准了T3的请求,T2只得继续等待。接着T4也请求封锁R,T3释放R上的锁后,系统又批准了T4的请求……,T2有可能就这样永远等待下去。这就是活锁的情形,如下图所示。
    
    
    
    避免活锁的简单方法是采用先来先服务的策略。当多个事务请求封锁同一数据对象时,封锁子系统按请求封锁的先后次序对这些事务排队,该数据对象上的锁一旦释放,首先批准申请队列中第一个事务获得锁。
    
    (2)    死锁
    
    如果事务T1封锁了数据A,事务T2封锁了数据B。之后T1又申请封锁数据B,因T2已封锁了B,于是T1等待T2释放B上的锁。接着T2又申请封锁A,因T1已封锁了A,T2也只能等待T1释放A上的锁。这样就出现了T1在等待T2,而T2又在等待T1的局面,T1和T2两个事务永远不能结束,形成死锁。如下图所示。
    
    
    
    死锁问题在操作系统和一般并行处理中已做了深入研究,但数据库系统有其自己的特点,操作系统中解决死锁的方法并不一定合适数据库系统。
    
    目前在数据库中解决死锁问题主要有两类方法,一类方法是采取一定措施来预防死锁的发生,另一类方法是允许发生死锁,采用一定手段定期诊断系统中有无死锁,若有则解除之。
    
    ①    死锁的预防
    在数据库系统中,产生死锁的原因是两个或多个事务都已封锁了一些数据对象,然后又都请求对已为其他事务封锁的数据对象加锁,从而出现死锁等待。防止死锁的发生其实就是要破坏产生死锁的条件。预防死锁通常有两种方法。
    
    ◆ 一次封锁法
    一次封锁法要求每个事务必须一次将所有要使用的数据全部加锁,否则就不能继续执行。例如,在上图的例子中,如果事务T1将数据对象A和B一次加锁,T1就可以执行下去,而T2等待。T1执行完后释放A,B上的锁,T2继续执行。这样就不会发生死锁。
    
    一次封锁法虽然可以有效地防止死锁的发生,但也存在问题。第一,一次就将以后要用到的全部数据加锁,势必扩大了封锁的范围,从而降低了系统的并发度。第二,数据库中数据是不断变化的,原来不要求封锁的数据,在执行过程中可能会变成封锁对象,所以很难实现精确地确定每个事务所要封锁的数据对象,只能采取扩大封锁范围,将事务在执行过程中可能要封锁的数据对象全部加锁,这就进一步降低了并发度。
    
    ◆    顺序封锁法
    顺序封锁法是预先对数据对象规定一个封锁顺序,所有事务都按这个顺序执行封锁。在上例中,我们规定封锁顺是A,B,T1和T2都按此顺序封锁,即T2也必须先封锁A。当T2请求A的封锁时,由于T1已经封锁住A,T2就只能等待。T1释放A,B上的锁后,T2继续运行。这样就不会发生死锁。
    
    顺序封锁法同样可以有效地防止死锁,但也同样存在问题。第一,数据库系统中可封锁的数据对象及其众多,并且随数据的插入、删除等操作而不断地变化,要维护这样极多而且变化的资源的封锁顺序非常困难,成本很高。
    
    第二,事务的封锁请求可以随着事务的执行而动态地决定,很难事先确定每一个事务要封锁哪些对象,因此也就很难按规定的顺序取施加封锁。例如,规定数据对象的封锁顺序为A,B,C,D,E。事务T3起初要求封锁数据对象B,C,E,但当它封锁B,C后,才发现还需要封锁A,这样就破坏了封锁顺序。
    
    可见,在操作系统中广为采用的预防死锁的策略并不很适合数据库的特点,因此DBMS在解决死锁的问题上更普遍采用的是诊断并解除死锁的方法。
    ②    死锁的诊断与解除
    数据库系统中诊断死锁的方法与操作系统类似,即使用一个事务等待图,它动态地反映所有事务的等待状况。并发控制子系统周期性地(比如每隔1分钟)检测事务等待图,如果发现图中存在回路,则表示系统中出现了死锁。关于诊断死锁的详细讨论请参阅操作系统的有关书籍。
    DBMS的并发控制子系统一旦检测到系统中存在死锁,就要设法解除。通常采用的方法是选择一个处理死锁代价最小的事务,将其撤销,释放此事务持有的所有的锁,使其他事务能继续运行下去。
    二、    DM的并发控制
    (一)    事务隔离级
    事务的隔离级描述了给定事务的行为对其它并发执行事务的暴露程度。 SQL-92共规定了四种隔离级别,通过选择四个隔离级中的一个,用户能增加对其它未提交事务的暴露程度,获得更高的并发度。隔离级别是一个事务必须与其它事务进行隔离的程度。
    SQL-92的四种隔离级别如下所示,DM支持所有这些隔离级别: 
    (1)脏读(READ UNCOMMITTED):事务隔离的最低级别,事务可能查询到其它事务未提交的数据, 仅可保证不读取物理损坏的数据)。
    (2)读提交(READ COMMITTED):DM默认级别,保证不读脏数据。  
    (3)可重复读(REPEATABLE READ):保证不可重复读,但有可能读入幻像数据。 
    (4)可串行化(SERIALIZABLE):事务隔离的最高级别,事务之间完全隔离。
    DM允许用户改变未启动的事务的隔离级和读写特性 ,而且设置的选项将一直对那个连接保持有效,直到显式更改该选项为止。设置事务隔离级别虽然使程序员承担了某些完整性问题所带来的风险,但可以换取对数据更大的并发访问权。与以前的隔离级别相比,每个隔离级别都提供了更大的隔离性,但这是通过在更长的时间内占用更多限制锁换来的。DM还提供设置事务只读属性的语句,使用该语句后该事务只能做查询操作,不能更新数据库。
    需要注意的是,事务的隔离级别并不影响事务查看本身对数据的修改,也就是说,事务总可以查看自己对数据的修改。事务的隔离级别需要根据实际需要设定,较低的隔离级别可以增加并发,但代价是降低数据的正确性。相反,较高的隔离级别可以确保数据的正确性,但可能对并发产生负面影响。应用程序要求的隔离级别确定了DM使用的锁定行为。 
    下表中列出四种隔离级别允许不同类型的现象 
    
    
    
    注意:丢失或覆盖更新在所有的标准SQL隔离级中都是禁止的。
    
    (二)    并发处理
    
    1.    数据锁定机制
    
    DM用数据锁定机制来解决并发问题。它可以保证任何时候都可以有多个正在运行的事务,但是所有事务都在彼此完全隔离的环境中运行。 
    
    DM的封锁对象为表和元组。封锁的实施有自动和手动两种,即隐式上锁和显式上锁。隐式封锁动作的封锁根据事务的隔离级有所不同。同时, DM提供给用户4种手动上锁语句,用以适应用户定义的应用系统。 
    
    一般而言, DM的隐式封锁足以保证数据的一致性,但用户可以根据自己的需要改变对表的封锁。 DM提供给用户四种表锁:意向共享锁(IS:INTENSIVE SHARE)、共享锁(S:SHARE)、意向排它锁(IX:INTENSIVE EXCLUSIVE)和排它锁(X:EXCLUSIVE)。例如,在读提交隔离级下,系统缺省的表锁是 IS或IX ,在这两种表锁下,在访问元组前还需对元组进行封锁,为了提高系统的效率,用户可以手动对表进行 X封锁,这样,就不需对访问元组封锁。 
    
    封锁机制要达到以下目的:
    
    (1)一致性:保证用户正在查看时,改变的数据并未从根本上发生变化。 
    
    (2)完整性:保证数据库的基本结构以正确的顺序,准确地反映对它们的所有改变。 
    
    一个“ 锁定” 可以认为是当某一进程需要防止其它进程做某事时获得的某种东西,当该进程不再关心此事时就 “释放 ”此锁定,通常一个锁定是加在某个 “资源 ”(某些客体,如表 )上的。 
    
    DM的内部锁定是自动完成的。当某一进程要查看一个客体但不允许其他人修改它时,就获得一个共享方式的锁定。当某一进程要修改一客体,并且防止任何其它进程修改它时,就获得更新方式的锁定。当某一进程要修改一客体,并且防止任何其它进程修改它或以共享方式封锁它时,就获得独占方式的锁定。
    
    2.    锁定类型
    
    DM中的锁有三种,表锁、行锁和键范围锁。
    
    ◆   表锁
    表锁用来封锁表对象,在对表进行检索和更新时,DM会对表对象进行封锁,但是DM为用户提供手动的表锁语句,用户可以根据自己的需要改变对表的封锁类型。表锁的模式:意向共享锁 IS,意向排它锁 IX,共享锁 S,排它锁 X,共四种,其相容矩阵可定义如下表。
    
    
    
    ◆   行锁
    行锁封锁元组,在存取元组和更新元组前, DM会对元组上行锁,系统不提供手动的行封锁语句。行锁有两种模式:共享锁(S)、排它锁(X),其相容矩阵定义如下表。
    
    
    
    ◆     键范围锁
    键范围锁用在可串行事务上,主要解决了幻像读并发问题。键范围锁覆盖单个记录以及记录之间的范围,可以防止对事务访问的记录集进行幻像插入或删除。键范围锁仅用于在可串行隔离级别上操作的事务。 
    可串行性要求,如果任意一个查询在一个事务中后面的某一时刻再次执行,其所获取的行集应与该查询在同一事务中以前执行时所获得的行集相同。如果本查询试图提取的行不存在,则在试图访问该行的事务完成之前,其它事务不能插入该行。如果允许另一个事务插入该行,则它将以幻像出现。 
    如果另一个事务试图插入驻留在锁定数据页上的行,页级锁定可以防止添加幻像行,并维护可串行性。但是,如果该行要添加到未被第一个事务锁定的数据页,应设定锁定机制防止添加该行。 
    键范围锁通过覆盖索引行和索引行之间的范围来工作(而不是锁定整个基础表的行)。因为第二个事务在该范围内进行任何行插入、更新或删除操作时均需要修改索引,而键范围锁覆盖了索引项,所以在第一个事务完成之前会阻塞第二个事务的进行。 
    键范围锁由系统自行执行,执行的条件是: (1) 事务隔离级为可串行化级; (2) 查询结果通过某个索引得出。 
    用户上锁成功后锁将一直有效,直到当前事务结束时,该锁被系统自动解除。 
    
    3.    锁定类型比较
    
    
    
    4.    SQL语句锁定分析
    DM对各种 DDL和GRANT 等非DML 语句都分解为增、删、改。下表为DM对各种DML语句和查询语句的封锁策略。 
    
    表:SQL语句封锁策略
    
    
    注:S* 表示瞬时锁,在语句结束后释放;Range表示键范围锁。 
    
    上表只是系统在一般情况下的处理,当系统检测到有锁升级的可能,则会升级锁。一般而言,IS锁升级为 S锁,IX锁升级为 X锁,同时,不再进行行封锁。 
    
    5.    自定义锁定提高系统效率
    
    DM也提供了两个函数 SET_TABLE_OPTION([db.][sch.]tablename, option, value) 、SET_INDEX_OPTION([db.]indexname, option, value)(具体语法参见《 DM_SQL语言使用手册》第 8 章)供用户自行定义锁定类型,以增强系统并发度,提高系统效率。这两个函数是为那些清楚地知道特定类型的锁适用于何种情况的专家级用户提供的。 
    
    函数SET_TABLE_OPTION() 用于禁用指定表上的页级锁、行级锁或同时禁用二者,这一设置对该表上的所有索引都生效。函数 SET_INDEX_OPTION() 则用于禁用某一索引上的页级锁、行级锁或同时禁用二者。
    
    例如,当用户只需要修改索引中某定长字段时,修改操作不会造成 B 树的分裂与合并,此时就可以禁用该索引的页级锁。又如,当所有的用户都只做插入操作时,用户之间并不会对同一元组进行操作,此时就可以禁用行级锁。当用户能保证不对表进行增、删、改,而只是进行查询时,则可以同时禁用该表上的页级锁和行级锁,此时并发度最高。 
    
    6.    死锁处理
    
    解决死锁问题的三种方法:预防死锁、检测死锁及避免死锁。死锁预防要求用户进程事先申报所需的资源或按严格的规程申请资源,而死锁检测原则上应允许死锁发生,在适当的时机检查,若发生死锁,则设法排除之。与预防死锁相比,后者过于放手,致使死锁频繁。而避免死锁则以事务撤消为前提,当不能获得资源批准时,立刻进行死锁检测。它既不象预防死锁那样过于保守,也不象死锁检测那样过于放开,由于检测及时,由归纳法可知,在已获准等待的事务中,不可能存在死锁,所以检测算法比较简单。 
    
    DM4系统采用的是避免死锁方法。每当一个事务所申请占有的资源不能被立即获得时,便进行死锁检测,不存在死锁,则该事务入等待队列。否则,DM4视为产生运行时错误,将当前语句回滚。采用这种机制,从用户的角度看,DM4不存在解锁问题。 
    
    7.    加索引和不加索引的封锁区别
    
    加索引和不加索引的情况下,DM的封锁机制会影响到实际的封锁范围。索引的作用就在于,可以在查询中减少对无关数据的扫描。而在一般的隔离级中,总是要对扫描到的数据进行封锁。所以,利用索引可以减少封锁的数量,冲突的可能性也会大大减少。
    展开全文
  • 目录 12.1 什么是事务处理......12.2 事务处理控制语句... 1 12.2.1 COMMIT处理... 2 12.2.2 ROLL BACK处理... 2 12.2.3 SAVEPOINT和ROLL BACK TO SAVEPOINT. 3 12.2.4 SET TRANSACTION..

    目录

    12.1          什么是事务处理... 1

    12.2          事务处理控制语句... 1

    12.2.1       COMMIT处理... 2

    12.2.2       ROLL BACK处理... 2

    12.2.3       SAVEPOINT和ROLL BACK TO SAVEPOINT. 3

    12.2.4       SET TRANSACTION.. 3

    试验:冻结视图... 4

    12.2.5       SET CONSTRAINTS. 5

    12.3          事务处理的ACID属性... 7

    12.3.1       原子性... 7

    12.3.2       一致性... 7

    试验:事务处理级别的一致性... 8

    12.3.3       隔离性... 11

    12.3.4       持久性... 11

    12.4          并发控制... 11

    12.4.1       锁定... 12

    试验:引发死锁... 12

    12.4.2       多版本和读取一致性... 15

    考虑一个进一步的示例。... 16

    12.5          小结... 17

     

     

     

     

    开发者能够命名他们的PL/SQL程序块,为它们确定参数,将它们存储在数据库中,并且从任何数据库客户或者实用工具中引用或者运行它们,例如 SQL*Plus、Pro*C,甚至是JDBC。

    这此听PL/SQL程序称为存储过程和函数。它们的集合称为程序包。在本章中,我们将要解释使用过程、函数和程序包的三大优势、这三种相似结构之间的区别。

     

    Oracle 9i产品帮助文档:

    http://docs.oracle.com/cd/B10501_01/index.htm

    可根据自己需要进行查询,包含了众多的文档。

     

    Sample Schemas的目录:

    http://docs.oracle.com/cd/B10501_01/server.920/a96539/toc.htm

     

    Sample Schemas的文档(示例模式的表及介绍):

    http://docs.oracle.com/cd/B10501_01/server.920/a96539.pdf

     

    在讨论这2个特性的时候,我们将要在本章中学习如下内容:

    ●Oracle中的事务处理是什么

    ●怎样控制Oracle中的事务控制

    ●Oracle怎样在数据库中实现并发控制,让多个用户同时访问和修改相同的数据表

     

    12.1  什么是事务处理

    用于有效记录某机构感兴趣的业务活动(称为事务)的数据处理(例如销售、供货的定购或货币传输)。通常,联机事务处理 (OLTP) 系统执行大量的相对较小的事务。

    12.2  事务处理控制语句

    Oracle中的一个重要概念就是没有“开始事务处理”的语句。用户不能显式开始一个事务处理。事务处理会隐式地开始于第一条修改数据的语句,或者一些要求事务处理的场合。使用COMMIT或者ROLL BACK语句将会显式终止事务处理。

    如上所述,事务处理具有原子性,也就是说,或者所有语句都成功执行,或者所有语句都不能成功执行。我们会在这里介绍其中一些成员:

    ●COMMIT

    ●ROLL BACK

    ●SAVEPOINT

    ●ROLL BACK TO<SAVEPOINT>

    ●SET TRANSACTION

    ●SET CONSTRAINT(S)

     

    12.2.1             COMMIT处理

    作为开发者,用户应该使用COMMIT或者ROLL BACK显式终止用户的事务处理,否则用户正在使用的工具/环境就将为用户选取其中一种方式。

    无论事务处理的规模如何,提交都是非常快速的操作。用户可能会认为事务处理越大(换句话说,影响的数据越多),提交所耗费时间越长。事实并非如此,进化论事务处理规模如何,提交的响应时间通常都很“平缓”。这是因为提交实际上没有太多的工作去做,但是它所做的工作却至关重要。

    如果能够理解这点,那么就可以避免许多开发者所采用的工作方式,去限制他们的事务处理规模,每隔若干行就进行提交,而不是当逻辑单元的工作已经执行完毕之后才进行提交。他们这样做是因为他们错误地认为他们正在降低系统资源的负载;而事实上,他们正在增加负载。如果提交一行需要耗费X个时间单元,那么提交1000行也会消耗相同的X个时间单元,而采用1000次提交1行的方式完成这项工作就需要额外运行1000*X个时间单元。如果只在必要的时候进行一次提交(当事务处理完成的时候),那么用户就不仅可以提高性能,而且可以减少对共享资源(日志文件,保护SGA内共享数据结构的锁定)的争用。

    那么,为什么无论事务处理的规模如何,提交的响应时间都相当平缓呢?这是因为在数据库中进行提交之前,我们已经完成了所有实际工作,我们已经修改了数据库中的数据,完成了99.9%的工作。

    当提交的时候,我们还要执行三个任务:

    ●为我们的事务处理生成 SCN(系统改变编号)。这是Oracle的内部时钟,可以称为数据库时间。SCN不是传统意义上的时钟,因为它不是随着时间失衡而递进。相反,它是在事务处理提交的时候递进,由Oracle在内部使用,以对事务处理排序。

    ●将所有剩余的已经缓冲的重做日志表项写入磁盘,并且将SCN记录到在重做日志文件中。这要由LGWR执行。

    ●释放我们的会话(以及所有正在等等我们所占有锁定的用户)所占有的所有锁定。

    LGWR不会一直缓冲完成的所有工作,而是会随着操作的进行,在后台不断清理重做日志缓冲的内容,这非常类似于在PC机上运行的磁盘缓冲软件。它可以避免在清理所有的用户重做日志时,提交操作出现长时间等待现象。LGWR会在以下情况执行清理工作:

    ●每隔3秒

    ●当SGA中的日志缓冲超过了1/3的空间,或者包含了1MB或者更多的已缓冲数据

    ●进行任何事务处理提交

    所以,即使我们长时间运行事务处理,也会有部分它所产生的已缓冲重做日志在提交之前写入了磁盘。

     

    12.2.2             ROLL BACK处理

    当我们进行回滚的时候,我们还有一些任务需要执行:

    ●撤销所有已经执行的改变。这要通过读取我们生成的UNDO数据,有效地反转我们的操作来完成。如果我们插入了一行,那么回滚就要删除它。如果我们更新了一行,回滚就要将其更新到原来的样子。如果我们删除了一行,就要重新插入它。

    ●释放我们的会话(以及正在等等我们已经锁定的行的用户)占用的所有锁定。

     

    12.2.3             SAVEPOINT和ROLL BACK TO SAVEPOINT

    SAVEPOINT可以让用户在事务处理中建立标记点。用户可以在单独的事务处理中拥有多个保存点。当使用ROLL BACK TO <SAVEPOINT NAME>的时候,它就可以让用户有选择地回滚更大的事务处理中的一组语句。

    保存点是委有用的事务处理特性,它们可以让用户将单独的大规模事务处理分割成一系列较小的部分。拟执行的所有语句仍然是较大的事务处理的组成部分,用户可以将特定的语句组织到一起,将它们作为单独的语句进行回滚。

    为了做到这一点,用户需要在开始函数的时候使用如下的SQL语句:

    Savepoint function_name;

     

    这里的function_name是用户的函数名称。如果用户在处理期间遇到错误,用户就只需使用:

    Roll back to function_name;

     

     

    12.2.4             SET TRANSACTION

    这个语句可以使您设置事务处理的各种属性,例如它的隔离层(参考以下的隔离层次列表),它是只读还是可以进行读写,以及是否要使用特定的回滚段。

    SET TRANSACTION语句必须是事务处理中使用的第一个语句。这就是说,必须在任何INSERT、UPDATE或者DELETE语句,以及任何其它可以开始事务处理的语句之前使用它。SET TRANSACTION的作用域只是当前的事务处理。

    SET TRANSACTION语句可以让用户:

    ●规定事务处理隔离层次。

    ●规定为用户事务处理所使用的特定回滚段。

    ●命名用户事务处理。

     

    我们将要在这里使用的重要SET TRANSACTION语句包括:

    ●SET TRANSACTION READ ONLY

    ●SET TRANSACTION READ WRITE

    ●SET TRANSACTION ISOLACTION LEVEL SERIALIZABLE

    ●SET TRANSACTION ISOLACTION LEVEL READ COMMITTED

    注意:

    SET TRANSACTION语句事实上都是互斥的。例如,如果您选择了READ ONLY,那么就不能为它选择READ WRITE、SERIALIZABLE或者READ COMMITTED。

     

    1. READ ONLY

    命令SET TRANSACTION READ ONLY将会做2件事,它会确保您无法执行修改数据的DML操作,例如INSERT、UPDATE或者DELETE。如下所示:

    复制代码
    SQL> set transaction read only;
    
    事务处理集。
    
    SQL> update emp set ename=lower(ename);
    update emp set ename=lower(ename)
           *
    ERROR 位于第 1 行:
    ORA-01456: 不可以在 READ ONLY 事务处理中执行插入/删除/更新操作
    复制代码

     

     

    其效果很明显。而READ ONLY事务处理的另一个副作用则更为微妙。通过将事务处理设置为READ ONLY,我们就可以有效地将我们的数据库视图冻结到某个时间点。我的意思是,无论数据库中的其它会话如何工作,数据库在我们的面前都会是使用SET TRANSACTION语句时候样子。这有什么用处呢?我们假定现在是一个繁忙的工作日的下午1点。用户需要运行一个报表。这个报表将要执行几十条查询,从许多数据源中获取数据。所有这些数据都会相互关联,为了使其有意义,它们必须保持一致。换句话说,用户需要每个查询都看到“1点钟”的数据库。如果每个查询看不同时间点的数据库,那么我们报告中的结果就没有意义。

    为了确保用户数据一致,用户应该锁定报表查询所需的所有表,防止其它用户更新它们。作为一种替代的方法,用户可以使用SET TRANSACTION READ ONLY语句,冻结“1点钟”的用户数据库视图。这可以得到两全其美的结果。

    试验:冻结视图

    (1)       为了运行这个示例,用户需要在SQL*Plus中打开3个会话。建立一个表。

    SQL> create table t as select object_id from all_objects where rownum<=2000;
    表已创建。

     

    (2)       用以观察READ ONLY和READ WRITE事务处理区别。

    时间

    会话1

    会话2

    会话3

    注释

    T1

    set transaction read only;

     

     

     

    T2

    select count(*) from t;

    select count(*) from t;

     

    2个事务处理都可以看到2000行

    T3

     

     

    delete from t where rownum<=500;

    从T中删除500行,但是没有提交

    T4

    select count(*) from t;

    select count(*) from t;

     

    由于多版本的作用,所以这2个会话都会看到2000行

    T5

     

     

    commit;

    让500个已删除的行永久删除

    T6

    select count(*) from t;

    select count(*) from t;

     

    会话1仍然会看到2000行,会话2现在将要看到1500行!到提交或者回滚为止,会话1将会一直看到200行

    T7

     

     

    insert into t select * from t;

    对T的规模进行加倍,使其达到3000个记录

    T8

     

     

    commit;

    使得改变可见

    T9

    select count(*) from t;

    select count(*) from t;

     

    会话1会继续看到2000行。会话2现在可以看到所有的3000行

    T10

    commit;

     

     

     

    T11

    select count(*) from t;

     

     

    会话1现在也可以看到3000行。

     

    1. READ WRITE

    第二个命令SET TRANSACTION READ WRITE不会经常使用,因为它是默认设置。很少有必要使用这个命令,在这里包含它只是出于完整性的考虑。

     

    1. ISOLATION LEVEL SERIALIZABLE

    SET TRANSACTION ISOLATION LEVEL SERIALIZABLE与READ ONLY有一些类似。当使用了这个命令之后,无论是否出现改变,数据库都会为您进行“冻结”。就如同我们在READ ONLY事务处理中看到的那样,您可以完全隔离其它事务处理的影响。

    1. ISOLATION LEVEL READ COMITTED

    这个命令大体相当于将事务处理设置为READ WRITE,由于它是设置隔离层次时Oracle的默认操作模式,所以很少使用。如果您在会话的前面使用ALTER SESSION命令,将用户会话的事务处理的默认隔离层次从READ COMMITTED改变为SERIABLIZABLE,那么就可以会用到这个命令。使用ISOLATION LEVEL READ COMMITTED命令可以重置默认值。

    12.2.5             SET CONSTRAINTS

    在Oracle中,约束可以在DML语句执行后立即生效,也可以延迟到事务处理提交的时候才生效。SET CONSTRAINT语句可以让您在事务处理中设置延迟约束的强制模式。可以使用如下语法延迟单独的约束:

    set constraint constraint_name defferred

     

    或者,您也可以使用如下语法延迟所有约束:

    set constraints all defferred

     

    在这些命令中,您可以使用关键字IMMEDIATE代替DEFERRED,将他们的强制模式改回立即模式。为了看到实际的命令,我们可以使用一个简单的示例:

    复制代码
    SQL> drop table t;
    
    表已丢弃。
    
    SQL> create table t
      2  (x int,
      3  constraint x_greater_than_zero check(x>0)
      4   deferrable initially immediate
      5  )
      6  /
    
    表已创建。
    
    SQL> insert into t values(-1);
    insert into t values(-1)
    *
    ERROR 位于第 1 行:
    ORA-02290: 违反检查约束条件 (SCOTT.X_GREATER_THAN_ZERO)
    复制代码

     

    不错,我们无法向X中插入值-1.现在,我们使用SET CONSTRAINT命令延迟约束的脸证,如下所示:

    SQL> set constraint x_greater_than_zero deferred;
    约束条件已设置。
    
    SQL> insert into t values(-1);
    已创建 1 行。

     

    然而,我们无法在数据库中提交这条语句:

    复制代码
    SQL> commit;
    commit
    *
    ERROR 位于第 1 行:
    ORA-02091: 事务处理已重算
    ORA-02290: 违反检查约束条件 (SCOTT.X_GREATER_THAN_ZERO)
    复制代码

     

    有2种方法使用这个命令,或者规定一组约束名称,或者使用关键词ALL。例如,为了延迟约束X_GREATER_THAN_ZERO,我们可以使用:

    set constraint x_greater_than_zero deferred;

     

    我们也可以很容易地使用如下命令:

    set constraints all deferred;

     

    这里的区别是第2个版本使用了ALL,它会影响我们会话中的所有延迟约束,而不只是我们感兴趣的约束。而且,为了明确使用用户约束,也可以使用如下语句:

    set constraint <constraint_name> immediate;

     

    例如,我们可以在以上的救命中使用如下语句:

    复制代码
    SQL> set constraint x_greater_than_zero deferred;
    
    约束条件已设置。
    
    SQL> insert into t values(-1);
    
    已创建 1 行。
    
    SQL> set constraint x_greater_than_zero immediate;
    SET constraint x_greater_than_zero immediate
    *
    ERROR 位于第 1 行:
    ORA-02290: 违反检查约束条件 (SCOTT.X_GREATER_THAN_ZERO)
    
    
    SQL> commit;
    commit
    *
    ERROR 位于第 1 行:
    ORA-02091: 事务处理已重算
    ORA-02290: 违反检查约束条件 (SCOTT.X_GREATER_THAN_ZERO)
    复制代码

     

    Oracle会回滚我们的事务处理。通过SET CONSTRAINT <constraint_name> IMMEDIATE命令,我们就能够发现我们已经违反了一些约束,而我们的事务处理仍然有效。这个约束将会处理延迟模式,它不会立即检查。

    12.3  事务处理的ACID属性

    ACID是替代如下内容的首字母缩写:

    ●原子性(Atomicity)——事务处理要么全部进行,要么不进行。

    ●一致性(Consistency)——事务处理要将数据库从一种状态变成另一种状态。

    ●隔离性(Isolation)——在事务处理提交之前,事务处理的效果不能由系统中其它事务处理看到。

    ●持久性(Durability)——一旦提交了事务处理,它就永久生效。

     

    12.3.1             原子性

    在Oracle中,事务处理具有原子性。换句话说,或者提交所有的工作,或者什么工作都不提交。

    12.3.2             一致性

    这是非常重要的事务处理特性,任何事务处理都会将数据库从一种逻辑上的一致状态转变为另一种逻辑上的一致状态。这就是说,在事务处理开始之前,数据库的所有数据都会满足您已经施加给数据库的业务规则(约束)。

    我们所使用的表和触发器如下所示:

    复制代码
    SQL> drop table t;
    
    表已丢弃。
    
    SQL> create table t
      2  ( x int,
      3   constraint t_pk primary key(x)
      4  )
      5  /
    
    表已创建。
    
    SQL> create trigger t_trigger
      2  after update on T for each row
      3  begin
      4   dbms_output.put_line('Updated x='||:old.x||' to x='||:new.x);
      5  end;
      6  /
    
    触发器已创建
    复制代码

     

    现在,我们要向T中插入一些行:

    复制代码
    SQL> insert into t values(1);
    
    已创建 1 行。
    
    SQL> insert into t values(2);
    
    已创建 1 行。
    复制代码

     

    这就结束了这个示例的设置。现在,物体 尝试对表T进行更新,将所有行都设置为数值2。

    复制代码
    SQL> set serverout on
    SQL> begin
      2   update t set x=2;
      3  end;
      4  /
    Updated x=1 to x=2
    Updated x=2 to x=2
    begin
    *
    ERROR 位于第 1 行:
    ORA-00001: 违反唯一约束条件 (SCOTT.T_PK)
    ORA-06512: 在line 2
    复制代码

     

    现在,如果我们使用如下所示的成功语句:

    复制代码
    SQL> begin
      2   update t set x=x+1;
      3  end;
      4  /
    Updated x=1 to x=2
    Updated x=2 to x=3
    
    PL/SQL 过程已成功完成。
    复制代码

     

    数据库在逻辑上保持了一致(它满足了所有的业务规则),所以语句将会成功。

    试验:事务处理级别的一致性

    (1)       我们要建立2个表PARENT和CHILD。CHILD表具有PARENT表上的外键,这个外键要定义为可延迟。

    复制代码
    SQL> create table parent(pk int,
      2   constraint parent_pk primary key(pk));
    
    表已创建。
    
    SQL> create table child(fk,
      2   constraint child_fk foreign key(fk)
      3   references parent deferrable);
    
    表已创建。
    复制代码

     

    (2)       现在,我们使用一些数据生成这些表。

    复制代码
    SQL> insert into parent values(1);
    
    已创建 1 行。
    
    SQL> insert into child values(1);
    
    已创建 1 行。
    复制代码

     

    (3)       现在,我们尝试更新PARENT表,改变它的主键值。

    SQL> update parent set pk=2;
    update parent set pk=2
    *
    ERROR 位于第 1 行:
    ORA-02292: 违反完整约束条件 (SCOTT.CHILD_FK) - 已找到子记录日志

     

    (4)       为了解决这个问题,我们只需告诉Oracle我们将要延迟约束CHILD_FK,也就是说,我们不想它在语句层次进行检查。

    SQL> set constraints child_fk deferred;
    约束条件已设置。

     

    (5)       现在,我们可以正确地更新PARENT表

    复制代码
    SQL> update parent set pk=2;
    已更新 1 行。
    
    SQL> select * from parent;
            PK
    ----------
             2
    
    SQL> select * from child;
            FK
    ----------
             1
    SQL> commit;
    commit
    *
    ERROR 位于第 1 行:
    ORA-02091: 事务处理已重算
    ORA-02292: 违反完整约束条件 (SCOTT.CHILD_FK) - 已找到子记录日志
    复制代码

     

    (6)       我们再次进行尝试,看看如何让Oracle验证我们数据的逻辑一致性,而不进行回滚。我们首先会再次设置约束DEFERRED,然后再次更新父表,重做Oracle刚才撤销的工作。

    复制代码
    SQL> set constraints child_fk deferred;
    约束条件已设置。
    
    SQL> update parent set pk=2;
    已更新 1 行。
    
    SQL> select * from parent;
            PK
    ----------
             2
    
    SQL> select * from child;
            FK
    ----------
             1
    复制代码

     

    (7)       我们现在将CHILD_FK约束设置为IMMEDIATE。这将导致Oracle立即验证我们业务规则的一致性。

    SQL> set constraints child_fk immediate;
    SET constraints child_fk immediate
    *
    ERROR 位于第 1 行:
    ORA-02291: 违反完整约束条件 (SCOTT.CHILD_FK) - 未找到父项关键字

     

    (8)       现在,我们可以更新CHILD记录,再次检查约束并且提交

    复制代码
    SQL> update child set fk=2;
    已更新 1 行。
    
    SQL> set constraints child_fk immediate;
    约束条件已设置。
    
    SQL> commit;
    提交完成。
    SQL> select * from parent;
            PK
    ----------
             2
    
    SQL> select * from child;
            FK
    ----------
             2
    复制代码

     

    这将会导致一致性数据满足我们的约束。

    12.3.3             隔离性

    在给定用户隔离层次的情况下,使用相同的输入,采用相同的方式执行的相同的工作可能会导致不同的答案,这些隔离层次采用指定层次上许可(或者不符合规定)的三种“读取”方式进行定义。它们是:

    ●脏读取(Dirty read)——这种读取方式的含义同它听起来一样糟糕。您可以读取没有提交的“脏”数据。

    ●非可重复读取(Non-repeatable read)——这种方式意味着,如果用户在T1时刻读取了一行,在T2时刻再次读取一行,那么这个行就可能发生改变。例如,它可能已经被删除或者更新。

    ●影像读取(Phantom read)——这种方式意味着如果用户在T1时刻执行了一个查询,在T2时刻再次执行它,就可能会有影响结果的附加行加入到数据库中。在这种情况下,这种读取方式与非可重复读取有所不同,您已经读取的数据不会发生变化,而是会有比以前更多的满足查询条件的数据。

    SQL92采用了这三种“读取”方式,并且基于它们的存在与否建立了4种隔离层次,见表13-2,它们是:

    隔离层次

    脏读取

    非重复读取

    影像读取

    非提交读取(Read Uncommitted)

    允许

    允许

    允许

    提交读取(Read committed)

    禁止

    允许

    允许

    可重复读取(Repeatable Read)

    禁止

    禁止

    允许

    串行读取(Serializable)

    禁止

    禁止

    禁止

     

    12.3.4             持久性

    持久性是数据库提供的最重要的特性之一。它可以确保一旦事务处理提交之后,它的改变就会永久生效。它们不会由于系统故障或者错误而“消失”。

     

    12.4  并发控制

    开发多用户、数据库驱动的应用 的主要挑战之一就是要最大化并发访问(多个用户同时访问数据),而且与此同时,还要确保每个用户都能在一致的方式下读取和修改数据。能够提供这些功能的锁定和并发控制是所有数据库的关键特性。

    12.4.1             锁定

    锁定(lock)是用来控制共享资源并发访问的机制。要注意,我们使用术语“共享资源”,而没有使用“数据库行”或者“数据库表”。Oracle确实可以在行级别上锁定表数据,但是它还可以在许多不同的层次上使用锁定,提供对各种资源的并发访问。

    大多数情况下,锁对于作为开发者的用户来讲是透明的。当更新数据行的时候,我们不必对其进行锁定,Oracle会为我们完成这些工作。有些时候显式锁定是必须的,但是大多数时候,锁定会自行管理。

    在单用户的数据库中,锁根本没有必要。因为根据定义,在这种情况下只有一个用户会修改信息。然而,当多用户访问或者修改数据或者数据结构的时候,具有可以防止并发访问修改相同信息的机制就分外重要。这就是锁定的价值。

    1. 死锁

    当2个用户战胜了2者都希望使用的资源时就会出现死锁。

    Oracle处理死锁的方式非常简单。当检测出死锁的时候(它们就会立刻被检测出来),Oracle就会选择一个会话作为“牺牲者”。

    试验:引发死锁

    (1)       我们要建立2个所需要的表,如下所示:

    SQL> create table a as select 1 x from dual;
    表已创建。
    
    SQL> create table b as select 1 x from dual;
    表已创建。

     

    (2)       现在,打开SQL*Plus会话,并且更新表A。

    SQL> update a set x=x+1;
    
    已更新 1 行。

     

    (3)       新打开一个SQL*Plus会话,更新B表。

    SQL> update b set x=x+1;
    
    已更新 1 行。

     

    (4)       在相同的会话,再一次更新a表

    SQL> update a set x=x+1;

     

    会发生死锁。

    1. 锁定升级

    可以在许多层次上进行锁定。用户可以在一行上拥有锁定,或者实际上也可以在一个表上拥有锁定。一些数据库(尽管没有Oracle)还可以在数据库层次上锁定数据。在一些数据库中,锁定是稀缺的资源,拥有许多锁定可以负面地影响系统的性能。在这些数据库中,你可以发现为了保存这些资源,用户的100个行级别的锁定会转换为一个表级别的锁定。这个过程就称为锁定升级(lock escalation)。它使得系统降低了用户锁定的粒度。

    锁定升级不是数据库想要的属性。事实上,数据库支持升级锁定暗示着它的锁定机制存在固有的系统开销,管理上百个锁定是需要处理的重要工作。在Oracle中,拥有一个锁定或者上百万个锁定的系统开销是相同的——没有区别。

    Oracle会使用锁定转换(lock conversion)或者提升(promotion)。它将会在尽可能级别上获取锁定,并且将锁定转换到更具限制性的级别上。例如,如果使用FOR UPDATE子句从表中选取一行,那么就会应用2个锁定。一个锁定会放置在您所选择的行上。这个锁定是的,它将会阻止其它的用户锁定指定行。另一个锁定要放置在表上,它是一个ROW SHARE TABLE锁。这个锁将会阻止其它会话获取表上的排它锁。

    1. 遗失更新

    阻塞(Blocking)会在一个会话拥有另一个会话正在请求的资源上的锁定时出现。正在进行请示的会话一直阻塞,直到占用资源的会话翻译锁定资源为止。例如,USER1、USER2同时查询,1分钟后,USER1修改了USER3的地址,USER2的会话仍然为1分钟前的数据,修改了USER3的电话号码,此时,USER2的旧数据替换了USER1修改的数据。

    1. 悲观锁定

    悲观锁定(Pessimistic locking)听起来不好,但是相信我,它实际并非如此。在使用悲观锁定的时候,您就是在表明“我相信,某人很有可能会改变我正在读取(并且最终要更新)的相同数据,因此,在我们花费时间,改变数据之前,我要锁定数据库中的行,防止其它会话更新它”。

    为了完成这项工作,我们要使用类似如下语句的查询:

    复制代码
    select *
    from table
    where column1 =  : old_column1
        and column2 =  : old.column2
        and...
        and primary_key =  : old_primary_key
        for update nowait;
    复制代码

     

    所以我们将会从这个语句中得到三种结果:

    ●我们将要获取我们的行,并且对这个行进行锁定,防止被其它会话更新(但是阻止读取)。

    ●我们将会得到ORA-00054 Resource Busy错误。其它的人已经锁定了这个行,我们必须要等待它。

    ●由于某人已经对行进行了改变,所以我们将会返回0行。

    由于我们要在进行更新之前对行进行锁定,所以这称为悲观锁定(pessimistic locking)。我们对行维持不被改动并不乐观(因此命名为悲观)。用户应用中的活动流程应该如下所示:

    ●不进行锁定查询数据

    复制代码
    SQL> select empno,ename,sal from emp where deptno=10;
    
         EMPNO ENAME             SAL
    ---------- ---------- ----------
          7782 CLARK            2450
          7839 KING             5000
          7934 MILLER           1300
    复制代码

     

    ●允许终端用户“查询”数据。

    复制代码
    SQL> select empno,ename,sal
      2  from emp
      3  where empno=7934
      4  and ename='MILLER'
      5  and sal=1300
      6  for update nowait
      7  /
    
         EMPNO ENAME             SAL
    ---------- ---------- ----------
          7934 MILLER           1300
    复制代码

     

    ●假如我们锁定了数据行,我们的应用就可以使用更新,提交改变

    复制代码
    SQL> update emp
      2  set ename='miller'
      3  where empno=7934;
    已更新 1 行。
    
    SQL> commit;
    提交完成。
    复制代码

     

    在Oracle中,悲观锁定可以运行良好。它可以让用户相信他们正在屏幕上修改的数据当前由他们所“拥有”。如果用户要离开,或者一段时间没有使用记录,用户就要让应用释放锁定,或者也可以在数据库中使用资源描述文件设定空闲会话超时。这样就可以避免用户锁定数据行,然后回家过夜的问题。

    1. 乐观锁定

    第二种方法称为乐观锁定(optimistic locking),这经会在应用中同时保存旧值和新值。

    我们首先作为用户SCOTT登录,并且打开2个SQL*Plus会话,我们要作为2个独立的用户(USER1和USER2)。然后,2个用户都要使用以下的SELECT语句。

    USER1

    SQL> select * from dept where deptno=10;
    
        DEPTNO DNAME          LOC
    ---------- -------------- -------------
            10 ACCOUNTING     NEW YORK

     

    USER1更新

    SQL> update dept
      2  set loc='BOSTON'
      3  where deptno=10;
    已更新 1 行。

     

    USER2更新

    SQL> update dept
      2  set loc='ALBANY'
      3  where deptno=10;
    已更新 1 行。

     

    DEPTNO=10的LOC=’BOSTON’的记录不再存在,所以这里更新了0行。我们乐观地认为更新可以完成。

    1. 悲观锁定和乐观锁定

    悲观锁定最大的优势在于终端用户可以在他们花费时间进行修改之前,就能够发现他们的改变不能够进行。而在乐观锁定的情况中,终端用户将会花费大量的时间进行修改。

    12.4.2             多版本和读取一致性

    Oracle使用了多版本读取一致性并发模型。从根本讲,Oracle通过这个机制可以提供:

    ●读取一致性查询(Read-consistent queries):能够产生结果与相应时间点一致的查询。

    ●非阻塞查询(Non-blocking queries):查询不会被数据定入器阻塞。

    复制代码
    SQL> drop table t;
    
    表已丢弃。
    
    SQL> create table t as select * from all_users;
    
    表已创建。
    
    SQL> set serverout on
    SQL> declare
      2   cursor c1 is select username from t;
      3   l_username varchar2(30);
      4  begin
      5   open c1;
      6   delete from t;
      7   commit;
      8   loop 
      9    fetch c1 into l_username;
     10    exit when c1%notfound;
     11    dbms_output.put_line(l_username);
     12   end loop;
     13   close c1;
     14  end;
     15  /
    SYS
    SYSTEM
    OUTLN
    DBSNMP
    WMSYS
    ORDSYS
    ORDPLUGINS
    MDSYS
    ……
    
    PL/SQL 过程已成功完成。
    复制代码

     

    虽然从表中删除所有数据,我们甚至可以提交删除工作。这些行都不见了,我们也会这样认为。事实上,它们还可以通过游标获得。实际上,通过OPEN命令返回给我们的结果集在打开它的时候就已经预先确定了。我们在获取数据之前没有办法知道答案是什么,但是从游标的角度来看,结果是不变的。并不是Oracle在我们打开游标的时候,将所有以上数据复制到了其它的位置;事实上是删除操作作为我们保留了数据,将它们放置到了UNDO表空间或者回滚段中。

    考虑一个进一步的示例。

    建立一个ACCOUNTS的表。可以使用如下方式建立它:

    复制代码
    SQL> create table accounts
      2  (
      3   account_id number,
      4   account_type varchar2(20),
      5   balance number
      6  );
    
    表已创建。
    复制代码

     

    精确快速地报告BALANCE总数。用户需要报告时间以及所有账户余额总和:

    SQL> select current_timestamp,sum(balance) total_balance from accounts;

     

    注意:

    CURRENT_TIMESTAMP是Oracle 9i的内建列,它将会返回当前的日期和时间。在较早的Oracle版本中,用户需要使用SYSDATE。

    然而,如果当我们进行处理的时候,这个数据库表上要应用成百上千个事务处理,那么问题就会稍微复杂一些。人们会从他们的储蓄账号向支票账号划转资金,他们还要进行取款、存款(我们可能是一个非常繁忙的银行)等。

    我们假定ACCOUNTS表如表13-3所示:

    表13-3 ACCOUNTS表示例

    ACCOUNT_ID

    ACCOUNT_TYPE

    BALANCE

    1234

    储蓄

    100

    5678

    支票

    4371

    2542

    储蓄

    6232

    7653

    储蓄

    234

    …<上百万行>

     

     

    1234

    支票

    100

    我们的示例将要有2个结局,一个结局将要展示当查询检测到锁定灵气的时候会出现什么情况,另一个结局展示当查询检测到数据已经发生改变,它不能够看到它们的时候会出现什么情况。

    表13-4 查询遇到锁定数据

    时间

    事件

    注释

    T1

    我们在会话1中开始查询W(现在有150)

    它要开始对表进行读取,由于ACCOUNTS表非常大,所以这 需要花几分钟的时间。这个查询已经读取了“第一行”的ACCOUNT_ID为1234储蓄账号,但是还没有到达支票账号

    T2

    账号1234的所有者在ATM上开始事务处理

     

    T3

    账号1234的所有者选取TRANSFERFUNDS,并且选取从他们的支票账号向储蓄账号划转50美金(150-50)

    数据库中的数据进行了更新,所以支票账号现在具有50美金,而储蓄账号具有150美金。工作还没有提交(但是已经存储了UNDO信息)

    T4

    我们查询最终到达ACCOUNT_ID为1234的支票账户行

    这时发生什么呢?在其它大多数流行数据库中,答案是“查询将要等待”。而Oracle中不会这样

    T5

    由于检测到数据已经被T3时刻执行的工作所锁定,所以我们的查询将要接受到UNDO信息(数据“以前的”映像),并且使用T1时刻的数据映像

    Oracle将要讲到锁定,它不会等待。我们查询将要在支票账号读取到100美金

    T6

    我们的报告生成

     

    T7

    ATM提交并且完成

     

    本将操作的有意思的部分发生在以上时间链T5时刻

    表13-5 查询遇到已经改变的数据

    时间

    事件

    注释

    T4

    ATM会话进行提交,完成转账

    资金发生转移,另外的会话现在可以看到在ACCOUNT_ID为1234的储蓄账号中有150美金,支票账号中有50美金

    T5

    我们查询最终到达ACCOUNT_ID为1234的支票账户行

    它不再锁定,没有人正在更新它

    T6

    由于检测到在T1时刻之后数据已经进行了修改,我们的查询将会接收到UNDO信息,并且使用T1时刻的数据映像

    我的查询将要再次为支票账号读取100美金

    T6

    我们的报告生成

     

    简单来说,Oracle除了删除,在更新、增加中,能够把UNDO表空间或者回滚段中的数据读取出来,来为客户展示它数据的一致性。

    12.5  小结

    在本章中,我们首先了事务处理的构成。了解了事务处理控制语句,以及怎样和在什么时候使用它们。

    当讨论并发控制的时候,我们讨论了2个重要而又复杂的主题,它们是锁定和多版本读取一致性。了解了死锁、跟踪文件分析死锁、数据库中避免阻塞等待的不同模式、悲观锁定和乐观锁定。

     

    文章根据自己理解浓缩,仅供参考。

    摘自:《Oracle编程入门经典》 清华大学出版社 http://www.tup.com.cn


    from: http://www.cnblogs.com/yongfeng/p/3219593.html

    展开全文
  • 数据库的基本概念总结 1. 数据库定义:数据库是长期储存在计算机内、有组织的、可共享的大量数据的集合。...主要功能:1,数据定义功能。2,数据组织、存储和管理。3,数据操纵功能。4,数据库的事务管理和

    数据库的基本概念总结
    1. 数据库定义:数据库是长期储存在计算机内、有组织的、可共享的大量数据的集合。数据库中的数据按一定的数据模型组织、描述和储存,具有较小的冗余度、较高的数据独立性和易扩展性,并可为各种用户共享。
    2. 数据库管理技术发展的三个阶段:人工管理阶段,文件系统阶段,数据库系统阶段。
    3. DBMS(数据库管理系统)是位于用户与操作系统之间的一层数据管理软件。主要功能:1,数据定义功能。2,数据组织、存储和管理。3,数据操纵功能。4,数据库的事务管理和运行管理。5,数据库的建立和维护功能。6,其他功能。
    4. 什么是数据模型及其要素? (设计题): 数据模型是数据库中用来对现实世界进行抽象的工具,是数据库中用于提供信息表示和操作手段的形式构架。一般地讲,数据模型是严格定义的概念的集合。这些概 念精确地描述系统的静态特性、动态特性和完整性约束条件。因此数据模型通常由数据结构、数据操作和完整性约束三部分组成。 (1)数据结构:是所研究的对象类型的集合,是对系统的静态特性的描述。 (2)数据操作:是指对数据库中各种对象(型)的实例(值)允许进行的操作的集合,包括操作及有关的操作规则,是对系统动态特性的描述。 (3)数据的约束条件:是完整性规则的集合,完整性规则是给定的数据模型中数据及其联系所具有的制约和依存规则,用以限定符合数据模型的数据库状态以及状态的变化,以保证数据的正确、有效、相容。
    最常用的数据模型:层次模型,网状模型,关系模型,面积对象模型,对象关系模型。
    5.常用的数据模型有哪些(逻辑模型是主要的),各有什么特征,数据结构是什么样的。
    答:数据模型可分为两类:
    第一类是概念模型,也称信息模型,它是按用户的观点来地数据和信息建模,主要用于数据库设计。
    第二类是逻辑模型和物理模型。
    其中逻辑模型主要包括层次模型、层次模型、关系模型、面向对象模型和对象关系模型等。它是按计算机系统的观点对数据建模,主要用于DBMS的实现。
    物理模型是对数据最低层的抽象,它描述数据在系统内部的表示方式和存取方法,在磁盘或磁带上的存储方式和存取方法,是面向计算机系统的。物理模型是具体实现是DBMS的任务,数据库设计人员要了解和选择物理醋,一般用户则不必考虑物理级的细节。
    层次数据模型的数据结构特点:一是:有且只有一个结点没有双亲结点,这个结点称为根结点。二是:根 以外的其他结点有且只有一个双亲结点。优点是:1.层次 数据结构比较简单清晰。2.层次数据库的查询效率高。3.层次数据模型提供了良好的完整性支持。缺点主要有:1.现实世界中很多联系是非层次性的,如结点之间具有多对多联系。2.一个结点具有多个双亲等 ,层次模型表示这类联系的方法很笨拙,只能通过引入冗余数据或创建非自然的数据结构来解决。对插入和删除操作的限制比较多,因此应用程序的编写比较复杂。3.查询子女结点必须通过双亲结点。4.由于结构严密,层次命令趋于程序化。可见用层次模型对具有一对多的层次联系的部门描述非常自然,直观容易理解,这是层次数据库的突出优点。
    网状模型:特点:1.允许一个以上的结点无双亲2.一个结点可以有多于一个的双亲。网状数据模型的优点主要有:1.能够更为直接地描述现实世界,如一个结点可以有多个双亲。结点
    之间可以有多种上联第。2.具有良好的性能,存取效率较高。缺点主要有:1.结构比较复杂,而且随着应用环境的扩大,数据库的结构就变得越来越复杂,不利于最终 用户掌握。2.网状模型的DDL,DML复杂,并且要嵌入某一种高级语言中,用户不容易掌握,不容易使用。
    关系数据模型具有下列优点:1.关系模型与非关系模型不同,它是建立在严格的数学概念的基础上的。2.关系模型的概念单一。。3.关系模型的存取路径对用户透明,从而具有更高的数据独立性,更好的安全保密性,也简化了程序员的工作和数据库开发的建立 的工作。。主要的缺点是:由于存取路径房租明,查询效率往往不如非关系数据模型。因此为了提高性能,DBMS必须对用户的查询请求进行优化。因此增加 了开发DBMS的难度,不过用户不必考虑这些系统内部的优化技术细节。
    6.三级体系结构,外模式,模式 ,内模式定义是什么?
    模式也称逻辑模式,是数据库中全体数据的逻辑结构和牲的描述,是所有用户的公共数据视图。 外模式也称子模式或用户模式,它是数据库用户能够看见和使用的局部数据的逻辑结构和特征的描述,是数据库用户的数据视图是与某一应用有关的数据的逻辑表示。 内模式也称存储模式 ,是一个数据库只有一个内模式。它是数据物理结构和存储方式的描述,是数据在数据库内部的表示方式。
    7.两级映像和两级独立性,为什么叫物理独立性和逻辑独立性。
    当模式改变时由数据库管理员对各个外模式、模式的映像亻相应改变,可以使外模式保持不变。应用程序是依据数据的外模式编写的,从而应用程序不必修改,保证了数据与程序的逻辑独立生,简称数据的逻辑独立性。
    当数据库的存储结构改变了,由数据库管理员对模式、内模式映像作 相应改变,可以使模式保持不变,从而应用程序也不必改变。保证了数据与程序的物理独立性,简称数据的物理独立性。
    8.数据库系统一般由数据库、数据库管理系统 (及其开发工具)、应用系统和数据库管理员构成。
    9.关系的完整性(实体完整性、参照完整性、和用户定义的完整性)三部分内容,其中前二者是系统自动支持的,DBMS完整性控制子系统的三个主要功能?:提供定义完整性约束条件的机制,提供完整性检查的方法,违约处理。
    16.SQL的定义;即结构化查询语言,是关系数据库的标准语言,是一个通用的、功能极强的关系数据库语言。分类(交互式和嵌入式)17.group by 和having子句的作用
    20.视图的概念:视图是从一个或几个基本表导出的表。及相关操作:定义视图,查询视图,更新视图。视图更新有什么操作:插入,删除,和修改。
    22.数据库规范化的方法函数依赖的定义什么叫1NF2NF3NF BCNF定义:关系数据库中的关系是要满足一定要求的,满足不同程度要求的为不同范式。满足最低要求的叫第一范式,简称1NF。在第一范式中满足进一步要求的为第二范式,其余以此类推。各种范式之间的联系有:5NF(4NF(BCNF(3NF(2NF(1NF。
    25.数据库设计的几个阶段,每个阶段常用的方法和简要的内容:六个阶段:需求分析、概念结构设计、罗织结构设计、物理设计、数据库实施、数据库运行和维护。
    28.事务的概念?事务有哪些基本属性commit roll back含义:事务:是用户定义的一个数据库操作序列,这些操作要么全做,要么全不做,是一个不可分割的单位。四个特性:原子性,一致性,隔离性,持续性。Commit(提交:提交事务的所有操作) rollback(回滚:在事务运行的过程中发生了某种故障,事务不能继续执行,系统将事务中对数据库的所有已完成的操作全部撤销,回滚到事务开始时的状态。
    29.什么叫数据库系统的可恢复性?:数据库管理系统具有把数据库从错误状态恢复到某一已知的正确状态的功能,这就是数据库系统的可恢复性。数据库故障的种类:事务内部的故障,系统故障(软故障),介质故障(硬故障),计算机病毒。
    30.不进行并发控制可能产生的问题?:多个事务对数据库并发操作可能造成事务ACID特点遭到在破坏。如何解决(三个):1,丢失修改 2,不可重复读 3,读“脏”数据。
    31.三级封锁协议?能解决什么问题?:一级封锁协议:事务T在修改数据R之前必须先对其加X锁,直到事务结束才释放。事务结束包括正常结束(COMMIT)和非正常结束(ROLLBACK)。一级封锁协议中,如果是读数据不修改,是不需要加锁的,可防止丢失修改。二级封锁协议:在一级封锁协议基础上,加上事务T在读数据R之前必须先对其加上S锁,读完后即可释放S锁。在二级封锁协议中,由于读完数据后即可释放S锁,所以它不能保证可重复读。三级封锁协议:一级封锁协议加上事务T在读取数据R之前必须先对其加S锁,直到事务结束才释放。三级封锁协议除了防止了丢失修改和不读“脏”数据外,还进一步防止了不可重复读。
    上述三级协议的主要区别在于:什么操作需要申请封锁,以及何时释放锁。
    一般采取哪三种措施?插入呢?删除呢?:1,拒绝执行(不允许该操作执行),2,级连操作(当删除或修改被参照表的一个元组造成了与参照表的不一致,则删除或修改参照表中的所有造成不一致的元组),3,设置为空值(当删除或修改被参照表的一个元组时造成了不一致,则将参照表中的所有不造成不一致的元组的对应属性设置为空值)。
    38.视图对数据库安全性的作用?:1,视图能够简化用户的操作,2,视力使用户能以多种角度看待同一数据,3,视图对重构数据库提供了一定程度的逻辑独立性,4,视图能够对机密数据提供安全保护,5,适当的利用视图可以更清晰的表达查询。
    数据库:储存在计算机内,永久存储、有组织、有共享的大量数据的集合。
    数据管理技术的发展阶段:1.人工管理阶段:数据不保存,应用程序管理数据,数据不共享,数据不具有独立性。2.文件系统阶段:数据可以长期保存,由文件系统管理数据;数据共享性太差,冗余度大,数据独立性差。3.数据库系统阶段:出现数据库管理系统。
    数据库系统的特点:数据结构化(本质区别);数据共享性高、冗余度低、易扩充;数据独立性高;数据有DBMS统一管理和控制。
    数据库管理系统:1.定义:DBMS,是位于用户与操作系统之间的一层数据管理软件。2.功能:数据定义功能;数据组织、存储和管理;数据操纵功能;数据库的事务管理和运行管理;数据库的建立和维护功能;通信功能、数据转换功能、互访和互操作功能。
    数据库系统:1.概念:DBS,是指在计算机系统中引入数据库后的系统。2.组成:一般由数据库、数据库管理系统、应用系统、数据库管理员构成。3.分类:集中式,C/S式,并行式,分布式。
    数据模型:1.定义:现实世界数据特征的抽象。2.组成,三要素:数据结构、数据操作、数据的完整性约束。两类数据模型为1)概念模型2)逻辑模型和物理模型。
    数据结构:描述数据库的组成对象以及对象之间的联系,主要描述与对象的类型、内容、性质有关的对象和与数据之间联系有关的对象。
    常用的数据模型:1.层次模型,用树形结构表示各类实体以及实体间的联系。2.网状模型,允许一个以上的结点无双亲,允许一个结点可以有多于一个的双亲。3.关系模型,包含单一数据结构


    数据库的并发控制技术

    数据库是一个共享资源,可以供许多用户使用。各个用户程序(通常为一个事务)可以一个一个地串行执行,即每个时刻只有一个用户程序运行,执行数据库的存取操作,其它用户程序必须等到这个用户程序结束后方能对数据库存取。但是,由于用户程序在执行过程中随着时间的不同需要不同的资源,有时需要CPU,有时需要访问磁盘,有时需要I/O,有时需要通讯等,如果只让一个用户程序运行,而其它用户闲置等待,则导致许多系统资源在大部分时间内处于闲置状态。 因此,为了充分利用系统资源,发挥数据库共享资源的特点,应该允许各个用户并行地存取数据。但这样就会产生多个用户程序并发存取同一数据的情况。若对并发操作不加控制就可能导致存取和存储不正确的数据,从而破坏数据库的一致性。
    所以DBMS必须提供并发控制机制,且并发控制机制的好坏是衡量一个DBMS性能的重要指标之一。

    一.事务及其特性
    DBMS的并发控制是以事务(transaction)为单位进行的,因此,本节先介绍事务的概念及其特性。
    在多用户环境中用户程序不断访问数据库中的数据,构成了若干操作序列,在这些操作序列中,有些操作必须作为一个整体来执行,这些作为一个整体来执行的操作序列称为一个事务(Transaction)。即事务是用户定义的一组操作序列的集合,是数据恢复和并发控制的基本单位。数据库系统在执行事务时,要么执行事务中全部操作,要么一个操作都不执行。一个应用程序往往由若干个独立的事务组成。在应用程序中,事务的开始与结束可以由用户显示地控制。如果用户没有显示地定义事务,则由DBMS自动地按照缺省方式划分事务。在SQL中,用户显示定义事务的语句有如下3条:
    1、BEGIN TRANSACTION,该语句显示地定义一个事务的开始。
    2、COMMIT,该语句显示地提交一个事务,并表示该事务已正常结束。
    3、ROLLBACK,该语句显示地回滚一个事务,且表示事务因执行失败而结束。
    所谓提交事务,就是将该事务对数据库的所有更新操作结果永久地保存到磁盘上的物理数据库中去。
    所谓回滚事务,就是撤消该事务对数据库的所有更新操作,使数据库恢复到该事务开始时的状态。
    由以上可知,用户若要显示地定义事务,必须以BEGIN TRANSATION开始,而以COMMIT 或 ROLLBACK结束。
    事务具有四个特性,即原子性、一致性、隔离性和持续性,又常简称为ACID特性:
    1、原子性 :即一个事务是不可分割的数据库逻辑工作单位。
    2、一致性 :事务的执行结果必须使数据库从一个一致性状态变到另一个一致性状态。
    3、隔离性 :一个事务的执行不能被其它事务干扰。
    4、持续性 :持续性也称为永久性,指一个事务一旦提交,它对数据库中数据的改变应该是永久性的,其它操作或故障不对其产生任何影响。

    二.数据库的并发控制
    DBMS的并发控制是以事务为单位进行的。数据库在执行事务时,要么执行事务中的全部操作,要么一个操作都不执行。
    当有多个事务对数据库进行操作时,如果对数据库进行操作的各个事务按顺序执行,即一个事务执行完全结束后,另一个事务才开始,则称这种执行方式为串行访问(Serial access)。
    如果DBMS可以同时接纳多个事务,事务可以在时间上重叠执行,则称这种执行方式为并发访问(Concurrent access)。
    在单CPU系统中,同一时间只能有一个事务占用CPU,若各个事务交叉使用CPU,则称这种并发方式为交叉并发。
    在多CPU系统中,可以允许多个事务同时占用CPU,这种并发方式称为同时并发。

    三.并发的目的
    在数据库管理系统中对事务采用并发机制的主要目的有两个:
    1、改善系统的资源利用率
    对于一个事务来讲,在不同的执行阶段需要的资源不同,有时需要CPU,有时需要访问磁盘,有时需要I/O、有时需要通信如果事务串行执行,有些资源可能会空闲;如果事务并发执行,则可以交叉利用这些资源,有利于提高系统资源的利用率
    2、改善短事务的响应时间
    设有两个事务T1 和T2,其中T1是长事务,交付系统在先;T2是短事务,交付系统比T1稍后。如果串行执行,则须等T1执行完毕后才能执行T2。而T2的响应时间会很长。一个长事务的响应时间长一些还可以得到用户的理解,而一个短事务的响应时间过长,用户一般难以接受。
    如果T1 和T2并发执行,则T2可以和T1重叠执行,可以较快地结束,明显地改善其响应时间。

    四.并发所引起的问题
    数据库中的数据是共享的,即多个用户可以同时使用数据库中的数据,这就是并发操作。
    但是当多个用户存取同一组数据时,由于相互的干扰和影响,并发操作可能引发错误的结果,从而导致数据的不一致性问题我们将用下面的例子予以说明。
    例5.5 设有一个飞机机票订票系统,考虑如下售票活动的并发操作问题:
    1、售票员A通过网络在数据库中读出某航班的机票余额为y张,设y=15;
    2、售票员B通过网络在数据库中读出某航班的机票余额为y张,设y=15;
    3、售票员A卖出一张机票,然后修改余额为y=y?1,此时,y=14,将14写会数据库;
    4、售票员B卖出一张机票,然后修改余额为y=y?1,此时,y=14,将14写会数据库;
    这个售票活动并发操作的结果是:实际已经卖出2张机票,但在数据库中机票余额仅减少1,从而导致错误。这就是著名的飞机机票订票系统问题。
    这个问题之所以会产生错误,其根本原因在于两个用户反复交叉地使用同一个数据库的结果。
    如果事务不加控制地并发执行,会产生以下几个问题:
    1、丢失修改(Lost update)
    2、脏读(Dirty read)。
    3、不能重读(Unrepeatable read)

    五.并发控制方法
    实现现数据库并发控制的方法有多种,常用的有封锁技术、时标技术和版本更新技术等,而封锁技术在商品化数据库管理系统中使用得最为普遍。
    本节中介绍:
    (1)封锁技术
    (2)时标技术

    1.封锁技术
    封锁的定义
    锁是为了防止其它事务访问指定资源的一种手段。封锁(locking)是实现并发控制的一个非常重要的技术。
    所谓封锁即是在一段时间内禁止某些用户对数据对象做某些操作,以避免产生数据的不一致性问题。即事务T在对某个数据对象,如表、元组等进行操作之前,先向系统发出请求,并对其加锁。加锁成功后,事务T就对数据对象有了一定的控制权,在事务T释放它的锁之前,其它的事务就不能更新此数据对象。
    封锁的类型
    基本的封锁一般有排它锁和共享锁两种类型。
    (1)排它锁:又称X锁,如果某事务T对某数据建立了排它锁,则该事务能对该数据对象进行读、修改、插入和删除等操作,而其它事务则不能。
    (2)共享锁:又称S锁,如果某事务对某数据建立了共享锁,则此时该事务能对该数据对象进行读操作,但不能进行修改等更新操作;而其它事务只能对该数据对象加S锁,而不能加X锁,即其它事务只能对该数据对象进行读操作。

    封锁粒度
    所谓封锁的粒度,即是被封锁数据对象的大小。在关系数据库中,封锁的粒度有如下几种:属性值、属性值集、元组、关系、索引项、整个索引或整个数据库。还可以是一些物理单元:页(数据页或索引页)、块等等。
    封锁的粒度与系统的并发度和并发控制的开销密切相关。封锁粒度越大,系统中能够被封锁的对象就越少,并发度也就越小,但同时系统开销也越少;封锁粒度越小,并发度越高,但系统开销也就越大。因此,如果在一个系统中能同时存在不同大小的封锁粒度对象供不同的事务选择使用,是比较理想的。

    封锁协议
    封锁的目的是为了保证能够正确地调度和控制并发操作。为此,在运用X锁和S锁这两种基本锁对一定粒度的数据对象加锁时,还需约定一些规则。
    例如,一个事务何时该申请X锁或S锁、事务持锁时间的长短如何控制、何时释放所获得的锁等等。我们称这些规则为封锁协议 (Locking protocol)。
    下面介绍三级封锁协议:
    1、一级封锁协议:某事务T若要修改某个数据对象,则必须先对该数据对象加X锁,直到事务结束才释放。此种封锁协议可防止“丢失修改”所产生的数据不一致性问题。
    2、二级封锁协议:一级封锁协议加上某事务T若要读取某个数据对象之前,则必须先对该数据对象加S锁,读完后即可释放S锁,这样可进一步防止“读脏数据”的问题。
    3、三级封锁协议:一级封锁协议加上某事务T若要读取某个数据对象之前,则必须先对该数据对象加S锁,且知道该事务结束后才可释放S锁,这样可进一步防止数据“不可重复读”的问题。

    死锁和活锁
    活锁:如果事务T1封锁了数据对象R后,事务T2也请求封锁R,于是T2等待。接着T3也请求封锁R。当T1释放了加在R上的锁后,系统首先批准了T3的请求,T2只得继续等待。接着T4也请求封锁R,T3释放R上的锁后,系统又批准了T4的请求,……,因此,事务T2就有可能这样永远地等待下去。以上这种情况就称为活锁。
    避免活锁的简单办法是采用先来先服务的策略。当多个事务请求封锁同一数据对象时,封锁子系统按封锁请求的先后次序对这些事务排队,该数据对象上的锁一旦释放,首先批准申请队列中的第一个事务获得锁。

    死锁:如果事务T1封锁了数据对象A,T2封锁了数据对象B之后,T1又申请封锁数据对象B,且T2又申请封锁数据对象A。因T2已封锁了B,于是T1等待T2释放加在B上的锁。因T1已封锁了A,T2也只能等待T1释放加在A上的锁。这样就形成了T1在等待T2结束,而T2又在等待T1结束的局面。T1和T2这两个事务永远不能结束,这就是死锁问题。
    死锁的另一情况是数据库系统有若干个长时间运行的事务在执行并行的操作,当查询分析器处理一种非常复杂的连接查询时,由于不能控制处理的顺序,有可能发生死锁现象。
    目前,解决死锁问题的方法主要有两类:
    预防法:
    此类方法中常用的又有:
    (1)一次封锁法
    (2)顺序封锁法
    诊断解除法

    2.时标技术
    (1).时标的定义
    (2).时标的原理
    (3).基于时标技术的并发控制方法所采用的协议
    (4).基于时标技术的并发调度实例
    (5).时标技术与封锁技术的基本区别
    (6).时标技术的优缺点

    数据库 并发调度技术 四种问题 三级封锁协议

    数据库是一个共享资源,可以提供多个用户使用。这些用户程序可以一个一个地串行执行,每个时刻只有一个用户程序运行,执行对数据库的存取,其他用户程序必须等到这个用户程序结束以后方能对数据库存取。但是如果一个用户程序涉及大量数据的输入/输出交换,则数据库系统的大部分时间处于闲置状态。因此,为了充分利用数据库资源,发挥数据库共享资源的特点,应该允许多个用户并行地存取数据库。但这样就会产生多个用户程序并发存取同一数据的情况,若对并发操作不加控制就可能会存取和存储不正确的数据,破坏数据库的一致性,所以数据库管理系统必须提供并发控制机制。并发控制机制的好坏是衡量一个数据库管理系统性能的重要标志之一。

    DM用封锁机制来解决并发问题。它可以保证任何时候都可以有多个正在运行的用户程序,但是所有用户程序都在彼此完全隔离的环境中运行。

    一、 并发控制的预备知识

    (一) 并发控制概述

    并发控制是以事务(transaction)为单位进行的。

    1. 并发控制的单位――事务

    事务是数据库的逻辑工作单位,它是用户定义的一组操作序列。一个事务可以是一组SQL语句、一条SQL语句或整个程序。

    事务的开始和结束都可以由用户显示的控制,如果用户没有显式地定义事务,则由数据库系统按缺省规定自动划分事务。

    事务应该具有4种属性:原子性、一致性、隔离性和持久性。

    (1)原子性

    事务的原子性保证事务包含的一组更新操作是原子不可分的,也就是说这些操作是一个整体,对数据库而言全做或者全不做,不能部分的完成。这一性质即使在系统崩溃之后仍能得到保证,在系统崩溃之后将进行数据库恢复,用来恢复和撤销系统崩溃处于活动状态的事务对数据库的影响,从而保证事务的原子性。系统对磁盘上的任何实际数据的修改之前都会将修改操作信息本身的信息记录到磁盘上。当发生崩溃时,系统能根据这些操作记录当时该事务处于何种状态,以此确定是撤销该事务所做出的所有修改操作,还是将修改的操作重新执行。

    (2)一致性

    一致性要求事务执行完成后,将数据库从一个一致状态转变到另一个一致状态。它是一种以一致性规则为基础的逻辑属性,例如在转账的操作中,各账户金额必须平衡,这一条规则对于程序员而言是一个强制的规定,由此可见,一致性与原子性是密切相关的。事务的一致性属性要求事务在并发执行的情况下事务的一致性仍然满足。它在逻辑上不是独立的,它由事务的隔离性来表示。

    (3) 隔离性

    隔离性意味着一个事务的执行不能被其他事务干扰。即一个事务内部的操作及使用的数据对并发的其他事务是隔离的,并发执行的各个事务之间不能互相干扰。它要求即使有多个事务并发执行,看上去每个成功事务按串行调度执行一样。这一性质的另一种称法为可串行性,也就是说系统允许的任何交错操作调度等价于一个串行调度。串行调度的意思是每次调度一个事务,在一个事务的所有操作没有结束之前,另外的事务操作不能开始。由于性能原因,我们需要进行交错操作的调度,但我们也希望这些交错操作的调度的效果和某一个串行调度是一致的。 DM实现该机制是通过对事务的数据访问对象加适当的锁,从而排斥其他的事务对同一数据库对象的并发操作。

    (4)持久性

    系统提供的持久性保证要求一旦事务提交,那么对数据库所做的修改将是持久的,无论发生何种机器和系统故障都不应该对其有任何影响。例如,自动柜员机( ATM)在向客户支付一笔钱时,就不用担心丢失客户的取款记录。事务的持久性保证事务对数据库的影响是持久的,即使系统崩溃。正如在讲原子性时所提到的那样,系统通过做记录来提供这一保证。

    DM没有提供显式定义事务开始的语句,第一个可执行的SQL语句(除CONNECT语句外)隐含事务的开始,但事务的结束可以由用户显式的控制。在DM中以下几种情况都结束 (正常,非正常)某一事务:

    (1)当某一连接的属性设置为自动提交,每执行一条语句都会提交;

    (2)遇到COMMIT/ROLLBACK语句,便提交/回滚一事务;

    (3)当系统的 DDL自动提交开关打开时(缺省为打开),遇到DDL语句则自动提交该DDL语句和以前的DML和DDL操作;

    (4)事务所在的程序正常结束和用户退出;

    (5)系统非正常终止时;

    说明:DM在配置文件中提供了DDL语句的自动提交开关DDL_AUTO_COMMIT。 当此配置项的值为 1(缺省情况)时,所有DDL语句自动提交;当此配置项的值为0时,除CREATEDATABASE、ALTERDATABASE和CREATESCHEMA语句外的所有DDL语句都不自动提交。

    DM中的一致性是以事务为基础的。DM通过提交和回滚分别用于将对数据库的修改永久化和废除,但是无论是提交和回滚,DM保证数据库在每个事务开始前、结束后是一致的。为了提高事务管理的灵活性,DM提供了设置保存点(SAVEPOINT)语句和回滚到保存点语句。保存点提供了一种灵活的回滚,事务在执行中可以回滚到某个保存点,在该保存点以前的操作有效,而以后的操作被回滚掉。

    DM中的事务同样具有上述4个属性:原子性、一致性、隔离性和持久性。

    2. 并发操作与数据的不一致性

    如果没有锁定且多个用户同时访问一个数据库,则当他们的事务同时使用相同的数据时可能会发生问题,导致数据库中的数据的不一致性。

    一个最常见的并发操作的例子是火车/飞机订票系统中的订票操作。例如,在该系统中的一个活动序列:

    ① 甲售票员读出某航班的机票张数余额A,设A=16;

    ② 乙售票员读出同一航班的机票张数余额A,也是16;

    ③ 甲售票员卖出一张机票,修改机票张数余额A=A-1=15,把A写回数据库;

    ④ 乙售票员也卖出一张机票,修改机票张数余额A=A-1=15,把A写回数据库。

    结果明明卖出两张机票,数据库中机票余额只减少1。

    这种情况称为数据库的不一致性。这种不一致性是由甲、乙两个售票员并发操作引起的。在并发操作情况下,对甲、乙两个事务操作序列的调度是随机的。若按上面的调度序列行,甲事务的修改就被丢失。这是由于第4步中乙事务修改A并写回覆盖了甲事务的修改。

    并发操作带来的数据库不一致性可以分为四类:丢失或覆盖更新、脏读、不可重复读和幻像读,上例只是并发问题的一种。

    (1) 丢失或覆盖更新(lost update)

    当两个或多个事务选择同一数据,并且基于最初选定的值更新该数据时,会发生丢失更新问题。每个事务都不知道其它事务的存在。最后的更新将重写由其它事务所做的更新,这将导致数据丢失。上面预定飞机票的例子就属于这种并发问题。事务1与事务2先后读入同一数据A=16,事务1执行A-1,并将结果A=15写回,事务2执行A-1,并将结果A=15写回。事务2提交的结果覆盖了事务1对数据库的修改,从而使事务1对数据库的修改丢失了。




    (2) 脏读
    一个事务读取了另一个未提交的并行事务写的数据。当第二个事务选择其它事务正在更新的行时,会发生未确认的相关性问题。第二个事务正在读取的数据还没有确认并且可能由更新此行的事务所更改。换句话说,当事务1修改某一数据,并将其写回磁盘,事务2读取同一数据后,事务1由于某种原因被撤销,这时事务1已修改过的数据恢复原值,事务2读到的数据就与数据库中的数据不一致,是不正确的数据,称为脏读。
    例如,在下图中,事务1将C值修改为200,事务2读到C为200,而事务1由于某种原因撤销,其修改作废,C恢复原值100,这时事务2读到的就是不正确的“脏“数据了。



    (3) 不可重复读(nonrepeatable read)
    一个事务重新读取前面读取过的数据,发现该数据已经被另一个已提交的事务修改过。即事务1读取某一数据后,事务2对其做了修改,当事务1再次读数据时,得到的与第一次不同的值。
    例如,在下图中,事务1读取B=100进行运算,事务2读取同一数据B,对其进行修改后将B=200写回数据库。事务1为了对读取值校对重读B,B已为200,与第一次读取值不一致。



    (4) 幻像读
    如果一个事务在提交查询结果之前,另一个事务可以更改该结果,就会发生这种情况。这句话也可以这样解释,事务1按一定条件从数据库中读取某些数据记录后未提交查询结果,事务2删除了其中部分记录,事务1再次按相同条件读取数据时,发现某些记录神秘地消失了;或者事务1按一定条件从数据库中读取某些数据记录后未提交查询结果,事务2插入了一些记录,当事务1再次按相同条件读取数据时,发现多了一些记录。
    产生上述四类数据不一致性的主要原因是并发操作破坏了事务的隔离性。并发控制就是要用正确的方式调度并发操作,使一个用户事务的执行不受其他事务的干扰,从而避免造成数据的不一致性。
    3. 并发场景列举
    结合SQL语句,列举各种并发情况(包括可能导致数据不一致性和对数据一致性不产生影响的情况)。A表示某一条数据,b和c都表示满足某一个标准的两条或多条数据,^表示“非”的意思,∈表示属于或包含于的意思,1表示第一个事务,2表示第二个事务。



    (二) 并发操作的调度
    计算机系统对并行事务中并行操作的调度是随机的,而不同的调度可能会产生不同的结果,那么哪个结果是正确的,哪个是不正确的呢?
    如果一个事务运行过程中没有其他事务在同时运行,也就是说没有受到其他事务的干扰,那么就可能认为该事务的运行结果是正常的或者预想的,因此将所有事务串行起来的调度策略是正确的调度策略。虽然以不同的顺序串行执行事务也可能会产生不同的结果,但由于不会将数据库置于不一致状态,所以都可以认为是正确的。由此可以得到如下结论:几个事务的并行执行是正确的,当且仅当其结果与按某一次序串行地执行它们的结果相同。我们称这种并行调度策略为可串行化(serializable)的调度。可串行性(serializability)是并行事务正确性的唯一准则。
    例如,现在有两个事务,分别包含下列操作:
    事务1:读B;A=B+1;写回A;
    事务2:读A;B=A+1;写回B;
    假设A的初值为10,B的初值为2。下图给出了对这两个事务的三种不同的调度策略,(a)和(b)为两种不同的串行调度策略,虽然执行结果不同,但他们都是正确的调度。(c)中两个事务是交错执行的,由于执行结果与(a)、(b)的结果都不同,所以是错误的调度。(d)中的两个事务也是交错执行的,由于执行结果与串行调度1(图(a))的执行结果相同,所以是正确的调度。



    为了保证并行操作的正确性,DBMS的并行控制机制必须提供一定的手段来保证调度是可串行化的。
    从理论上讲,在某一事务执行时禁止其他事务执行的调度策略一定是可串行化的调度,这也是最简单的调度策略,但这种方法实际上是不可行的,因为它使用户不能充分共享数据库资源。
    目前DBMS普遍采用封锁方法(悲观方法,DM采用的就是这种方法,SQL Server也是采用的这种方法)来保证调度的正确性;即保证并行操作调度的可串行性。除此之外还有其他一些方法,如时标方法、乐观方法等。

    • 悲观并发控制
    锁定系统阻止用户以影响其它用户的方式修改数据。如果用户执行的操作导致应用了某个锁,则直到这个锁的所有者释放该锁,其它用户才能执行与该锁冲突的操作。该方法主要用在数据争夺激烈的环境中,以及出现并发冲突时用锁保护数据的成本比回滚事务的成本低的环境中,因此称该方法为悲观并发控制。

    • 乐观并发控制
    在乐观并发控制中,用户读数据时不锁定数据。在执行更新时,系统进行检查,查看另一个用户读过数据后是否更改了数据。如果另一个用户更新了数据,将产生一个错误。一般情况下,接收错误信息的用户将回滚事务并重新开始。该方法主要用在数据争夺少的环境内,以及偶尔回滚事务的成本超过读数据时锁定数据的成本的环境内,因此称该方法为乐观并发控制。

    • 时标并发控制
    时标和封锁技术之间的基本区别是封锁是使一组事务的并发执行(即交叉执行)同步,使用它等价于这些事务的某一串行操作;时标法也是使用一组事务的交叉执行同步,但是使它等价于这些事务的一个特定的串行执行,即由时标的时序所确定的一个执行。如果发生冲突,是通过撤销并重新启动一个事务解决的。事务重新启动,则赋予新的时标。

    (三) 封锁
    封锁是事项并发控制的一个非常重要的技术。所谓封锁就是事务T在对某个数据对象,例如,在标、记录等操作之前,先向系统发出请求,对其加锁。加锁后事务T就对数据库对象有了一定的控制,在事务T释放它的锁之前,其他事务不能更新此数据对象。
    1. 封锁类型
    DBMS通常提供了多种数据类型的封锁。一个事务对某个数据对象加锁后究竟拥有什么样的控制是由封锁类型决定的。基本的封锁类型有两种:排他锁(exclusive lock,简记为X锁)和共享锁(share lock简记为S锁)
    排他锁又称为写锁。若事务T对数据对象A加上X锁,则只允许T读取和修改A,其他任何事务都不能再对A加任何类型的锁,直到T释放A上的锁。这就保证了其他事务在T释放A上的锁之前不能再读取和修改A。
    共享锁又称为读锁。若事务T对数据对象A加上S锁,则其他事务只能再对A加S锁,而不能加X锁,直到T释放A上的锁。这就保证了其他事务可以读A,但在T释放A上的S锁之前不能对A做任何修改。
    排他锁与共享锁的控制方式可以用下图的相容矩阵来表示。
    在下图的封锁类型相容矩阵中,最左边一列表示事务T1已经获得的数据对象上的锁的类型,其中横线表示没有加锁。最上面一行表示另一事务T2对同一数据对象发出的封锁请求。T2的封锁请求能否被满足用Y和N表示,其中Y表示事务T2的封锁要求与T1已持有的锁相容,封锁请求可以满足。N表示T2的封锁请求与T1已持有的锁冲突,T2请求被拒绝。



    2. 封锁粒度

    X锁和S锁都是加在某一个数据对象上的。封锁的对象可以是逻辑单元,也可以是物理单元。例如,在关系数据库中,封锁对象可以是属性值、属性值集合、元组、关系、索引项、整个索引、整个数据库等逻辑单元;也可以是页(数据页或索引页)、块等物理单元。封锁对象可以很大,比如对整个数据库加锁,也可以很小,比如只对某个属性值加锁。封锁对象的大小称为封锁的粒度(granularity)。

    封锁粒度与系统的并发度和并发控制的开销密切相关。封锁的粒度越大,系统中能够被封锁的对象就越小,并发度也就越小,但同时系统开销也越小;相反,封锁的粒度越小,并发度越高,但系统开销也就越大。
    因此,如果在一个系统中同时存在不同大小的封锁单元供不同的事务选择使用是比较理想的。而选择封锁粒度时必须同时考虑封锁机构和并发度两个因素,对系统开销与并发度进行权衡,以求得最优的效果。一般说来,需要处理大量元组的用户事务可以以关系为封锁单元;需要处理多个关系的大量元组的用户事务可以以数据库为封锁单位;而对于一个处理少量元组的用户事务,可以以元组为封锁单位以提高并发度。

    3. 封锁协议

    封锁的目的是为了保证能够正确地调度并发操作。为此,在运用X锁和S锁这两种基本封锁,对一定粒度的数据对象加锁时,还需要约定一些规则,例如,应何时申请X锁或S锁、持锁时间、何时释放等。我们称这些规则为封锁协议(locking protocol)。对封锁方式规定不同的规则,就形成了各种不同的封锁协议,它们分别在不同的程度上为并发操作的正确调度提供一定的保证。本节介绍保证数据一致性的三级封锁协议和保证并行调度可串行性的两段锁协议,下一节将介绍避免死锁的封锁协议。

    (5) 保证数据一致性的封锁协议――三级封锁协议

    对并发操作的不正确调度可能会带来四种数据不一致性:丢失或覆盖更新、脏读、不可重复读和幻想读。三级封锁协议分别在不同程度上解决了这一问题。

    ① 1级封锁协议

    1级封锁协议的内容是:事务T在修改数据R之前必须先对其加X锁,直到事务结束才释放。事务结束包括正常结束(commit)和非正常结束(rollback)。

    1级封锁协议可以防止丢失或覆盖更新,并保证事务T是可以恢复的。例如,下图使用1级封锁协议解决了定飞机票例子的丢失更新问题。



    上图中,事务1在读A进行修改之前先对A加X锁,当事务2再请求对A加X锁时被拒绝,只能等事务1释放A上的锁。事务1修改值A=15写回磁盘,释放A上的X锁后,事务2获得对A的X锁,这时他读到的A已经是事务1更新过的值15,再按此新的A值进行运算,并将结果值A=14回到磁盘。这样就避免了丢失事务1的更新。

    在1级封锁协议中,如果仅仅是读数据不对其进行修改,是不需要加锁的,所以它不能保证可重复读和脏读。

    ② 2级封锁协议

    2级封锁协议的内容是:1级封锁协议加上事务T在读取数据R之前必须先对其加S锁,读完后即可释放S锁。
    2级封锁协议除防止了丢失或覆盖更新,还可进一步防止脏读。例如,下图使用2级封锁协议解决了脏读的问题。
    |
    下图中,事务1在对C进行修改之前,先对C加X锁,修改其值后写回磁盘。这时事务2请求C加上S锁,因T1已在C上加了X锁,事务2只能等待事务1释放它。之后事务1因某种原因被撤销,C恢复为原值100,并释放C上的X锁。事务2获得C上的S锁,读C=100。这就避免了事务2脏读数据。

    在2级封锁协议中,由于读完数据后即可释放S锁,所以它不能保证可重复读。



    ③ 3级封锁协议

    3级封锁协议的内容是:1级封锁协议加上事务T在读取数据之前必须先对其加S锁,直到事务结束才释放。
    3级封锁协议除防止丢失或覆盖更新和不脏读数据外,还进一步防止了不可重复读和幻想读。例如下图,使用3级封锁协议解决了不可重复读和幻像读问题。



    上图中,事务1在读A,B之前,先对A,B加S锁,这样其他事务只能再对A,B加S锁,而不能加X锁,即其他事务只能读A,B,而不能修改它们。所以当事务2为修改B而申请对B的X锁时被拒绝,使其他无法执行修改操作,只能等待事务1释放B上的锁。接着事务1为验算再读A,B,这时读出的B仍是100,求和结果仍为150,即可重复读。

    上述三级协议的主要区别在于什么操作需要申请封锁以及何时释放锁(即持锁时间)。三级封锁协议可以总结为下表。



    (6) 保证并行调度可串行性的封锁协议――两段封锁协议

    可串行性是并行调度正确性的唯一准则,两段锁(two-phase locking,简称2PL)协议是为保证并行调度可串行性而提供的封锁协议。

    两段封锁协议规定:

    ①在对任何数据进行读、写操作之前,事务首先要获得对该数据的封锁,而且②在释放一个封锁之后,事务不再获得任何其他封锁。

    所谓“两段”锁的含义是,事务分为两个阶段,第一阶段是获得封锁,也称为扩展阶段,第二阶段是释放封锁,也称为收缩阶段。

    例如,事务1的封锁序列是:

    Slock A... Slock B… Xlock C… Unlock B… Unlock A… Unlock C;

    事务2的封锁序列是:

    Slock A... Unlock A… Slock B… Xlock C… Unlock C… Unlock B;

    则事务1遵守两段封锁协议,而事务2不遵守两段封锁协议。

    可以证明,若并行执行的所有事务均遵守两段锁协议,则对这些事务的所有并行调度策略都是可串行化的。因此我们得出如下结论:所有遵守两段锁协议的事务,其并行的结果一定是正确的。

    需要说明的是,事务遵守两段锁协议是可串行化调度的充分条件,而不是必要条件。即可串行化的调度中,不一定所有事务都必须符合两段封锁协议。例如,在下图中,(a)和(b)都是可串行化的调度,但(a)遵守两段锁协议,(b)不遵守两段锁协议。



    4. 死锁和活锁

    封锁技术可以有效地解决并行操作的一致性问题,但也带来一些新的问题,即死锁和活锁的问题。

    (1) 活锁

    如果事务T1封锁了数据对象R后,事务T2也请求封锁R,于是T2等待。接着T3也请求封锁R。T1释放R上的锁后,系统首先批准了T3的请求,T2只得继续等待。接着T4也请求封锁R,T3释放R上的锁后,系统又批准了T4的请求……,T2有可能就这样永远等待下去。这就是活锁的情形,如下图所示。



    避免活锁的简单方法是采用先来先服务的策略。当多个事务请求封锁同一数据对象时,封锁子系统按请求封锁的先后次序对这些事务排队,该数据对象上的锁一旦释放,首先批准申请队列中第一个事务获得锁。

    (2) 死锁

    如果事务T1封锁了数据A,事务T2封锁了数据B。之后T1又申请封锁数据B,因T2已封锁了B,于是T1等待T2释放B上的锁。接着T2又申请封锁A,因T1已封锁了A,T2也只能等待T1释放A上的锁。这样就出现了T1在等待T2,而T2又在等待T1的局面,T1和T2两个事务永远不能结束,形成死锁。如下图所示。



    死锁问题在操作系统和一般并行处理中已做了深入研究,但数据库系统有其自己的特点,操作系统中解决死锁的方法并不一定合适数据库系统。

    目前在数据库中解决死锁问题主要有两类方法,一类方法是采取一定措施来预防死锁的发生,另一类方法是允许发生死锁,采用一定手段定期诊断系统中有无死锁,若有则解除之。

    ① 死锁的预防
    在数据库系统中,产生死锁的原因是两个或多个事务都已封锁了一些数据对象,然后又都请求对已为其他事务封锁的数据对象加锁,从而出现死锁等待。防止死锁的发生其实就是要破坏产生死锁的条件。预防死锁通常有两种方法。

    ◆ 一次封锁法
    一次封锁法要求每个事务必须一次将所有要使用的数据全部加锁,否则就不能继续执行。例如,在上图的例子中,如果事务T1将数据对象A和B一次加锁,T1就可以执行下去,而T2等待。T1执行完后释放A,B上的锁,T2继续执行。这样就不会发生死锁。

    一次封锁法虽然可以有效地防止死锁的发生,但也存在问题。第一,一次就将以后要用到的全部数据加锁,势必扩大了封锁的范围,从而降低了系统的并发度。第二,数据库中数据是不断变化的,原来不要求封锁的数据,在执行过程中可能会变成封锁对象,所以很难实现精确地确定每个事务所要封锁的数据对象,只能采取扩大封锁范围,将事务在执行过程中可能要封锁的数据对象全部加锁,这就进一步降低了并发度。

    ◆ 顺序封锁法
    顺序封锁法是预先对数据对象规定一个封锁顺序,所有事务都按这个顺序执行封锁。在上例中,我们规定封锁顺是A,B,T1和T2都按此顺序封锁,即T2也必须先封锁A。当T2请求A的封锁时,由于T1已经封锁住A,T2就只能等待。T1释放A,B上的锁后,T2继续运行。这样就不会发生死锁。

    顺序封锁法同样可以有效地防止死锁,但也同样存在问题。第一,数据库系统中可封锁的数据对象及其众多,并且随数据的插入、删除等操作而不断地变化,要维护这样极多而且变化的资源的封锁顺序非常困难,成本很高。

    第二,事务的封锁请求可以随着事务的执行而动态地决定,很难事先确定每一个事务要封锁哪些对象,因此也就很难按规定的顺序取施加封锁。例如,规定数据对象的封锁顺序为A,B,C,D,E。事务T3起初要求封锁数据对象B,C,E,但当它封锁B,C后,才发现还需要封锁A,这样就破坏了封锁顺序。

    可见,在操作系统中广为采用的预防死锁的策略并不很适合数据库的特点,因此DBMS在解决死锁的问题上更普遍采用的是诊断并解除死锁的方法。
    ② 死锁的诊断与解除
    数据库系统中诊断死锁的方法与操作系统类似,即使用一个事务等待图,它动态地反映所有事务的等待状况。并发控制子系统周期性地(比如每隔1分钟)检测事务等待图,如果发现图中存在回路,则表示系统中出现了死锁。关于诊断死锁的详细讨论请参阅操作系统的有关书籍。
    DBMS的并发控制子系统一旦检测到系统中存在死锁,就要设法解除。通常采用的方法是选择一个处理死锁代价最小的事务,将其撤销,释放此事务持有的所有的锁,使其他事务能继续运行下去。
    二、 DM的并发控制
    (一) 事务隔离级
    事务的隔离级描述了给定事务的行为对其它并发执行事务的暴露程度。 SQL-92共规定了四种隔离级别,通过选择四个隔离级中的一个,用户能增加对其它未提交事务的暴露程度,获得更高的并发度。隔离级别是一个事务必须与其它事务进行隔离的程度。
    SQL-92的四种隔离级别如下所示,DM支持所有这些隔离级别:
    (1)脏读(READ UNCOMMITTED):事务隔离的最低级别,事务可能查询到其它事务未提交的数据, 仅可保证不读取物理损坏的数据)。
    (2)读提交(READ COMMITTED):DM默认级别,保证不读脏数据。
    (3)可重复读(REPEATABLE READ):保证不可重复读,但有可能读入幻像数据。
    (4)可串行化(SERIALIZABLE):事务隔离的最高级别,事务之间完全隔离。
    DM允许用户改变未启动的事务的隔离级和读写特性 ,而且设置的选项将一直对那个连接保持有效,直到显式更改该选项为止。设置事务隔离级别虽然使程序员承担了某些完整性问题所带来的风险,但可以换取对数据更大的并发访问权。与以前的隔离级别相比,每个隔离级别都提供了更大的隔离性,但这是通过在更长的时间内占用更多限制锁换来的。DM还提供设置事务只读属性的语句,使用该语句后该事务只能做查询操作,不能更新数据库。
    需要注意的是,事务的隔离级别并不影响事务查看本身对数据的修改,也就是说,事务总可以查看自己对数据的修改。事务的隔离级别需要根据实际需要设定,较低的隔离级别可以增加并发,但代价是降低数据的正确性。相反,较高的隔离级别可以确保数据的正确性,但可能对并发产生负面影响。应用程序要求的隔离级别确定了DM使用的锁定行为。
    下表中列出四种隔离级别允许不同类型的现象



    注意:丢失或覆盖更新在所有的标准SQL隔离级中都是禁止的。

    (二) 并发处理

    1. 数据锁定机制

    DM用数据锁定机制来解决并发问题。它可以保证任何时候都可以有多个正在运行的事务,但是所有事务都在彼此完全隔离的环境中运行。

    DM的封锁对象为表和元组。封锁的实施有自动和手动两种,即隐式上锁和显式上锁。隐式封锁动作的封锁根据事务的隔离级有所不同。同时, DM提供给用户4种手动上锁语句,用以适应用户定义的应用系统。

    一般而言, DM的隐式封锁足以保证数据的一致性,但用户可以根据自己的需要改变对表的封锁。 DM提供给用户四种表锁:意向共享锁(IS:INTENSIVE SHARE)、共享锁(S:SHARE)、意向排它锁(IX:INTENSIVE EXCLUSIVE)和排它锁(X:EXCLUSIVE)。例如,在读提交隔离级下,系统缺省的表锁是 IS或IX ,在这两种表锁下,在访问元组前还需对元组进行封锁,为了提高系统的效率,用户可以手动对表进行 X封锁,这样,就不需对访问元组封锁。

    封锁机制要达到以下目的:

    (1)一致性:保证用户正在查看时,改变的数据并未从根本上发生变化。

    (2)完整性:保证数据库的基本结构以正确的顺序,准确地反映对它们的所有改变。

    一个“ 锁定” 可以认为是当某一进程需要防止其它进程做某事时获得的某种东西,当该进程不再关心此事时就 “释放 ”此锁定,通常一个锁定是加在某个 “资源 ”(某些客体,如表 )上的。

    DM的内部锁定是自动完成的。当某一进程要查看一个客体但不允许其他人修改它时,就获得一个共享方式的锁定。当某一进程要修改一客体,并且防止任何其它进程修改它时,就获得更新方式的锁定。当某一进程要修改一客体,并且防止任何其它进程修改它或以共享方式封锁它时,就获得独占方式的锁定。

    2. 锁定类型

    DM中的锁有三种,表锁、行锁和键范围锁。

    ◆ 表锁
    表锁用来封锁表对象,在对表进行检索和更新时,DM会对表对象进行封锁,但是DM为用户提供手动的表锁语句,用户可以根据自己的需要改变对表的封锁类型。表锁的模式:意向共享锁 IS,意向排它锁 IX,共享锁 S,排它锁 X,共四种,其相容矩阵可定义如下表。



    ◆ 行锁
    行锁封锁元组,在存取元组和更新元组前, DM会对元组上行锁,系统不提供手动的行封锁语句。行锁有两种模式:共享锁(S)、排它锁(X),其相容矩阵定义如下表。



    ◆ 键范围锁
    键范围锁用在可串行事务上,主要解决了幻像读并发问题。键范围锁覆盖单个记录以及记录之间的范围,可以防止对事务访问的记录集进行幻像插入或删除。键范围锁仅用于在可串行隔离级别上操作的事务。
    可串行性要求,如果任意一个查询在一个事务中后面的某一时刻再次执行,其所获取的行集应与该查询在同一事务中以前执行时所获得的行集相同。如果本查询试图提取的行不存在,则在试图访问该行的事务完成之前,其它事务不能插入该行。如果允许另一个事务插入该行,则它将以幻像出现。
    如果另一个事务试图插入驻留在锁定数据页上的行,页级锁定可以防止添加幻像行,并维护可串行性。但是,如果该行要添加到未被第一个事务锁定的数据页,应设定锁定机制防止添加该行。
    键范围锁通过覆盖索引行和索引行之间的范围来工作(而不是锁定整个基础表的行)。因为第二个事务在该范围内进行任何行插入、更新或删除操作时均需要修改索引,而键范围锁覆盖了索引项,所以在第一个事务完成之前会阻塞第二个事务的进行。
    键范围锁由系统自行执行,执行的条件是: (1) 事务隔离级为可串行化级; (2) 查询结果通过某个索引得出。
    用户上锁成功后锁将一直有效,直到当前事务结束时,该锁被系统自动解除。

    3. 锁定类型比较



    4. SQL语句锁定分析
    DM对各种 DDL和GRANT 等非DML 语句都分解为增、删、改。下表为DM对各种DML语句和查询语句的封锁策略。

    表:SQL语句封锁策略


    注:S* 表示瞬时锁,在语句结束后释放;Range表示键范围锁。

    上表只是系统在一般情况下的处理,当系统检测到有锁升级的可能,则会升级锁。一般而言,IS锁升级为 S锁,IX锁升级为 X锁,同时,不再进行行封锁。

    5. 自定义锁定提高系统效率

    DM也提供了两个函数 SET_TABLE_OPTION([db.][sch.]tablename, option, value) 、SET_INDEX_OPTION([db.]indexname, option, value)(具体语法参见《 DM_SQL语言使用手册》第 8 章)供用户自行定义锁定类型,以增强系统并发度,提高系统效率。这两个函数是为那些清楚地知道特定类型的锁适用于何种情况的专家级用户提供的。

    函数SET_TABLE_OPTION() 用于禁用指定表上的页级锁、行级锁或同时禁用二者,这一设置对该表上的所有索引都生效。函数 SET_INDEX_OPTION() 则用于禁用某一索引上的页级锁、行级锁或同时禁用二者。

    例如,当用户只需要修改索引中某定长字段时,修改操作不会造成 B 树的分裂与合并,此时就可以禁用该索引的页级锁。又如,当所有的用户都只做插入操作时,用户之间并不会对同一元组进行操作,此时就可以禁用行级锁。当用户能保证不对表进行增、删、改,而只是进行查询时,则可以同时禁用该表上的页级锁和行级锁,此时并发度最高。

    6. 死锁处理

    解决死锁问题的三种方法:预防死锁、检测死锁及避免死锁。死锁预防要求用户进程事先申报所需的资源或按严格的规程申请资源,而死锁检测原则上应允许死锁发生,在适当的时机检查,若发生死锁,则设法排除之。与预防死锁相比,后者过于放手,致使死锁频繁。而避免死锁则以事务撤消为前提,当不能获得资源批准时,立刻进行死锁检测。它既不象预防死锁那样过于保守,也不象死锁检测那样过于放开,由于检测及时,由归纳法可知,在已获准等待的事务中,不可能存在死锁,所以检测算法比较简单。

    DM4系统采用的是避免死锁方法。每当一个事务所申请占有的资源不能被立即获得时,便进行死锁检测,不存在死锁,则该事务入等待队列。否则,DM4视为产生运行时错误,将当前语句回滚。采用这种机制,从用户的角度看,DM4不存在解锁问题。

    7. 加索引和不加索引的封锁区别

    加索引和不加索引的情况下,DM的封锁机制会影响到实际的封锁范围。索引的作用就在于,可以在查询中减少对无关数据的扫描。而在一般的隔离级中,总是要对扫描到的数据进行封锁。所以,利用索引可以减少封锁的数量,冲突的可能性也会大大减少。

    展开全文
  • 并发的结构(模型)由控制结构确定 转载于:https://www.cnblogs.com/feng9exe/p/10778106.html
  • 电商实例、业务并发、网站并发及解决方法 一、怎么防止多用户同一时间抢购同一商品,防止高并发同时下单同一商品 最近在做抢购系统,但头疼的是,在多用户高并发的情况下经常会库存出现问题。排查到,在同一时间...
  • 并发抢购思路

    万次阅读 2016-11-24 08:57:06
    电商的秒杀和抢购,对我们来说,都不是一个陌生的东西。然而,从技术的角度来说,这对于Web系统是一个巨大的考验。当一个Web系统,在...在过去的工作中,我曾经面对过5w每秒的高并发秒杀功能,在这个过程中,整...
  • 多线程与高并发

    2019-05-13 18:18:57
    在过去的工作中,我曾经面对过5w每秒的高并发秒杀功能,在这个过程中,整个Web系统遇到了很多的问题和挑战。如果Web系统不做针对性的优化,会轻而易举地陷入到异常状态。我们现在一起来讨论下,优化的思路和方法哈。...
  • 1.传统的OS中应具有处理机管理、存储器管理、设备管理和文件管理等基本功能。 其中处理机管理功能:在传统的多道程序系统中,处理机的分配和运行都是...在多道程序环境下为使作业能并发执行,必须为每道作业创建一个或
  • 并发情况

    千次阅读 2017-06-28 15:54:39
    电商的秒杀和抢购,对我们来说,都不是一个陌生的东西。然而,从技术的角度来说,这对于Web系统是一个巨大的考验。当一个Web系统,在...在过去的工作中,我曾经面对过5w每秒的高并发秒杀功能,在这个过程中,整个W
  • Java高并发解决方案

    万次阅读 多人点赞 2019-03-03 22:23:50
    电商的秒杀和抢购,对我们来说,都不是一个陌生的东西。然而,从技术的角度来说,这对于Web系统是一个巨大的考验。...在过去的工作中,我曾经面对过5w每秒的高并发秒杀功能,在这个过程中,整个W...
  • 并发高流量网站架构

    千次阅读 2012-01-08 21:23:09
    以用户为导向的新网站建设概念,细分了网站功能和用户群,不仅成功的造就了一大批新生的网站,也极大的方便了上网的人们。但Web2.0以用户为导向的理念,使得新生的网站有了新的特点——高并发,高流量,数据量大,...
  • Web系统大规模并发处理

    千次阅读 2017-01-23 22:11:57
    面对5w每秒的高并发秒杀功能,如果Web系统不做针对性的优化,会轻而易举地陷入到异常状态。我们现在一起来讨论下,优化的思路和方法哈。  1. 请求接口的合理设计 一个秒杀或者抢购页面,通常分为2个部分,...
  • 2w字 + 40张图带你参透并发编程!

    千次阅读 多人点赞 2020-08-19 07:28:16
    并发历史 在计算机最早期的时候,没有操作系统,执行程序只需要一种方式,那就是从头到尾依次执行。任何资源都会为这个程序服务,在计算机使用某些资源时,其他资源就会空闲,就会存在 浪费资源 的情况。 这里说的...
  • 需要进行大量的培训考试的大集团,员工人数也会上千以前,那么这时我们要看在线考试系统的并发性,选择并发控制比较好的系统。 并发性概括 所谓并发性,是指在相同的时间间隔内,有两个或两个以上的...
  • 在过去的工作中,我曾经面对过5w每秒的高并发秒杀功能,在这个过程中,整个Web系统遇到了很多的问题和挑战。如果Web系统不做针对性的优化,会轻而易举地陷入到异常状态。我们现在一起来讨论下,优化的思路和方法哈。...
  • 多线程并发拓展 (1)死锁 public class DeadLockTest implements Runnable { private int flag ; private static Object o1=new Object(); private static Object...
  • Java 高并发解决方案(电商的秒杀和抢购)

    万次阅读 多人点赞 2018-07-25 14:08:08
    电商的秒杀和抢购,对我们来说,都不是一个陌生的东西。然而,从技术的角度来说,这对于Web系统是一个巨大的考验。当一个Web系统,在...在过去的工作中,我曾经面对过5w每秒的高并发秒杀功能,在这个过程中,整...
  • Java高并发程序设计入门

    千次阅读 2016-02-29 14:41:24
    说在前面本文绝大部分参考《JAVA高并发程序设计》,类似读书笔记和扩展。走入并行世界概念同步(synchronous)与异步(asynchronous)同步和异步通常来形容一次方法调用。同步方法调用一旦开始,调用者必须等到方法...
  • tomcat7 性能优化,提高并发

    万次阅读 多人点赞 2018-01-25 18:07:54
    通过优化tomcat提高网站的并发能力。当我们今天我们将这个优化讲完之前 优化完成后看能达到什么层次。     2. 服务器资源 服务器所能提供CPU、内存、硬盘的性能对处理能力有决定性影响。硬件我们不...
  • 并发程序设计入门

    万次阅读 多人点赞 2016-02-24 08:39:29
    说在前面本文绝大部分参考《JAVA高并发程序设计》,类似读书笔记和扩展。走入并行世界概念同步(synchronous)与异步(asynchronous)同步和异步通常来形容一次方法调用。同步方法调用一旦开始,调用者必须等到方法...
  • 电商的秒杀和抢购,对我们来说,都不是一个陌生的东西。然而,从技术的角度来说,这对于Web系统是一个巨大的考验。当一个Web系统,在一秒钟内收到数以万计甚至...在过去的工作中,我曾经面对过5w每秒的高并发秒杀功能
  • 什么是服务器的高并发

    千次阅读 2018-03-02 11:18:04
    在网上购物,秒抢某个商品,比如说小米...1、大规模并发带来的挑战比如说5w每秒的高并发秒杀功能,在这个过程中,整个Web系统遇到了很多的问题和挑战。如果Web系统不做针对性的优化,会轻而易举地陷入到异常状态。...
  • 并发高流量的大型网站架构设计

    千次阅读 2015-06-15 22:26:40
    以用户为导向的新网站建设概念,细分了网站功能和用户群,不仅成功的造就了一大批新生的网站,也极大的方便了上网的人们。但Web2.0以用户为导向的理念,使得新生的网站有了新的特点——高并发,高流量,数据量大,...
  • 电商的秒杀和抢购,对我们来说,都不是一个陌生的东西。然而,从技术的角度来说,这对于Web系统是一个巨大的考验。当一个Web系统,在...在过去的工作中,我曾经面对过5w每秒的高并发秒杀功能,在这个过程中,整...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 22,244
精华内容 8,897
关键字:

并发控制机构的功能