一、事务的四大特性

1.原子性

一个事务要么全部执行,要么全部不执行。称为事务的原子性。

2.一致性

如果事务执行之前数据库是一个完整的状态,那么事务结束后,无论事务是否执行成功,数据库仍然是一个完整的状态。
数据库的完整状态:当一个数据库中的所有的数据都符合数据库中所定义的所有约束,此时可以称数据库是一个完整的状态。

3.持久性

事务一旦提交,数据将在数据库中永久的保存下来。被称为事务的持久性。

4.隔离性

当多个用户并发访问数据库时,比如操作同一张表时,数据库为每一个用户开启的事务,不能被其他事务的操作所干扰。

二、事务中会出现的问题

1.脏读

脏读:某一事务读取了另一个事务未提交的脏数据,我们不知道该数据是否会被提交,该数据可能出现撤销和提交两种状态。因此该数据是不可靠的。
例:A事务把一条记录a字段从1修改为2,B事务查看该记录的a字段值,此时显示为2,A事务回滚事务,a字段的值变回1,此时B数据拿到的数据就是无效数据。

2.不可重复读

不可重复读:指一个事务中,第一次和第二次查询数据的间隔中,其他事务修改了要查询的数据,并提交了。导致此次事务中两次查询结果不同。
例:A事务查看一条记录的a字段值为1,B事务修改该记录a字段为2,并提交。A再查看时发现该字段值变成了2。

3.幻读

幻读:指一次事务执行的过程中,其他事务插入了新数据,导致此次事务中第一次查询的行数和第二次查询的行数不同。
例:A事务对全表a字段值从1修改为2,B事务插入一条记录,a字段值为1,并提交。A再查看时发现有一条记录未被修改。

注意

不可重复读和幻读的区别在于,幻读主要指查询的结果集的行数不符合预期。

三、当前读和快照读

MySQL做读取数据时,有两种读取方法。
第一种是当前读:读取当前数据库最新的数据。
另一种就是快照读:获取当前数据库的快照,之后的读取都在快照中进行读操作,也就是说,其他事务操作了数据库并不会反映到当前事务中。

三、事务的四种隔离级别

将所有操作串行加锁当然可以保证不会出现任何问题,比如一个事务结束前不允许其他事务进行读写。那么当然可以保证不出问题。但效率将显而易见的大幅下降。因此,mysql提供了四种隔离级别可供选择,以此提高数据库效率。

1.读未提交(Read uncommitted)

顾名思义,可以读取到其他事务未提交的内容。
每次读操作,都使用当前读,读取最新的数据库数据。因此可以读取到到其他事务修改后未提交的数据。
因为未提交的数据可能被回滚,所以可能出现脏读现象。

2. 读已提交(Read committed)

顾名思义,只能读取到其他事务已经提交的内容。
每次读操作时,都会使用最新数据库快照,只包括已经提交事务的快照。会在快照版本上做查询操作。因此避免了脏读。

因为每次都会读取当前数据库快照的数据,当该事务的两次读操作中,其他事务更新了数据库的话,就会导致前后两次读操作结果不一致,导致不可重复读现象

3.可重复读(Repeatable read)

顾名思义,一次事务中可以重复读取相同的数据,也不会导致前回查询结果不一致。
一次事务中,只在第一次读操作时获取数据库快照,只包括已经提交事务的快照。因此多次查询操作操作的是同一份快照,避免了不可重复读现象。
但可能出现幻读现象

4.串行化(Serializable)

顾名思义,开启该事务后读写操作都变成串行化,其他事务不允许读写。
该隔离等级最高,可以完全保证数据的一致性。但性能极低。

四、事务的具体操作

以mysql为例。

1.开启事务和关闭事务
//开启事务
begin;
//提交事务
commit;
2.设置和查看隔离级别
//查看隔离级别
select @@tx_isolation;
//设置隔离级别(隔离名称中空格都要变成‘-’,忽略大小写。)
set tx_isolation= '隔离级别名称';
//设置隔离级别为 可重复读。
set tx_isolation= 'repeatable-read';
注意

设置隔离级别一定要在开启事务之前。
对于使用MySQL命令窗口而言,当前窗口设置的隔离级别只对当前窗口中的事务有效。

五、如何在Spring项目中使用事务

在指定的方法上加上 @Transactional注解,即可开启事务。默认是数据库默认的隔离级别。
MySQL的默认隔离级别是可重复读
当然我们也可以使用isolation参数来修改事务的隔离级别。

Isolation.DEFAULT:使用底层数据库默认的隔离级别。

Isolation.READ_UNCOMMITTED:读未提交

Isolation.READ_COMMITTED:读已提交

Isolation.REPEATABLE_READ:可重复读

Isolation.SERIALIZABLE:串行化