目录
关于事务概念
事务:一组逻辑操作单元,使数据从一种状态转到另一种状态。
事务处理(事务操作):一个事务中可包含一个或多个操作,一个事务中的这些操作,要么都被提交,进行持久化储存,要么全部都被回滚(撤销)到最初状态,可看作为一个整体。
例如:一个学校为各班级评红领巾班级(是否提交事务),当班级中的全部同学都佩戴了红领巾(全部通过),就可评级(事务提交),否则,如果一个班中只要有一个同学未带红领巾(一个操作失败),就取消评级资格(事务回滚)。这称为事务的一致性。
什么操作会让事务提交或回滚呢?如下:
关于数据回滚说明
什么情况下可以进行数据回滚?
1.在DDL操作(创建库,表等)后自动提交,不可回滚。
2.在DML操作(增删改记录)后自动提交,提交后不可回滚,但是,
只要我们在DML操作前设置:set autocommit = false(取消自动提交)。
就可以进行数据的回滚操作。
3.在关闭连接(会话)时,会自动提交事务。
事务案例-回滚必要性
需求说明:用户A给用户B转账,当转账过程无异常(如网络异常等),转账成功,否则,转账失败。
看如下代码:
//用户AA给用户BB转¥100
@Test
public void test() throws Exception {
String sql1 = "update user_table set balance = balance - 100 where user = ?";
upDate(sql1,"AA");
//模拟网络异常等中断的情况
System.out.println(5 / 0);
String sql2 = "update user_table set balance = balance + 100 where user = ?";
upDate(sql2,"BB");
}
不难看出,此方式存在缺陷,当执行了AA的,到中间时报错时,BB加钱未执行,程序中断,AA却扣款了。有错误,应当报错后不提交数据,将AA的扣款操作回滚。
处理缺陷方式 :关闭自动提交,当无异常后再提交数据。
@Test
public void test2() {
//在外面创建conn传入方法,就不会使之自动提交
Connection conn = null;
try {
conn = JDBCUtils.getConnection();
//关闭自动提交,当程序执行到末尾后,再提交
conn.setAutoCommit(false);
String sql1 = "update user_table set balance = balance - 100 where user = ?";
//upDate为外部封装的通用增删改方法,参数:(对象,sql语句,填充占位符)
upDate(conn,sql1,"AA");
//模拟网络异常等中断的情况
System.out.println(5 / 0);
String sql2 = "update user_table set balance = balance + 100 where user = ?";
upDate(conn,sql2,"BB");
System.out.println("转账成功");//执行到此处才提交,否则判断为中间存在异常
conn.commit();
} catch (Exception e) {
e.printStackTrace();
} finally {
//关闭资源,在封装的upDate方法中已经关闭了preparestatement
JDBCUtils.closeResouce(conn,null);
//在通用增删改时不该关闭连接资源,因为连接关闭后就会自动提交
//因此将连接放到此处关闭。
}
}
事务的ACID属性
事务属性有:原子性(Atomicity)、一致性(Consistency)、隔离性(Isolation)、持久性(Durability)。称为事务的ACID属性。
事务属性的解释
1.原子性:一个事务时不可再分的工作单元,事务内操作要么都提交,要么都回滚。
2.一致性:事务必须使数据库从一个状态变为另一个状态。
3.隔离性:一个事务的执行过程不受其他事务所影响。
4.持久性:事务一旦被提交,将持久的存储在数据库中。
值得注意的是,隔离性,我们可以设置隔离级别来解决事务并发的一些问题。
先了解事务并发时会产生的问题有哪些。
事务并发产生的问题
当存在多个事务一起执行时,这些事务同时访问一些数据(共享数据),如果没有设置必要的隔离机制,就会导致各种并发问题。如下:
1.脏读:存在两个事务T1,T2一起执行,当T1更新某数据但未提交时,T2再去访问
该数据时读取到的就是T1更新但未提交的数据,这就是脏读。
2.不可重复读:存在两个事务T1,T2一起执行,T1在第一次访问某数据时,数据为a,T2
在T1访问后,访问该数据,将a改为b后提交,当T1再次访问该数据时,发
现a变成了b,因为T1事务仍在执行期间,在此期间不可重复读到数据a,
读取到的数据是可以被改变的,这就是不可重复读的问题。
3.幻读:存在两个事务T1,T2一起执行,跟不可重复读类似,事务T1,T2一起执行,T1第一
访问共享的数据表,表中存在2条记录,T2访问该数据表,向表中新增一条记录,
此时表中就有了3条记录,T1在执行期间再次访问该数据表,发现多了一条记录。
这就是幻读问题。
为了解决事务并发时存在的这些问题,我们需要设置隔离机制,也就是设置隔离级别。
数据库的四种隔离级别
这四种隔离级别,处理的事务并发问题数量不同,处理掉的事务并发问题越多,并发性就越低,处理掉的并发问题越少,并发性越高。所以,需要根据需求设置隔离级别。
隔离级别 | 描述 |
read uncommitted(读未提交) | 允许读取其他事务未提交的数据,三种并发问题都会出现。 |
read committed(读已提交) | 只允许读取其他事务提交了的数据,解决了脏读。其他两个并发问题还会出现。 |
repeatable read(可重复度) | 在事务T1执行期间读取数据a,其他事务修改a后,T1再次读取依然可以读取到数据a,解决脏读和不可重复读。 |
serializable(串行化) | 解决了脏读,幻读,不可重复读的问题,但并发性极低。 |
设置以上隔离级别,可以处理掉对应的问题。
其实在现实生活中,不可重复度和幻读我们是还能接受的,可以认为是正常情况。
例如”不可重复读”问题,在抢票的时候,你上一秒看见还有一张票,下一秒就没了,这是不可重复度问题。
再例如”幻读”问题,某商检承诺在12点时更新库存,11:59:59秒时库存还是0,12点后刷新,就变成了100了。这是幻读问题。
隔离级别的选择和设置
由上,我们知道有四种隔离级别,那我们要如何选择设置何种隔离级别呢?
在MySQL中,支持4种隔离级别,MySQL默认的事务隔离级别为:repeatable read。
也就是解决了脏读和不可重复读的问题,存在幻读问题。
在Oracle中 ,支持2种隔离级别,分别是:read committed 和 repeatable read。
Oracle默认隔离级别为:read committed。只解决脏读。
我们在选择设置何种隔离级别时,就要根据并发性要求和项目可接受的并发问题做出选择。
并发性能和问题并发问题数量成正比。
MySQL中设置隔离级别方式
查看当前的隔离级别:
select @@transaction_isolation;
设置当前会话(连接)的隔离级别:
SET SESSION TRANSACTION ISOLATION LEVEL 隔离级别名;
设置全局的隔离级别 :
SET GLOBAL TRANSACTION ISOLATION LEVEL 隔离级别名;
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。
文章由极客之音整理,本文链接:https://www.bmabk.com/index.php/post/154561.html