一、事务
1. 事务的概念
举个例子,去超市买东西,“一手交钱,一手交货”就是一个事务的例子,交钱和交货必须全部成功,事务才算成功,任一个步骤失败,事务将撤销所有已成功的步骤(回滚)。
定义:事务可以看成一个大的任务,它由不同的小步骤组成,这些步骤要么全部成功,要么全部失败。
2. 本地事务
在计算机系统中,更多的是通过关系型数据库来控制事务,这是利用数据库本身的事务特性来实现的,因此叫数据库事务,由于主要靠关系数据库来控制事务,而数据库通常和应用在同一个服务器,所以基于关系型数据库的事务又被称为本地事务。
数据库事务的四大特性ACID:
- A(Atomic):原子性,构成事务的所有操作,要么都执行完成,要么全部不执行,不会出现部分成功部分失败的情况。
- C(Consistency):一致性,事务执行前后状态一致。例如,a账户200元,b账户0元,a向b转账100元,转账前和转账后的余额总数都为200,则为一致性。
- I(Isolation):隔离性,数据库中的事务一般都是并发的,隔离性是指并发的两个事务的执行互不干扰,一个事务不能看到其他事务运行过程的中间状态。(通过配置事务隔离级别可以避免脏读、重复读等问题。)
- D(Durability):持久性,事务完成之后,该事务对数据的更改会被持久化到数据库,写入磁盘,无中间状态,且不会被回滚。
数据库事务在实现时会将一次事务涉及的所有操作全部纳入到一个不可分割的执行单元,该执行单元中的所有操作要么都成功,要么都失败,只要其中任一操作执行失败,都将导致整个事务的回滚(到最初状态)。
- 回滚原理: 每一个步骤写入一条log,展示的是一个中间状态(并没有真正执行,只写日志)。未提交时,另一个事务看到的是未改的。当需要回滚时只要删除日志就可以。提交时才真正执行。
2.1 并发事务问题
隔离级别 | 脏读 | 不可重复读 | 幻读 |
---|---|---|---|
Read Uncommitted(读未提交) | ⎷ | ⎷ | ⎷ |
Read Committed(读已提交) | ⎷ | ⎷ | |
Repeatable Read(可重复读)【mysql默认】 | ⎷ | ||
Serializable(可串行化) |
- 脏读: 一个事务读到另一个事务还没有提交的数据
- 不可重复读: 一个事务先后读取同一条记录,但两次读取的数据不同,称之为不可重复读
- 幻读: 一个事务按照条件查询数据时,没有对应的数据行,但是在插入数据时,又发现这行数据已经存在,好像出现了“幻影”
(id为主键。A步骤1查询出不存在id为1的记录;B插入id为1的记录并提交,表中存在id为1的记录;A步骤2插入id为1的记录失败;A步骤3同步骤1语句一样,结果也一样,查询不到记录,因为不可重复读已经解决。)
2.2 隔离级别
– 查看事务隔离级别
SELECT @@TRANSACTION_ISOLATION;– 设置事务隔离级别
SET [SESSION | GLOBAL] TRANSACTION ISOLATION LEVEL {READ UNCOMMITTED | READ COMMITTED | REPEATABLE READ | SERIALIZABLE}
3. 分布式事务
随着互联网的快速发展,软件系统由原来的单体应用转变为分布式应用,下图描述了单体应用向微服务的演变:
分布式系统会把一个应用系统拆分为可独立部署的多个服务,因此需要服务与服务之间远程协作才能完成事务操作,这种分布式系统环境下由不同的服务之间通过网络远程协作完成事务称之为分布式事务,例如用户注册送积分事务、创建订单减库存事务、银行转账事务等都是分布式事务。
本地事务依赖数据库本身提供的事务特性来实现,因此以下逻辑可以控制本地事务:
begin transaction;
//1. 本地数据库操作:a减少金额
//2. 本地数据库操作:b增加金额
commit transaction;
但在分布式环境下,是这样的:
begin transaction;
//1. 本地数据库操作:a减少金额
//2. 远程调用:让b增加金额
commit transaction;
可以设想,当远程调用让b增加金额成功了,由于网络问题远程调用并没有返回,此时本地事务提交失败就回滚了a减少金额的操作,此时a和b的数据就不一致了。
因此在分布式架构的基础上,传统数据库事务就无法使用了,a和b的账户不在一个数据库中甚至不在一个应用系统里,实现转账事务需要通过远程调用,由于网络问题就会导致分布式事务问题。
3.1 分布式事务产生的场景
-
典型的场景就是微服务架构
微服务之间通过远程调用完成事务操作。
比如:订单微服务和库存微服务,下单的同时订单微服务请求库存微服务减库存。
简而言之:跨JVM进程产生分布式事务。
-
单体系统访问多个数据库实例
当单体系统需要访问多个数据库(实例)时就会产生分布式事务。
比如:用户信息和订单信息分别在两个MySQL实例存储,用户管理系统删除用户信息,需要分别删除用户信息及用户的订单信息,由于数据分布在不同的数据实例,需要通过不同的数据库链接去操作数据,此时产生分布式事务。
简而言之:跨数据库实例产生分布式事务。
- 数据库是文件的集合,是依照某种数据模型组织起来并存放于二级存储器中的数据集合。
- 数据库实例是程序,是位于用户与操作系统之间的一层数据管理软件,用户对数据库数据的任何操作,包括数据库定义、数据查询、数据维护、数据库运行控制等都是在数据库实例下进行的,应用程序只有通过数据库实例才能和数据库打交道。(实例就是内存结构和一组后台进程)
-
多服务访问同一个数据库实例
比如:订单微服务和库存微服务即使访问同一个数据库也会产生分布式事务,原因就是跨JVM进程,两个微服务持有了不同的数据库链接进行数据库操作,此时产生分布式事务。