事务就是一个完整的业务逻辑
举例:
a账户转账至b账户,该操作是一个工作单元,要么同时成功要么同时失败,不可再分。
只有DML(INSERT DELETE UPDATE)语句才会有和事务有关系,因为这三个是操作数据库表中数据进行增删改的。
总结:事务就是指多个DML语句同时成功或同时失败。
- 存储引擎和事务
只有InnoDB存储引擎支持事务,并提供一组用于记录事务性活动的日志文件。
在事务的执行过程中,每一条DML操作都会记录到“事务性活动的日志文件”中。
事务特性(ACID)
-
原子性(Atomicity)
事务是最小的单元,不可再分
-
一致性(Consistency)
在同一个事务中,所有操作必须同时成功或同时失败,以保证数据的一致性
-
隔离性(Isolation)
一个事务不会影响其他事务的运行。即一个事务内部的操作及使用的数据对并发的其他事务是隔离的,并发执行的各个事务之间不能互相干扰。
-
持久性(Durability)
在事务完成以后,该事务对数据库所作的更改将持久地保存在数据库中,不会被回滚。
其中,持久性是通过事务日志来保证的。日志包括了重做日志和回滚日志。当我们通过事务对数据进行修改的时候,首先会将数据库的变化信息记录到重做日志中,然后再对数据库中对应的行进行修改。因此即使数据库系统崩溃,数据库重启后也能找到没有更新到数据库系统中的重做日志,重新执行,从而使事务具有持久性。
事务的状态
MySQL根据操作执行的不同阶段,把事务大致划分成几个状态:
- 活动的
事务对应的数据库操作正在执行过程中时,我们就说该事务处在活动的状态。
- 部分提交的
当事务中的最后一个操作执行完成,但由于操作都在内存中执行,所造成的影响并没有刷新到磁盘时,我们就说该事务处在 部分提交的 状态。
- 失败的
当事务处在 活动的 或者 部分提交的 状态时,可能遇到了某些错误(数据库自身的错误、操作系统 错误或者直接断电等)而无法继续执行,或者人为的停止当前事务的执行,我们就说该事务处在 失败的 状态。
- 中止的
如果事务执行了一部分而变为 失败的 状态,那么就需要把已经修改的事务中的操作还原到事务执 行前的状态。换句话说,就是要撤销失败事务对当前数据库造成的影响。我们把这个撤销的过程称 之为 回滚 。当 回滚 操作执行完毕时,也就是数据库恢复到了执行事务之前的状态,我们就说该事 务处在了 中止的 状态。
- 提交的
当一个处在 部分提交的 状态的事务将修改过的数据都 同步到磁盘 上之后,我们就可以说该事务处 在了 提交的 状态。
提交和回滚
-
提交事务(COMMIT):清空事务性活动的日志文件,将数据全部持久化到数据库表中
- 提交事务意味着事务的结束,并且是一种全部成功的结束。
-
回滚事务(ROLLBACK):将之前所有的DML操作全部撤销,并且清空事务性活动的日志文件
- 回滚事务标志着事务的结束,并且是一种全部失败的结束。
MySQL中默认的事务行为是自动提交的,即每执行一条DML语句,就提交一次。但是这种提交不符合开发习惯,通常一个业务需要多条DML语句共同执行才能完成的,而且必须同时成功之后才能提交,所以不能执行一条语句就提交。
开启事务
START TRANSACTION
或 BEGIN
:开启事务机制(关闭自动提交)
①READ ONLY
:标识当前事务是一个只读事务,也就是属于该事务的数据库操作只能读取数据,而不 能修改数据。
②READ WRITE
:标识当前事务是一个读写事务,也就是属于该事务的数据库操作既可以读取数据, 也可以修改数据。
③WITH CONSISTENT SNAPSHOT
:启动一致性读。
事务隔离级别
MySQL是一个 客户端/服务器 架构的软件,对于同一个服务器来说,可以有若干个客户端与之连接,每个客户端与服务器连接上之后,就可以称为一个会话( Session )。每个客户端都可以在自己的会话中向服务器发出请求语句,一个请求语句可能是某个事务的一部分,也就是对于服务器来说可能同时处理多个事务。事务有 隔离性 的特性,理论上在某个事务对某个数据进行访问时,其他事务应该进行 排队 ,当该事务提交之后,其他事务才可以继续访问这个数据。但是这样对 性能影响太大 ,我们既想保持事务的隔离性,又想让服务器在处理访问同一数据的多个事务时 性能尽量高些 ,需要权衡事务的隔离性和并发性。
数据并发问题
访问相同数据的事务在不保证串行执行(执行完一个再执行另一个)的情况下会发生哪些问题:
1. 脏写( Dirty Write )
对于两个事务 Session A、Session B,如果事务Session A 修改了 另一个 未提交 事务Session B 修改过 的数据,那就意味着发生了 脏写
2. 脏读( Dirty Read )
对于两个事务 Session A、Session B,Session A 读取 了已经被 Session B 更新 但还 没有被提交 的字段。
之后若 Session B 回滚 ,Session A 读取 的内容就是 临时且无效 的。
Session A和Session B各开启了一个事务,Session B中的事务先将studentno列为1的记录的name列更新为’张三’,然后Session A中的事务再去查询这条studentno为1的记录,如果读到列name的值为’张三’,而Session B中的事务稍后进行了回滚,那么Session A中的事务相当于读到了一个不存在的数据,这种现象就称之为 脏读 。
3. 不可重复读( Non-Repeatable Read )
对于两个事务Session A、Session B,Session A 读取 了一个字段,然后 Session B 更新 了该字段。 之后Session A 再次读取 同一个字段,值就不同 了。那就意味着发生了不可重复读。
我们在Session B中提交了几个 隐式事务 (注意是隐式事务,意味着语句结束事务就提交了),这些事务都修改了studentno列为1的记录的列name的值,每次事务提交之后,如果Session A中的事务都可以查看到最新的值,这种现象也被称之为 不可重复读 。
4. 幻读( Phantom )
对于两个事务Session A、Session B, Session A 从一个表中 读取 了一个字段, 然后 Session B 在该表中 插入 了一些新的行。 之后, 如果 Session A 再次读取 同一个表, 就会多出几行。那就意味着发生了幻读。
Session A中的事务先根据条件 studentno > 0这个条件查询表student,得到了name列值为’张三’的记录;之后Session B中提交了一个 隐式事务 ,该事务向表student中插入了一条新记录;之后Session A中的事务再根据相同的条件 studentno > 0查询表student,得到的结果集中包含Session B中的事务新插入的那条记录,这种现象也被称之为 幻读 。我们把新插入的那些记录称之为 幻影记录 。
- 按照严重性来排序:
脏写 > 脏读 > 不可重复读 > 幻读
四种隔离级别
READ UNCOMMITTED
:读未提交,在该隔离级别,所有事务都可以看到其他未提交事务的执行结果。不能避免脏读、不可重复读、幻读。READ COMMITTED
:读已提交,它满足了隔离的简单定义:一个事务只能看见已经提交事务所做的改变。这是大多数数据库系统的默认隔离级别(但不是MySQL默认的)。可以避免脏读,但不可重复读、幻读问题仍然存在。REPEATABLE READ
:可重复读,事务A在读到一条数据之后,此时事务B对该数据进行了修改并提交,那么事务A再读该数据,读到的还是原来的内容。可以避免脏读、不可重复读,但幻读问题仍然存在。这是MySQL的默认隔离级别。SERIALIZABLE
:可串行化,确保事务可以从一个表中读取相同的行。在这个事务持续期间,禁止其他事务对该表执行插入、更新和删除操作。所有的并发问题都可以避免,但性能十分低下。能避免脏读、不可重复读和幻读。
由于脏写问题过于严重,不论是哪种隔离级别,都不允许脏写的情况发生。