事务

事务

一、什么是事务

​ 数据库的**事务(Transaction)**是一种机制、一个操作序列,包含了一组数据库操作命令。

​ 事务把所有的命令作为一个整体一起向系统提交或撤销操作请求,即这一组数据库命令要么都执行,要么都不执行,因此事务是一个不可分割的工作逻辑单元。

​ 在数据库系统上执行并发操作时,事务是作为最小的控制单元来使用的,特别适用于多用户同时操作的数据库系统。例如,航空公司的订票系统、银行、保险公司以及证券交易系统等

二、事务的四个特性

​ 事务具有 4 个特性,即原子性(Atomicity)一致性(Consistency)隔离性(Isolation)持久性(Durability),这 4 个特性通常简称为 ACID

1. 原子性

​ 事务是一个完整的操作。事务的各元素是不可分的(原子的)。事务中的所有元素必须作为一个整体提交或回滚。如果事务中的任何元素失败,则整个事务将失败。

​ 以银行转账事务为例,如果该事务提交了,则这两个账户的数据将会更新。如果由于某种原因,事务在成功更新这两个账户之前终止了,则不会更新这两个账户的余额,并且会撤销对任何账户余额的修改,事务不能部分提交。

2. 一致性

​ 当事务完成时,数据必须处于一致状态。也就是说,在事务开始之前,数据库中存储的数据处于一致状态。在正在进行的事务中. 数据可能处于不一致的状态,如数据可能有部分被修改。然而,当事务成功完成时,数据必须再次回到已知的一致状态。通过事务对数据所做的修改不能损坏数据,或者说事务不能使数据存储处于不稳定的状态。

​ 以银行转账事务事务为例。在事务开始之前,所有账户余额的总额处于一致状态。在事务进行的过程中,一个账户余额减少了,而另一个账户余额尚未修改。因此,所有账户余额的总额处于不一致状态。事务完成以后,账户余额的总额再次恢复到一致状态。

3. 隔离性

​ 对数据进行修改的所有并发事务是彼此隔离的,这表明事务必须是独立的,它不应以任何方式依赖于或影响其他事务。修改数据的事务可以在另一个使用相同数据的事务开始之前访问这些数据,或者在另一个使用相同数据的事务结束之后访问这些数据。

​ 另外,当事务修改数据时,如果任何其他进程正在同时使用相同的数据,则直到该事务成功提交之后,对数据的修改才能生效。张三和李四之间的转账与王五和赵二之间的转账,永远是相互独立的。

4. 持久性

​ 事务的持久性指不管系统是否发生了故障,事务处理的结果都是永久的。

​ 一个事务成功完成之后,它对数据库所作的改变是永久性的,即使系统出现故障也是如此。也就是说,一旦事务被提交,事务对数据所做的任何变动都会被永久地保留在数据库中。

​ 事务的 ACID 原则保证了一个事务或者成功提交,或者失败回滚,二者必居其一。因此,它对事务的修改具有可恢复性。即当事务失败时,它对数据的修改都会恢复到该事务执行前的状态。

三、执行事务的语法和流程

1. 开始事务

BEGIN;

START TRANSACTION;

这个语句显式地标记一个事务的起始点。

2. 提交事务

1)设置自动提交

​ MySQL 默认开启事务自动提交模式,即除非显式的开启事务(BEGIN 或 START TRANSACTION),否则每条 SOL 语句都会被当做一个单独的事务自动执行。但有些情况下,我们需要关闭事务自动提交来保证数据的一致性。下面主要介绍如何设置事务自动提交模式。

查看事务当前提交状态:

SHOW VARIABLES LIKE 'autocommit';

设置事务提交状态:

  • 值为 0 和值为 OFF:关闭事务自动提交。如果关闭自动提交,用户将会一直处于某个事务中,只有提交或回滚后才会结束当前事务,重新开始一个新事务。
  • 值为 1 和值为 ON:开启事务自动提交。如果开启自动提交,则每执行一条 SQL 语句,事务都会提交一次。
SET autocommit = 0|1|ON|OFF;

2)提交语句

MySQL 使用下面的语句来提交事务:

COMMIT;

​ COMMIT 表示提交事务,即提交事务的所有操作,具体地说,就是将事务中所有对数据库的更新都写到磁盘上的物理数据库中,事务正常结束。

​ 提交事务,意味着将事务开始以来所执行的所有数据都修改成为数据库的永久部分,因此也标志着一个事务的结束一旦执行了该命令,将不能回滚事务。只有在所有修改都准备好提交给数据库时,才执行这一操作。

3. 回滚(撤销)事务

MySQL 使用以下语句回滚事务:

ROLLBACK;

​ ROLLBACK 表示撤销事务,即在事务运行的过程中发生了某种故障,事务不能继续执行,系统将事务中对数据库的所有已完成的操作全部撤销,回滚到事务开始时的状态。这里的操作指对数据库的更新操作。

​ 当事务执行过程中遇到错误时,使用 ROLLBACK 语句使事务回滚到起点或指定的保持点处。同时,系统将清除自事务起点或到某个保存点所做的所有的数据修改,并且释放由事务控制的资源。因此,这条语句也标志着事务的结束。

总结

​ BEGIN 或 START TRANSACTION 语句后面的 SQL 语句对数据库数据的更新操作都将记录在事务日志中,直至遇到 ROLLBACK 语句或 COMMIT 语句。

​ 如果事务中某一操作失败且执行了 ROLLBACK 语句,那么在开启事务语句之后所有更新的数据都能回滚到事务开始前的状态。如果事务中的所有操作都全部正确完成,并且使用了 COMMIT 语句向数据库提交更新数据,则此时的数据又处在新的一致状态。

