概述
在通常的情况下,系统中总是有若干个事务再并发执行,他们可以共享的存取数据库中的数据。但是有的时候,不同的事务再访问数据库的时候可能会出现抢占资源的情况,从而造成死锁,死锁对于系统来说是一个非常影响性能的事情。就好像很多车辆来到了十字路口,但是他们如果互不相让就会造成拥堵。所以我们很有必要了解事务以及锁的原理,在充分提高数据库并发能力的同时,尽量的去避免和解决死锁的问题。
目标
了解事务以及锁的概念
了解死锁产生的原因
掌握自动提交、事务回滚的方法
会设置事务的隔离级别
oracle事务的工作原理
oracle是基于网络环境下的,多个用户可以同时来访问oracle数据库,当一个用户需要修改数据库的某个资源的时候,为了防止其他用户访问这个数据库资源,就需要锁定这个资源,修改完成之后再解除锁定。
事务的概念以及特性
事务就是一组数据库操作的逻辑单元,作用是用来保证数据库的一致性以及可恢复性,在事务中包含的数据库操作是一个不可分割的整体,要么一起被执行,要么回滚到事务之前的状态。
在执行事务的时候,并不是每执行其中的一个数据库操作就立马将结果写入数据库,而是完成所有的操作之后,执行提交命令commit或者是窒息感回滚命令 rollback来讲数据库恢复到执行事务之前的状态。
事务有自动性、一致性、独立性、持久性。自动性:事务再开始执行的时候,事务中所有的数据操作都会被写入到事务日志中。事务日志包括针对数据的读写操作(insert、update、delete),以及针对任务的操作(创建表、创建视图或者索引),取消事务的时候,oracle会自动执行该事务在事务日志中的操作记录的反操作。一致性:当事务执行完成的时候,数据中所有数据的状态应该是一致的,所有的规则都必须应用到事务的修改上,以便维护数据的完整性。独立性:主要体现在“锁”的概念上,当两个事务同时操作某张表的时候,事务B只能看到事务A修改之前或者提交之后的表的数据,不能看到该修改过程中的数据。持久性:当一个事务在完成并且提交之后,那么已经写入的数据就会永久的保存到数据库当中,这个就体现了数据的持久性。
SQL语句与事务之间的联系
SQL语句可以分为DDL、DCL、DML三种类型。
DML(Data Manipulation language)包含常见的insert、update、delete、merge等操作。
DDL(Data Definition Language)包含定义数据库和管理数据库中所有对象的语句,例如create、alter、drop等等。
DCL(Data Control Language)包含授予或者收回访问数据库的某种特权、控制数据库操纵事务发生的时间以及效果、对DB进行监视等。
一个事务开始于一条可以执行的SQL语句,结束于以下六种情况:
1、用户提交事务。2、用户回滚事务。3、遇到DDL语句。4、遇到DCL语句。5、用户结束会话。6、数据库关闭或者系统崩溃。
在下面的情况下,系统会自动的提交事务:
1、执行DDL语句。2、执行DCL语句。3、正常结束会话
在下面的情况下,系统会自动的回滚事务:
1、异常结束会话。2、数据库自动关闭或者系统崩溃。
事务并发可能造成的问题
更新丢失:
两个用户同时对某个表进行操作,用户A更新了表里的某条数据,在提交之前,用户B修改了表里的数据。从而用户A的数据会被用户B的数据覆盖掉,从而导致了用户A的数据更新丢失。
不可重复读:
一个用户在事务的过程中多次读取或者修改某一个表的数据,但是在读取的过程当中,会有另外一个事务修改了该表的数据,从而造成了该用户在两次前后读取的数据不一致。
脏读:
一个事务正在读取另外一个事务正在更新尚未提交的数据,所读取的过程中的数据是没有意义的。
幻读:
用户读取了某个结果集之后,其他的用户对该结果集中的数据进行了增加或者减少,从而造成了第一个用户两次读取的数据前后不一致。
oracle的锁定机制
锁定机制具有以下的特点:
1、锁定机制可以确保在事务处理期间数据和对象保持一致性。
2、数据库管理系统可以自动实现锁定机制。
3、通常事务的开始和结束时间决定了锁的锁定时间和释放的时间。
4、操作类型不同,锁的粒度以及强度也不相同。
锁的基本分类:
1、排它锁(X锁)可以防止并发事务对数据资源的写操作,在执行insert、update、delete的时候使用X锁。通常一般会先申请S锁。 2、共享锁(S锁)用于并发执行对一个数据源的读取操作,在执行select语句之前会使用共享锁模式,目的是为了防止在读取的过程当中有其他的事务来修改该表的数据。
3、DDL锁,当用户使用DDL语句的时候,oracle会自动对DDL涉及的语句进行加锁。 4、DML锁,当用户执行DML语句更新数据的时候,ORACLE会自动对设计的对象加DML锁。DML锁能够使多个事务并发访问数据的时候保持数据的一致性和完整性。
锁定协议的等级: 1、一级锁定协议。如果事务要对数据库对象进行修改,则对数据库对象加X锁,直到事务结束才能解除锁定;如果事务是读取数据,则不需要对数据库对象加锁。使用此锁定协议可以解决更新丢失的问题。 2、二级锁定协议。如果事务要对数据库对象进行修改,则对数据库对象加X锁,直到事务结束才能解除锁定;如果事务要对数据库进行读取,则对数据库对象加S锁,直到读取结束之后才能解除锁定,这样可以解决脏读的问题。 3、三级锁定协议。如果事务要对数据库对象进行修改,则对数据库对象加X锁,直到事务结束才能解除锁定;如果事务要对数据库对象进行读取,则对数据库对象加S锁,直到事务结束之后才能解除锁定,这样可以解决不可重复读的问题。
锁的兼容度和粒度:
共享锁与共享锁兼容、共享锁与排它锁不兼容。排它锁与其他的所有的锁都不兼容,所以称为排它锁。 锁粒度指的是Oracle锁定数据库资源的类型,锁定的粒度越小,那么数据库的并发性就越高,因为锁定较小的数据库资源,不会影响其他用户访问表中的所有的数据,如果只锁定表中的一条记录,其他用户还可以访问表中未被锁定的记录。锁定粒度越小,数据库系统管理所的开销就越大,因为一个事务可能需要在一个表中建立多个锁。
从*粒度的角度来看,ORACLE的DML锁可以分为行级锁和表级锁两个层次。
1、行级锁(事务锁,TX锁)
当一个事务第一次执行数据更改,例如INSERT,UPDATE,DELETE语句或者使用SELECT..FOR UPDATE语句执行查询操作的时候,该事务就会获得一个TX锁,直到事务结束的时候才释放该锁。行级锁并不是只针对一行记录,事务中涉及的被更新的所有的行都拥有行级锁。在表的每一行数据上面,都有一个标志位表明该行是否被锁定。Oracle数据行上没有S锁。
2、表级锁(TM锁)
对一个表申请表级锁的时候,首先要检查申请该锁与原有的表级锁是否兼容,然后还要检查它与表中的每一行上的锁是否兼容。
3、意向锁
意向锁表示oracle需要在行上获取共享锁或者排它锁。意向锁可以提高性能。包括意向共享锁IS,意向排它锁IX,共享意向排它锁SIX。 在实际的应用中,并不需要数据库用户考虑使用某种粒度的锁。oracle数据库采用动态锁策略,在执行检查的时候,数据库引擎会根据架构和查询语句的特点来自动选择最合适的锁粒度。
事务的隔离级别
隔离级别:定义了事务与事务之间的隔离程度,隔离级别和事务的并发性是矛盾的,隔离程度越高,数据的并发性越差;隔离程度越低数据的并发性越好。
ORACLE提供的事务隔离级别有READ COMMITTED,SERIALIZABLE,READ-ONLY等。
1、READ COMMITTED: 指定语句不能读取其他事务正在修改但尚未提交的行,这个可以避免脏读的情况发生,但是可能出现非重复读和幻象。这个是ORACLE默认的级别。
2、SERIALIZABLE: 提供*别的事务隔离。当一个事务处于SERIALIZABLE级别的时候,一个查询只能看到事务开始之前提交的数据,而无法看到脏读或者事务执行过程中其他的事务锁修改的数据。在此级别下,事务是串行执行的,而不是并发执行多个事务。
3、READ-ONLY: 遵从事务级的读一致性,在事务中,只能看到事务之前由其他事务提交的更改。在此级别下,只允许事务中执行查询操作,不允许执行DML操作。 限制级别越大,数据库的并发性就越差。在限制级别最大的SERIALIZABLE模式下,查询语句与插入/更新语句没有办法并行执行,如果有一个语句长时间查询一个表中的数据,则其他的用户都无法向该表中插入和更新数据。
死锁
客观的说,只要大型数据库应用系统中存在并发机制,那么死锁问题就是不可能避免的。但是按照一定的程序设计规范来设计数据库应用系统,完全可以尽量减少死锁情况的发生,从而提高数据库系统的性能。
1、尽量不要在一个事务中实现过于复杂的查询或者更新操作。为了维护事务内部数据的一致性,需要在严格的事务隔离级别和并发性方面进行选择。
2、尽量不要在事务中要求用户响应。
3、死锁是由于并发数据库资源造成的,减少死锁就应该限制应用系统的并发访问量。
4、尽可能的以分区表或者分区视图的方式拆分包含大量数据的表,将其在物理上保存在不同的磁盘和文件当中。
5、尽量避免使用占用时间很长的复杂查询,在条件允许的情况下应该尽量使用分页查询或者缩小结果集的方式。
6、尽可能使用较低的隔离级别。
7、应该注意统一访问表的顺序。尽量避免有的事务先访问A表,然后更新B表,有的事务先访问B表,然后更新A表的情况。