Oracle中提供了多种数据完整性的保证机制,如约束、触发器、事务和锁管理等。事务管理主要是为了保证一批相关数据库中数据的操作能全部被完成,从而保证数据的完整性。锁机制主要是执行对多个活动事务的并发控制。它可以控制多个用户对同一数据进行的操作,使用锁机制,可以解决数据库的并发问题。
1.事务管理
事务是Oracle中的基本工作单元,它是用户定义的一个数据库操作序列,这些操作要么全做,要么全不做,是一个不可分割的工作单位。
1.1什么是事务
事务是一组包含一条或多条语句的逻辑单元,在事务中的语句被作为一个整体,要么被一起提交,作用在数据库上,使数据库数据被修改;要么被一起撤销,对数据库不做任何的修改。
例如,常见的银行账号之间转账的例子,主要操作分3步完成:第一步在源账号中减少转账金额,例如减少1万;第二步在目标账号中增加转账金额,增加1万;第三步在事务日志中记录该事务。
在上面的3步操作中,如果有一步失败,整个事务都会回滚,所有的操作都将撤销,目标账号和源账号上的金额都不会发生变化。
1.2事务的属性
事务是作为单个逻辑工作单元执行的一系列操作。一个逻辑工作单元必须有四个属性,称为原子性(Atomic)、一致性(Consistent)、隔离性(Isolated)和持久性(Durable)属性,简称ACID属性,只有这样才能成为一个事务。
原子性:事务必须是原子工作单元;对于其数据修改,要么全都执行,要么全都不执行。
一致性:事务在完成时,必须使所有的数据都保持一致状态。在相关数据库中,所有规则都必须应用于事务的修改,以保持所有数据的完整性。事务结束时,所有的内部数据结构都必须是正确的。
隔离性:由并发事务所做的修改必须与任何其他并发事务所做的修改隔离。事务识别数据时数据所处的状态,要么是另一并发事务修改它之前的状态,要么是第二个事务修改它之后的状态,事务不会识别中间状态的数据。这称为可串行性,因为它能够重新装载起始数据,并且重播一系列事务,以使数据结束时的状态与原始事务执行的状态相同。
持久性:事务完成之后,它对于系统的影响是永久性的。该修改即使出现系统故障也将一直保持。
1.3事务管理的常用语句
Oracle中常用的事务管理语句包含如下几条:
SET TRANSACTION——设置事务的属性。
COMMIT——提交事务。
SAVEPIONT—设置事务点。
ROLLBACK——事务失败时执行回滚操作。
ROLLBACKTO SAVEPIONT--回滚到保存点。
一个事务中可以包含一条语句或者多条语句甚至一段程序,一段程序中也可以包含多个事务。事务可以根据需求把一段事务分成多个组,每个组可以理解为一个事务。
1.4事务的类型
事务的类型分为两种,包括显式事务和隐式事务。
显式事务
显式事务是通过命令完成的,具体语法规则如下:
COMMIT表示提交事务;
ROLLBACK表示事务回滚。
Oracle事务不需要设置开始标记。通常有下列情况之一时,事务会开启:
登录数据库后,第一次执行DML语句。
当事务结束后,第一次执行DML语句。2. 隐式事务隐式事务没有非常明确的开始和结束点,Oracle中的每一条数据操作语句,例如SELECT、INSERT、UPDATE和DELETE都是隐式事务的一部分,即使只有一条语句,系统也会把这条语句当作一个事务,要么执行所有语句,要么什么都不执行。
默认情况下,隐式事务AUTOCOMMIT(自动提交)为打开状态,可以控制提交的状态:
当有以下情况出现时,事务会结束:
执行DDL语句,事务自动提交。比如,使用CREATE、GRANT和DROP等命令。
使用COMMIT提交事务,使用ROLLBACK回滚事务。
正常退出SQL Plus是自动提交事务,非正常退出则ROLLBACK事务回滚。
1.5事务的应用实例
事务的主要作用是保证数据的一致性。在事务没有提交前,当前会话所做的操作其他会话不会看到。下面通过案例来理解事务的特性。
可以创建一个数据表pt并插入数据,命令如下:
登录SQL Plus,定义窗口为SQL Plus1。执行更新操作:
执行成功后,查询表pt的内容是否变化,重新打开一个SQL Plus2进行查询,结果如下:
在SQLPlusl窗口中提交事务,命令如下:
再次在窗口 SQL Plus2中查询表pt的内容,结果如下:
1.6事务的保存点
在事务中可以根据实际的工作需要设置保存点,保存点可以设置在任何位置,当然也可以设置多个保存点,这样就可以把一个长的事务根据需要划分为多个小的段,这样操作的好处是当对数据的操作出现问题时不需要全部回滚,只需要回滚到保存点即可。
事务可以回滚保存点以后的操作,但是保存点会被保留,保存点以前的操作不会回滚。
向数据表pt中插入数据,此时隐式事务已经自动打开,命令如下:
创建保存点,名称为BST,命令如下:
继续向数据表pt中插入数据,命令如下:
此时查看pt表中的记录,结果如下:
回滚到保存点BST,命令如下:
此时查看pt表中的记录,结果如下:
从结果可以看出,保存点以后的操作被回滚,保存点以前的操作被保留。
事务开始之后,事务中所有的操作都会写到事务日志中。写到日志中的事务,一般有两种:一种是针对数据的操作,例如插入、修改和删除,这些操作的对象是大量的数据;另一种是针对任务的操作,例如创建索引。当取消这些事务操作时,系统自动执行这种操作的反操作,保证系统的一致性。系统自动生成一个检查点机制,这个检查点周期地检查事务日志,如果在事务日志中,事务全部完成,那么检查点将事务日志中的事务提交到数据库中,并且在事务日志中做一个检查点提交标识;如果在事务日志中,事务没有完成,那么检查点不将事务日志中的事务提交到数据库中,并且在事务日志中做一个检查点未提交的标识。
2.锁
Oracle支持多用户共享同一数据库,但是,当多个用户对同一个数据库进行修改时,会产生并发问题。使用锁可以解决用户存取数据的这个问题,从而保证数据库的完整性和一致性。
2.1什么是锁
在多个会话同时操作一个表时,需要防止事务之间的破坏性交互,此时就需要对优先操作的会话进行锁定。可见,锁是在共享资源中控制访问的一种机制。
从事务的分离性可以看出,当前事务不能影响其他的事务,所以当多个会话访问相同的资源时,数据库会利用锁确保它们像队列一样依次进行。Oracle处理数据时用到的锁是自动获取的,但是Oracle 也允许用户手动锁定数据。对于一般的用户,通过系统的自动锁管理机制基本可以满足使用要求,但如果对数据安全、数据库完整性和一致性有特殊要求,则需要亲自控制数据库的锁和解锁。
如果不使用锁机制,对数据的并发操作会带来下面一些问题:脏读、幻读、非重复性读取、丢失更新。
脏读
当一个事务读取的记录是另一个事务的一部分时,如果第一个事务正常完成,就没有什么问题,如果此时另一个事务未完成,就产生了脏读。例如,员工表中编号为1001的员工工资为1740,如果事务1将工资修改为1900,但还没有提交确认;此时事务2读取员工的工资为1900;事务1中的操作因为某种原因执行了ROLLBACK回滚,取消了对员工工资的修改,但事务2已经把编号为1001的员工的数据读走了。此时就发生了脏读。如果此时用了行级锁,第一个事务修改记录时封锁改行,那么第二个事务只能等待,这样就避免了脏数据的产生,从而保证了数据的完整性。
幻读
当某一数据行执行INSERT或DELETE操作,而该数据行恰好属于某个事务正在读取的范围时,就会发生幻读现象。例如,现在要对员工涨工资,将所有低于1700的工资都涨到新的1900,事务1使用UPDATE语句进行更新操作,事务2同时读取这一批数据,但是在其中插入了几条工资小于1900的记录,此时事务1如果查看数据表中的数据,会发现自己UPDATE之后还有工资小于1900的记录!幻读事件是在某个凑巧的环境下发生的,简而言之,它是在运行UPDATE语句的同时执行了INSERT操作。因为插入了一个新记录行,所以没有被锁定,并且能正常运行。
非重复性读取
如果一个事务不止一次地读取相同的记录,但在两次读取中间有另一个事务刚好修改了数据,则两次读取的数据将出现差异,此时就发生了非重复读取。例如,事务1和事务2都读取一条工资为2310的数据行,如果事务1将记录中的工资修改为2500并提交,而事务2使用的员工的工资仍为2310。
丢失更新
一个事务更新了数据库之后,另一个事务再次对数据库更新,此时系统只能保留最后一个数据的修改。
例如对一个员工表进行修改,事务1将员工表中编号为1001的员工工资修改为1900,而之后事务2又把该员工的工资更改为3000,那么最后员工的工资为3000,导致事务1的修改丢失。
使用锁将可以实现并发控制,能够保证多个用户同时操作同一数据库中的数据而不发生上述数据不一致的现象。
2.2锁的分类
Oracle中提供了两种锁模式,包括排他锁和共享锁。
排他锁:用于数据修改操作,例如INSERT、UPDATE或DELETE,以确保不会同时对同一资源进行多重更新。
共享锁:用于读取数据操作,允许多个事务读取相同的数据,但不允许其他事务修改当前数据,如SELECT语句。当多个事务读取一个资源时,资源上存在共享锁,任何其他事务都不能修改数据,除非将事务隔离级别设置为可重复读或者更高的级别,或者在事务生存周期内用锁定提示对共享锁进行保留,那么一旦数据完成读取,资源上的共享锁立即得以释放。
2.3锁的类型
Oracle按对象的不同,使用不同类型的锁来管理并发会话对数据对象的操作,从而使数据库实现高度的并发访问。
按照操作对象,锁分为以下几种类型:
DML锁:该类型的锁主要是为了保护数据,也称为数据锁。
DDL锁:用于保护模式中对象的结构。
内部闩锁:完全自动调用,主要保护数据库的内部结构。
DML锁主要保证了并发访问数据的完整性,可以分为行级锁和表级锁。
行级锁:属于排他锁,也被称为事务锁。当修改表的记录时,需要对将要修改的记录添加行级锁,防止两个事务同时修改相同的记录,事务结束后,该锁也会释放。
表级锁:主要作用是防止在修改表的数据时,表的结构发生变化。
在Oracle中除了执行DML时自动为表添加锁外,用户还可以手动添加锁。添加锁的语法规则如下:
如果要释放锁,只需要使用ROLLBACK命令即可。
2.4锁等待和死锁
由排他锁机制可知,当一个会话正在修改表的某个记录时,会对该记录进行加锁,如果此时另外一个会话也来修改此记录,会因为等不到排他锁而一直等待,数据库长时间没有响应,直到第一个会话把事务提交,释放排他锁后,第二个会话才能对数据进行修改操作。
在两个或多个任务中,如果每个任务锁定了其他任务试图锁定的资源,此时会造成这些任务永久阻塞,从而出现死锁。此时系统处于死锁状态。
死锁的原因
在多用户环境下,死锁的发生是由于两个事务都锁定了不同的资源的同时又都在申请对方锁定的资源,即一组进程中的各个进程均占有不会释放的资源,但因互相申请其他进程占用的不会释放的资源而处于一种永久等待的状态。形成死锁有4个必要条件:
请求与保持条件——获取资源的进程可以同时申请新的资源。
非剥夺条件——已经分配的资源不能从该进程中剥夺。
循环等待条件——多个进程构成环路,并且其中每个进程都在等待相邻进程正占用的资源。
互斥条件资源只能被一个进程使用。
可能会造成死锁的资源
每个用户会话可能有一个或多个代表它运行的任务,其中每个任务可能获取或等待获取各种资源。以下类型的资源可能会造成阻塞,并最终导致死锁:
锁。等待获取资源(如对象、页、行、元数据和应用程序)的锁可能导致死锁。
工作线程。排队等待可用工作线程的任务可能导致死锁。如果排队等待的任务拥有阻塞所有工作线程的资源,则将导致死锁。
内存。当并发请求等待获得内存,而当前的可用内存无法满足其需要时,可能发生死锁。
并行查询执行的相关资源。通常与交换端口关联的处理协调器、发生器或使用者线程至少包含一个不属于并行查询的进程时,可能会相互阻塞,从而导致死锁。此外,当并行查询启动执行时,Oracle将根据当前的工作负荷确定并行度或工作线程数。如果系统工作负荷发生意外更改,例如,当新查询开始在服务器中运行或系统用完工作线程时,则可能发生死锁。
减少死锁的策略
复杂的系统中不可能百分之百避免死锁,从实际出发,为了减少死锁,可以采用以下策略:
在所有事务中以相同的次序使用资源。
使事务尽可能简短并且在一个批处理中。
为死锁超时参数设置一个合理范围,如3~30分钟。超时,则自动放弃本次操作,避免进程挂起。
避免在事务内和用户进行交互,减少资源的锁定时间。
事务和锁应用上的区别
事务将一段语句作为一个单元来处理,这些操作要么全部成功,要么全部失败。事务包含四个特性:原子性、一致性、隔离性和持久性。事务的方式分为显式事务和隐式事务。事务以“COMMIT”或“ROLLBACK”语句结束。锁是另一个和事务紧密联系的概念,对于多用户系统,使用锁来保护指定的资源。在事务中使用锁,防止其他用户修改另外一个还没有完成的事务中的数据。SQL Server中有多种类型的锁,允许事务锁定不同的资源。
领取专属 10元无门槛券
私享最新 技术干货