一、事务
1、实例介绍
user表数据
user1表数据
public static void main(String[] args) {
Connection connection = null;
PreparedStatement preparedStatement = null;
try {
Class.forName("com.mysql.cj.jdbc.Driver");
String url = "jdbc:mysql://localhost:3306/test?serverTimezone=UTC";
connection = DriverManager.getConnection(url,"root","root");
// 禁止jdbc自动提交事务
//connection.setAutoCommit(false);
preparedStatement = connection.prepareStatement("update user set money = money-? where id= ?");
preparedStatement.setInt(1,10);
preparedStatement.setInt(2,1);
preparedStatement.executeUpdate();
//抛出异常
String str = null;
if(str.equals("")){
}
preparedStatement = connection.prepareStatement("update user1 set money = money+? where id = ?");
preparedStatement.setInt(1,10);
preparedStatement.setInt(2,1);
preparedStatement.executeUpdate();
// 提交事务
//connection.commit();
} catch (Exception e) {
e.printStackTrace();
// 回滚事务
// try {
// connection.rollback();
//} catch (SQLException e1) {
// e1.printStackTrace();
//}
}finally {
try {
preparedStatement.close();
connection.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
由上述代码可知:操作一执行成功后,代码出现异常,导致操作二无法进行 但转账过程是一个事务:操作应要么全部成功,要么全部失败
因此我们应该:
1、禁止jdbc自动提交事务
connection.setAutoCommit(false);
2、在两条SQl语句执行完之后提交事务
connection.commit();
3、如果有异常则回滚事务
catch (Exception e) {
e.printStackTrace();
// 回滚事务
try {
connection.rollback();
} catch (SQLException e1) {
e1.printStackTrace();
}
关于事务提交、回滚的理解:
2、特性(ACID)
事务的原子性
<数据库事务不可分割的单位,要么都做,要么都不做>
事务是最小单元,不可再分,要么全部执行成功,要么全部失败回滚。
事务的一致性
<事务的操作不会改变数据库的状态,比方说唯一约束>
一致性是指事务必须使数据库从一个一致的状态变到另外一个一致的状态,
也就是执行事务之前和之后的状态都必须处于一致 的状态。
不一致性包含三点:脏读,不可重复读,幻读
事务的隔离性
<事务是相互不可见的>
隔离性是指当多个用户并发访问数据库时,比如操作同一张表时,数据库为每一个用户开启的事务,不能被其他事务的操作所 干扰,多个并发事务之间要相互隔离
事务的持久性
<事务一旦提交,即使宕机也是能恢复的>
DBMS(数据库管理系统)对数据的修改是永久性的。
3、分类
4、隔离级别
mysql默认的事务隔离级别为repeatable-read
未提交读
读未提交,即能够读取到没有被提交的数据
所以很明显这个级别的隔离机制无法解决脏读、不可重复读、幻读中的任何一种。
已提交读
读已提交,即能够读到那些已经提交的数据
自然能够防止脏读,但是无法限制不可重复读和幻读
可重复读
可重复读,读取了一条数据,这个事务不结束,
别的事务就不可以改这条记录,这样就解决了脏读、不可重复读的问题,
串行化
串行化,多个事务时,只有运行完一个事务之后,才能运行其他事务。
补充:
1、事务隔离级别为读提交时,写数据只会锁住相应的行
2、事务隔离级别为可重复读时,如果检索条件有索引(包括主键索引)的时候,默认加锁方式是next-key 锁;如果检索条件没有索引,更新数据时会锁住整张表。一个间隙被事务加了锁,其他事务是不能在这个间隙插入记录的,这样可以防止幻读。
3、事务隔离级别为串行化时,读写数据都会锁住整张表
4、隔离级别越高,越能保证数据的完整性和一致性,但是对并发性能的影响也越大。
5、MYSQL MVCC实现机制参考链接:blog.csdn.net/whoamiyang/…
6、关于next-key 锁可以参考链接:blog.csdn.net/bigtree_372…
5、并发问题
1、脏读:事务A读取了事务B更新的数据,然后B回滚操作,那么A读取到的数据是脏数据
2、不可重复读:事务 A 多次读取同一数据,事务 B 在事务A多次读取的过程中,对数据作了更新并提交,导致事务A多次读取同一数据时,结果 不一致。
3、幻读:系统管理员A将数据库中所有学生的成绩从具体分数改为ABCDE等级,但是系统管理员B就在这个时候插入了一条具体分数的记录,当系统管理员A改结束后发现还有一条记录没有改过来,就好像发生了幻觉一样,这就叫幻读。 小结:不可重复读的和幻读很容易混淆,不可重复读侧重于修改,幻读侧重于新增或删除。解决不可重复读的问题只需锁住满足条件的行,解决幻读需要锁表
二、锁
1、Lock
2、latch
<轻量级锁,锁的时间非常短,用来锁临界资源>