一、什么是事务
概念: 数据库事务( transaction)是访问并可能操作各种数据项的一个数据库操作序列,这些操作要么全部执行,要么全部不执行,是一个不可分割的工作单位。事务由事务开始与事务结束之间执行的全部数据库操作组成。
以生活中转账为例: A向B转账100元,这个过程包含两个sql语句,sql1:A的账户减100,sql2:B的账户加100。整个过程应该是一个整体,要么都成功,要么都失败。
总结如下:
- 在 MySQL 中只有使用了 Innodb 数据库引擎的数据库或表才支持事务。
- 事务处理可以用来维护数据库的完整性,保证成批的 SQL 语句要么全部执行,要么全部不执行。
- 事务用来管理 insert,update,delete 语句
二、事务的特性(ACID)
事务是必须满足4个条件(ACID):原子性(Atomicity)、一致性(Consistency)、隔离性(Isolation)、持久性(Durability)。
-
原子性:一个事务(transaction)中的所有操作,要么全部完成,要么全部不完成,不会结束在中间某个环节。事务在执行过程中发生错误,会被回滚(Rollback)到事务开始前的状态,就像这个事务从来没有执行过一样。 -
一致性:在事务开始之前和事务结束以后,数据库的完整性没有被破坏。这表示写入的资料必须完全符合所有的预设规则,这包含资料的精确度、串联性以及后续数据库可以自发性地完成预定的工作。 -
隔离性:数据库允许多个并发事务同时对其数据进行读写和修改的能力,隔离性可以防止多个事务并发执行时由于交叉执行而导致数据的不一致。事务隔离分为不同级别,包括读未提交(Read uncommitted)、读提交(read committed)、可重复读(repeatable read)和串行化(Serializable)。 -
持久性:事务处理结束后,对数据的修改就是永久生效的,即:数据被持久化。
注意: 在 MySQL 命令行的默认设置下,事务都是自动提交的,即执行 SQL 语句后就会马上执行 COMMIT 操作(提交)。因此要显式地开启一个事务务须使用命令 BEGIN 或 START TRANSACTION,或者执行命令 SET AUTOCOMMIT=0,用来禁止使用当前会话的事务自动提交。
三、操作事务
3.1 命令行下操作事务
1.设置为手动提交事务(mysql命令下默认自动提交事务)
set autocommit=0 禁止自动提交
set autocommit=1 开启自动提交
2.开启事务: begin 或者 start transaction
3.提交事务: commit
4.回滚事务: rollback
3.2 JDBC中操作事务
JDBC中的事务是自动提交的。
设置事务:connection.setAutoCommit(boolean autoCommit)
提交事务:connection.commit();
回滚事务:connection.rollback();
示例代码如下:
public class MyTest01 {
public static void main(String[] args) {
Connection connection = null;
PreparedStatement ps = null;
try {
Class.forName("com.mysql.cj.jdbc.Driver");
connection = DriverManager.getConnection("jdbc:mysql://localhost:3306/库名", "用户名", "密码");
connection.setAutoCommit(false);
ps = connection.prepareStatement("update user set age=20 where id=?");
ps.setLong(1,2);
ps.executeUpdate();
connection.commit();
} catch (Exception e) {
e.printStackTrace();
if(connection!=null){
try {
connection.rollback();
} catch (SQLException e1) {
e1.printStackTrace();
}
}
} finally{
if(ps!=null){
try {
ps.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
if(connection!=null){
try {
connection.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
}
}
四、事务隔离级别
4.1 并发下的事务问题
在单个客户端连接下,是不会出现事务并发问题的,但是如果多个连接并发操作同一个表中的数据,就会出现一些问题,例如:脏读,不可重复读,幻读。
4.1.1 更新丢失(Lost Update)
当两个事务更新同一行数据时,双方都不知道对方的存在,就有可能覆盖对方的修改,造成数据的丢失。
4.1.2 脏读(Dirty Reads)
事务B对数据进行了修改,但是并没有提交事务,此时事务A进行读取,读到的就是事务B修改的数据,而事务B又将事务回滚了,那么A读到的这个数据其实是不存在的,就称之为“脏数据”。
4.1.3 不可重复读(Non-Repeatable Reads)
事务A 多次读取同一数据,事务B在事务A多次读取的过程中,对数据作了更新并提交,导致事务A多次读取同一数据时,结果不一致,这种现象就叫 不可重复读。(重点在于读到了修改的数据)
4.1.4 幻读(Phantom Read)
幻读和不可重复读类似。幻读是指事务A读取了几行数据,事务A还没结束,接着另外事务B插入了一些数据,在随后的查询中,事务A读取到的数据就会比原本读取到的多,就好像发生了幻觉一样,所以称为幻读。(重点在于读到了新增的数据)
4.2 SQL中的事务隔离级别
4.2.1 事务并发问题严重性排序
为了解决事务在并发下的问题,专门提供了事务隔离级别来进行处理,在这之前,我们先来了解一下并发问题的严重性排序:
更新丢失 > 脏读 > 不可重复读 > 幻读
4.2.2 事务隔离级别
为了解决以上问题,mysql专门提供了“事务隔离级别”来处理。事务隔离级别一共4个。分别为:读取未提交(READ-UNCOMMITTED),读取已提交(READ-COMMITTED),可重复读(REPEATABLE-READ),可串行化(SERIALIZABLE)。 注意:这4种隔离级别都能解决更新丢失(Lost Update)的问题,因为我们认为更新丢失是不可被接受的。
4.2.2.1 读取未提交(READ-UNCOMMITTED)
该隔离级别为最低的隔离级别,所有事务都可以看到其他未提交事务的执行结果,不能避免脏读,不可重复读,幻读。(几乎不会使用)
4.2.2.2 读取已提交(READ-COMMITTED)
一个事务只能读取到已经提交事务的结果。能够避免脏读,但是不能避免不可重复读和幻读。
4.2.2.3 可重复读(REPEATABLE-READ)
一个事务读取一条数据之后,此时另一个事务对数据进行了修改并提交,那么第一个事务再读取还是原来的内容。可以避免脏读,不可重复读,但是幻读仍然存在。(mysql的默认隔离级别) 注意:
select * from user;
select * from user for update;
4.2.2.4 可串行化(SERIALIZABLE)
确保当前事务从一个表中读取相同的行,在这个事务保持期间,禁止其他事务对记录进行新增,修改,删除等操作,所有的事务依次执行,完全符合ACID的特性,可以解决:脏读,不可重复读和幻读。但是效率较低。
最终隔离级别对于事务并发问题的处理情况总结如下:
|