1定义编辑
2问题的提出编辑
更新丢失
脏读
不可重复读
3解决方案编辑
未受权读取
受权读取
可重复读取(Repeatable Read)
序列化(Serializable)
ORACLE数据库事务隔离级别介绍
两个并发事务同时访问数据库表相同的行时,可能存在如下三个问题:
一、幻想读:事务T1读取一条指定where条件的语句,返回结果集。此时事务T2插入一行新记录,刚好知足T1的where条件。而后T1使用相同的条件再次查询,结果集中能够看到T2插入的记录,这条新纪录就是幻想。
二、不可重复读取:事务T1读取一行记录,紧接着事务T2修改了T1刚刚读取的记录,而后T1再次查询,发现与第一次读取的记录不一样,这称为不可重复读。
三、脏读:事务T1更新了一行记录,还未提交所作的修改,这个T2读取了更新后的数据,而后T1执行回滚操做,取消刚才的修改,因此T2所读取的行就无效,也就是脏数据。
1、为了处理这些问题,SQL标准定义了如下几种事务隔离级别:
READ UNCOMMITTED 幻想读、不可重复读和脏读都容许。一个会话能够读取其余事务未提交的更新结果,若是这个事务最后以回滚结束,这时的读取结果就多是不正确的,因此多数的数据库都不会运用这种隔离级别。
READ COMMITTED 容许幻想读、不可重复读,不容许脏读。一个会话只能读取其余事务已提交的更新结果,不然,发生等待,可是其余会话能够修改这个事务中被读取的记录,而没必要等待事务结束,显然,在这种隔离级别下,一个事务中的两个相同的读取操做,其结果可能不一样。
REPEATABLE READ 容许幻想读,不容许不可重复读和脏读。在一个事务中,若是在两次相同条件的读取操做之间没有添加记录的操做,也没有其余更新操做致使在这个查询条件下记录数增多,则两次读取结果相同。换句话说,就是在一个事务中第一次读取的记录保证不会在这个事务期间发生改动。SQL Server是经过在整个事务期间给读取的记录加锁实现这种隔离级别的,这样,在这个事务结束前,其余会话不能修改事务中读取的记录,而只能等待事务结束,可是SQL Server不会阻碍其余会话向表中添加记录,也不阻碍其余会话修改其余记录。
SERIALIZABLE 幻想读、不可重复读和脏读都不容许。在一个事务中,读取操做的结果是在这个事务开始以前其余事务就已经提交的记录,SQL Server经过在整个事务期间给表加锁实现这种隔离级别。在这种隔离级别下,对这个表的全部DML操做都是不容许的,即要等待事务结束,这样就保证了在一个事务中的两次读取操做的结果确定是相同的。SQL标准所定义的默认事务隔离级别是SERIALIZABLE。
2、Oracle中的隔离级别及实现机制:
Oracle数据库支持READ COMMITTED 和 SERIALIZABLE这两种事务隔离级别。因此Oracle不支持脏读,即Oracle中不容许一个会话读取其余事务未提交的数据修改结果,从而防止了因为事务回滚发生的读取不正确。
Oracle回滚段,在修改数据记录时,会把这些记录被修改以前的结果存入回滚段或撤销段中。Oracle读取操做不会阻碍更新操做,更新操做也不会阻碍读取操做,这样在Oracle中的各类隔离级别下,读取操做都不会等待更新事务结束,更新操做也不会由于另外一个事务中的读取操做而发生等待,这也是Oracle事务处理的一个优点所在。
Oracle缺省的配置是Read Committed隔离级别(也称为语句级别的隔离),在这种隔离级别下,若是一个事务正在对某个表执行 DML操做,而这时另一个会话对这个表的记录执行读取操做,则Oracle会去读取回滚段或撤销段中存放的更新以前的记录,而不会象SQL Server同样等待更新事务的结束。
Oracle的Serializable隔离级别(也称为事务级别的隔离),事务中的读取操做只能读取这个事务开始以前已经提交的数据结果。若是在读取时,其余事务正在对记录执行修改,则Oracle就会在回滚段或撤销段中去寻找对应的原来未经修改的记录(并且是在读取操做所在的事务开始以前存放于回滚段或撤销段的记录),这时读取操做也不会由于相应记录被更新而等待。
设置隔离级别使用 SET TRANSACTION ISOLATION LEVEL [READ UNCOMMITTED|READ COMMITTED|REPEATABLE READ|SERIALIZABLE]
下面是oracle 设置SERIALIZABLE隔离级别一个示例:
左面是事务T1,右面是事务T2,由于T2级别为SERIALIZABLE,因此即便事务T1在提交了数据以后,事务T2仍是看不到T1提交的数据,幻想读和不可重复读都不容许了。
那如何能查看到T1新增的记录呢? 上面T1和T2是并发执行,在T1执行insert的时候事务T2已经开始了,由于T2级别是SERIALIZABLE,因此T2所查询的数据集是T2事务开始前数据库的数据。即事务T1在事务T2开始以后的insert和update操做的影响都不会影响事务T2。如今从新开启一个事务T3 就能够看到T1新增的记录了。
当下列事件发生时,事务就开始了:
一、链接到数据库,并执行第一条DML语句
二、前一个事务结束后,又输入了另外一条DML语句
两个并发事务同时访问数据库表相同的行时,可能存在如下三个问题:
一、幻想读:事务T1读取一条指定where条件的语句,返回结果集。此时事务T2插入一行新记录,刚好知足T1的where条件。而后T1使用相同的条件再次查询,结果集中能够看到T2插入的记录,这条新纪录就是幻想。
二、不可重复读取:事务T1读取一行记录,紧接着事务T2修改了T1刚刚读取的记录,而后T1再次查询,发现与第一次读取的记录不一样,这称为不可重复读。
三、脏读:事务T1更新了一行记录,还未提交所作的修改,这个T2读取了更新后的数据,而后T1执行回滚操做,取消刚才的修改,因此T2所读取的行就无效,也就是脏数据。
1、为了处理这些问题,SQL标准定义了如下几种事务隔离级别:
READ UNCOMMITTED 幻想读、不可重复读和脏读都容许。一个会话能够读取其余事务未提交的更新结果,若是这个事务最后以回滚结束,这时的读取结果就多是不正确的,因此多数的数据库都不会运用这种隔离级别。
READ COMMITTED 容许幻想读、不可重复读,不容许脏读。一个会话只能读取其余事务已提交的更新结果,不然,发生等待,可是其余会话能够修改这个事务中被读取的记录,而没必要等待事务结束,显然,在这种隔离级别下,一个事务中的两个相同的读取操做,其结果可能不一样。
REPEATABLE READ 容许幻想读,不容许不可重复读和脏读。在一个事务中,若是在两次相同条件的读取操做之间没有添加记录的操做,也没有其余更新操做致使在这个查询条件下记录数增多,则两次读取结果相同。换句话说,就是在一个事务中第一次读取的记录保证不会在这个事务期间发生改动。SQL Server是经过在整个事务期间给读取的记录加锁实现这种隔离级别的,这样,在这个事务结束前,其余会话不能修改事务中读取的记录,而只能等待事务结束,可是SQL Server不会阻碍其余会话向表中添加记录,也不阻碍其余会话修改其余记录。
SERIALIZABLE 幻想读、不可重复读和脏读都不容许。在一个事务中,读取操做的结果是在这个事务开始以前其余事务就已经提交的记录,SQL Server经过在整个事务期间给表加锁实现这种隔离级别。在这种隔离级别下,对这个表的全部DML操做都是不容许的,即要等待事务结束,这样就保证了在一个事务中的两次读取操做的结果确定是相同的。SQL标准所定义的默认事务隔离级别是SERIALIZABLE。
2、Oracle中的隔离级别及实现机制:
Oracle数据库支持READ COMMITTED 和 SERIALIZABLE这两种事务隔离级别。因此Oracle不支持脏读,即Oracle中不容许一个会话读取其余事务未提交的数据修改结果,从而防止了因为事务回滚发生的读取不正确。
Oracle回滚段,在修改数据记录时,会把这些记录被修改以前的结果存入回滚段或撤销段中。Oracle读取操做不会阻碍更新操做,更新操做也不会阻碍读取操做,这样在Oracle中的各类隔离级别下,读取操做都不会等待更新事务结束,更新操做也不会由于另外一个事务中的读取操做而发生等待,这也是Oracle事务处理的一个优点所在。
Oracle缺省的配置是Read Committed隔离级别(也称为语句级别的隔离),在这种隔离级别下,若是一个事务正在对某个表执行 DML操做,而这时另一个会话对这个表的记录执行读取操做,则Oracle会去读取回滚段或撤销段中存放的更新以前的记录,而不会象SQL Server同样等待更新事务的结束。
Oracle的Serializable隔离级别(也称为事务级别的隔离),事务中的读取操做只能读取这个事务开始以前已经提交的数据结果。若是在读取时,其余事务正在对记录执行修改,则Oracle就会在回滚段或撤销段中去寻找对应的原来未经修改的记录(并且是在读取操做所在的事务开始以前存放于回滚段或撤销段的记录),这时读取操做也不会由于相应记录被更新而等待。
设置隔离级别使用 SET TRANSACTION ISOLATION LEVEL [READ UNCOMMITTED|READ COMMITTED|REPEATABLE READ|SERIALIZABLE]
下面是oracle 设置SERIALIZABLE隔离级别一个示例:
左面是事务T1,右面是事务T2,由于T2级别为SERIALIZABLE,因此即便事务T1在提交了数据以后,事务T2仍是看不到T1提交的数据,幻想读和不可重复读都不容许了。
那如何能查看到T1新增的记录呢? 上面T1和T2是并发执行,在T1执行insert的时候事务T2已经开始了,由于T2级别是SERIALIZABLE,因此T2所查询的数据集是T2事务开始前数据库的数据。即事务T1在事务T2开始以后的insert和update操做的影响都不会影响事务T2。如今从新开启一个事务T3 就能够看到T1新增的记录了。
当下列事件发生时,事务就开始了:
一、链接到数据库,并执行第一条DML语句
二、前一个事务结束后,又输入了另外一条DML语句
http://blog.csdn.Net/jialinqiang/article/details/8723044
1.数据库事务的概念:
•事务是指一组相互依赖的操做行为,如银行交易、股票交易或网上购物。事务的成功取决于这些相互依赖的操做行为是否都能执行成功,只要有一个操做行为失败,就意味着整个事务失败。例如,Tom到银行办理转帐事务,把100元钱转到Jack的帐号上,这个事务包含如下操做行为:
–(1)从Tom的帐户上减去100元。
–(2)往Jack的帐户上增长100元。
•显然,以上两个操做必须做为一个不可分割的工做单元。假如仅仅第一步操做执行成功,使得Tom的帐户上扣除了100元,可是第二步操做执行失败,Jack的帐户上没有增长100元,那么整个事务失败。
•数据库事务是对现实生活中事务的模拟,它由一组在业务逻辑上相互依赖的SQL语句组成。
2.数据库事务的生命周期:
3.声明事务的边界:
•事务的开始边界。
•事务的正常结束边界(COMMIT):提交事务,永久保存被事务更新后的数据库状态。
•事务的异常结束边界(ROLLBACK):撤销事务,使数据库退回到执行事务前的初始状态。
(1).在mysql.exe中声明事务:
•每启动一个mysql.exe程序,就会获得一个单独的数据库链接。每一个数据库链接都有个全局变量@@autocommit,表示当前的事务模式,它有两个可选值:
–0:表示手工提交模式。
–1:默认值,表示自动提交模式。
•若是要察看当前的事务模式,可以使用以下SQL命令:
–mysql> select @@autocommit
•若是要把当前的事务模式改成手工提交模式,可以使用以下SQL命令:
–mysql> set autocommit=0;
——在自动提交模式下提交事务:
•在自动提交模式下,每一个SQL语句都是一个独立的事务。若是在一个mysql.exe程序中执行SQL语句:
–mysql>insert into ACCOUNTS values(1,'Tom',1000);
•MySQL会自动提交这个事务,这意味着向ACCOUNTS表中新插入的记录会永久保存在数据库中。此时在另外一个mysql.exe程序中执行SQL语句:
–mysql>select * from ACCOUNTS;
•这条select语句会查询到ID为1的ACCOUNTS记录。这代表在第一个mysql.exe程序中插入的ACCOUNTS记录被永久保存,这体现了事务的ACID特性中的持久性。
——在手工模式下提交事务:
•在手工提交模式下,必须显式指定事务开始边界和结束边界:
–事务的开始边界:begin
–提交事务:commit
–撤销事务:rollback
例:
–mysql>begin;
–mysql>select * from ACCOUNTS;
–mysql>commit;
(2).经过JDBC API声明事务边界:
• Connection提供了如下用于控制事务的方法:
–setAutoCommit(boolean autoCommit):设置是否自动提交事务
–commit():提交事务
–rollback():撤销事务
例:
try {
con = Java.sql.DriverManager.getConnection(dbUrl,dbUser,dbPwd);
//设置手工提交事务模式
con.setAutoCommit(false);
stmt = con.createStatement();
//数据库更新操做1
stmt.executeUpdate("update ACCOUNTS set BALANCE=900 where ID=1 ");
//数据库更新操做2
stmt.executeUpdate("update ACCOUNTS set BALANCE=1000 where ID=2 ");
con.commit(); //提交事务
}catch(Exception e) {
try{
con.rollback(); //操做不成功则撤销事务
}catch(Exception ex){
//处理异常
……
}
//处理异常
……
}finally{…}
(3).经过hibernate API声明事务边界:
•声明事务的开始边界:Transaction tx=session.beginTransaction();
•提交事务: tx.commit();
•撤销事务: tx.rollback();
4.多个事务并发时的并发问题:
•第一类丢失更新:撤销一个事务时,把其余事务已提交的更新数据覆盖。
•脏读:一个事务读到另外一事务未提交的更新数据。
•虚读:一个事务读到另外一事务已提交的新插入的数据。
•不可重复读:一个事务读到另外一事务已提交的更新数据。
•第二类丢失更新:这是不可重复读中的特例,一个事务覆盖另外一事务已提交的更新数据。
以取款事务和支票转帐事务例:
•取款事务包含如下步骤:
–(1)某银行客户在银行前台请求取款100元,出纳员先查询帐户信息,得知存款余额为1000元。
–(2)出纳员判断出存款额超过了取款额,就支付给客户100元,并将帐户上的存款余额改成900元。
•支票转帐事务包含如下步骤:
–(1)某出纳员处理一转账支票,该支票向一账户汇入100元。出纳员先查询帐户信息,得知存款余额为900元。
–(2)出纳员将存款余额改成1000元。
并发运行的两个事务致使脏读:
取款事务在T5时刻把存款余额改成900元,支票转帐事务在T6时刻查询帐户的存款余额为900元,取款事务在T7时刻被撤销,支票转帐事务在T8时刻把存款余额改成1000元。
因为支票转帐事务查询到了取款事务未提交的更新数据,而且在这个查询结果的基础上进行更新操做,若是取款事务最后被撤销,会致使银行客户损失100元。
并发运行的两个事务致使第二类更新丢失:
取款事务在T5时刻根据在T3时刻的查询结果,把存款余额改成1000-100元,在T6时刻提交事务。支票转帐事务在T7时刻根据在T4时刻的查询结果,把存款余额改成1000+100
元。因为支票转帐事务覆盖了取款事务对存款余额所作的更新,致使银行最后损失100元。
5.数据库的隔离级别:
(1).隔离级别与并发性能的关系:
(2).设置隔离级别的原则:
•隔离级别越高,越能保证数据的完整性和一致性,可是对并发性能的影响也越大。
•对于多数应用程序,能够优先考虑把数据库系统的隔离级别设为Read Committed,它可以避免脏读,并且具备较好的并发性能。尽管它会致使不可重复读、虚读
和第二类丢失更新这些并发问题,在可能出现这类问题的个别场合,能够由应用程序采用悲观锁或乐观锁来控制。
(3)在mysql.exe程序中中设置隔离级别:
•每启动一个mysql.exe程序,就会得到一个单独的数据库链接。每一个数据库链接都有个全局变量@@tx_isolation,表示当前的事务隔离级别。MySQL默认的隔离
级别为Repeatable Read。若是要察看当前的隔离级别,可以使用以下SQL命令:
–mysql> select @@tx_isolation;
•若是要把当前mysql.exe程序的隔离级别改成Read Committed,可以使用以下SQL命令:
–mysql> set transaction isolation level read committed;
(4)在Hibernate中设置隔离级别:
•在Hibernate的配置文件中能够显式的设置隔离级别。每一种隔离级别都对应一个整数:
–1:Read Uncommitted
–2:Read Committed
–4:Repeatable Read
–8:Serializable•例如,如下代码把hibernate.cfg.xml文件中的隔离级别设为Read Committed:
hibernate.connection.isolation=2
对于从数据库链接池中得到的每一个链接,Hibernate都会把它改成使用Read Committed隔离级别。