四、JDBC

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.SQLException;

/**
 * 事务JDBC
 * @author Administrator
 *
 */

public class Test {
	public static void main(String[] args) throws SQLException {
		moveMoney();
	}
	//得到连接
	static Connection connection=null;
    //用static代码块创建connection保证事务中的连接都是同一个
	static {
		try {
			Class.forName("com.mysql.jdbc.Driver");
			connection=DriverManager.getConnection("jdbc:mysql://localhost:3306/xsh8?characterEncoding=UTF-8","root","root");
		} catch (ClassNotFoundException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (SQLException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		
	}
	//创建更新方法
	public static void moneyUpdate(int number,String name) throws SQLException {
		String sql="update bankusers set money=? where name=?";
		PreparedStatement statement=connection.prepareStatement(sql);
		statement.setInt(1, number);
		statement.setString(2, name);
		statement.executeUpdate();
	}
	//模拟转账创建事务
	public static void moveMoney() throws SQLException {
		//设置不自动提交事务
		connection.setAutoCommit(false);
		//更改数据库数据
		try {
			moneyUpdate(555,"常宇豪");
			//创造错误
			int a=5/0;
			//更改数据库数据2
			moneyUpdate(666,"李连杰");
			//提交事务
			connection.commit();
		} catch (Exception e) {
			//出错回滚
			System.out.println("出错");
			connection.rollback();
		}	
	}
}

五、隔离级别

​ 为了保证并发时操作数据的正确性,事务有隔离特性,由此有了隔离级别的概念。

​ MySQL 事务隔离级别可能产生的问题如下表所示:

隔离级别脏读不可重复读幻读
READ UNCOMMITTED
READ COMMITTED×
REPEATABLE READ××
SERIALIZABLE×××

1. 四大隔离级别

1)串行化(SERIALIZABLE)

​ 如果一个事务先根据某些条件查询出一些记录,之后另一个事务又向表中插入了符合这些条件的记录,原先的事务再次按照该条件查询时,能把另一个事务插入的记录也读出来。那么这种隔离级别就称之为串行化。

​ SERIALIZABLE 是最高的事务隔离级别,主要通过强制事务排序来解决幻读问题。简单来说,就是在每个读取的数据行上加上共享锁实现,这样就避免了脏读、不可重复读和幻读等问题。但是该事务隔离级别执行效率低下,且性能开销也最大,所以一般情况下不推荐使用。

2)可重复读(REPEATABLE READ,RR)(mysql默认)

​ 顾名思义,可重复读是专门针对不可重复读这种情况而制定的隔离级别,可以有效的避免不可重复读。

​ 在一些场景中,一个事务只能读取到另一个已提交事务修改过的数据,但是第一次读过某条记录后,即使其它事务修改了该记录的值并且提交,之后该事务再读该条记录时,读到的仍是第一次读到的值,而不是每次都读到不同的数据。那么这种隔离级别就称之为可重复读。

​ 可重复读是 MySQL 的默认事务隔离级别,它能确保同一事务的多个实例在并发读取数据时,会看到同样的数据行。在该隔离级别下,如果有事务正在读取数据,就不允许有其它事务进行修改操作,这样就解决了可重复读问题。

3)读提交(READ COMMITTED,RC)(oracle默认)

​ 顾名思义,读提交就是只能读到已经提交了的内容。

​ 如果一个事务只能读取到另一个已提交事务修改过的数据,并且其它事务每对该数据进行一次修改并提交后,该事务都能查询得到最新值,那么这种隔离级别就称之为读提交。

​ 该隔离级别满足了隔离的简单定义:一个事务从开始到提交前所做的任何改变都是不可见的,事务只能读取到已经提交的事务所做的改变。

​ 这是大多数数据库系统的默认事务隔离级别(例如 Oracle、SQL Server),但不是 MySQL 默认的。

4)读未提交(READ UNCOMMITTED,RU)

​ 顾名思义,读未提交就是可以读到未提交的内容。

​ 如果一个事务读取到了另一个未提交事务修改过的数据,那么这种隔离级别就称之为读未提交。

​ 在该隔离级别下,所有事务都可以看到其它未提交事务的执行结果。因为它的性能与其他隔离级别相比没有高多少,所以一般情况下,该隔离级别在实际应用中很少使用。

2. 隔离级别产生的问题

1) 脏读

​ 脏读是指一个事务正在访问数据,并且对数据进行了修改,但是这种修改还没有提交到数据库中,这时,另外一个事务也访问这个数据,然后使用了这个数据。

2) 不可重复读

​ 不可重复读是指在一个事务内,多次读取同一个数据。

​ 在这个事务还没有结束时,另外一个事务也访问了该同一数据。那么,在第一个事务中的两次读数据之间,由于第二个事务的修改,那么第一个事务两次读到的的数据可能是不一样的。这样在一个事务内两次读到的数据是不一样的,因此称为是不可重复读。

3) 幻读

​ 幻读是指当事务不是独立执行时发生的一种现象,例如第一个事务对一个表中的数据进行了修改,这种修改涉及到表中的全部数据行。同时,第二个事务也修改这个表中的数据,这种修改是向表中插入一行新数据。那么,以后就会发生操作第一个事务的用户发现表中还有没有修改的数据行,就好象发生了幻觉一样。

3. 查看和设置隔离级别

1)查看隔离级别语句

select @@tx_isolation;

show variables like'%tx_isolation';

2)设置隔离级别语句

set [session|global] transaction isolation level 
[read uncommitted|read committed|repeatable read |serializable];
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值