MySQL事务
事务是数据库区别于普通文件系统的重要特性之一。当写文件的时候,如果突然掉电,没有保存的数据就有可能丢失。
什么是事务
事务:Transaction 是数据库管理系统执行过程中的一个逻辑单位,不可再进行分隔。简单而言就是一组(包含多个或者一个)DML语句,要么全部执行成功,要么全部执行失败,不存在部分执行成功,部分执行失败这样的中间状态。
为什么需要事务
在我们日常的业务程序中,不可避免的会在一个会话中,执行多条DML语句,为了保证在发生异常后,数据库的数据状态还是正常的,这个就需要事务的保证。
事务的特性(ACID)
事务具备四个特性,也就是常说的ACID:
- 原子性:也就是最基本的特征,使用begin开启一个事务,使用rollback回滚事务,使用commit提交事务,在开启事务和提交 / 回滚事务之间可以写很多条sql,这些sql在逻辑上是一个整体,是不可再分的,要么全部成功,要么全部失败。最主要的就是发生了异常可以全部回滚,这个特性是依靠undo log实现的。
- 一致性:事务执行前后,数据库的数据状态从一个一致性状态转移到另一个一致性状态。
- 持久性:事务一旦提交或者回滚,事务造成的结果,最终将持持久化到数据库中。持久性也分为不同的级别,innodb存储引擎提供了多种选择策略,持久性依靠redo log实现。
- 隔离性:不同的事务执行过程中理论上是相互隔离的,这个只是一种期许,需要在隔离性和并发能力之间进行权衡。
事务的分类
- 扁平事务:最基本也是最常见的,由begin开始,由commit / rollback结束。两者之间的所有操作归于一个原子操作。
- 带有保存点的扁平事务:相比扁平事务,可以回滚到之前的某个点上,这个点称之为保存点。
- 嵌套事务:在一个事务中,又开启了另外一个事务,外层的事务称为父事务,里层的事务称为子事务,子事务提交或者回滚之后,不会立即生效,还需要父事务提交或者回滚。
- 分布式事务:分布式情况下的事务。
事务的实现
持久性的实现
持久性是依靠redo log实现的。记录物理页的变更操作,即使意外崩溃,也可以利用redo log完成恢复。只要commit的事务,即使发生了数据库崩溃,也依然可以利用redo log进行持久化。
mysql遵循日志先行,当commit的时候,此时数据还没有持久化到磁盘,先将redo log写入到重做日志文件中,为了保证os缓存中的redo log刷新到磁盘,还需要执行一次fsync操作。innodb存储引擎可以设置同步redo log到磁盘的策略,可以在持久性和效率之间进行权衡。为了保证持久性,不丢数据,最好还是在commit时就完成一次redo log的刷盘。redo log是一种物理日志,记录的是某个表空间,某个页,多少偏移量的数据发生了什么变更,这种写是一种顺序IO。在一个事务执行的过程中,会不断的写入redo log。在innodb存储引擎中,重做日志都是以512字节进行存储的,称为一个日志块,和一个扇区一样大小,因此在写入的时候可以保证原子性,不需要doublewrite技术。
如何判断是否需要崩溃恢复呢?innodb存储引擎引入LSN,代表日志序列号的缩写,重做日志有一个LSN表示一共写了多少字节的重做日志,每个页也有一个自己的LSN,当页的LSN小于重做日志的LSN的时候,就说明需要重做。
相较于binlog日志,redo log是存储引擎层的,binlog是服务层的,redo log记录的是物理日志,binlog记录的是逻辑日志,redo log会在一个事务的执行过程中不断产生,而binlog只有在事务提交的时候才会产生,另外redo log是幂等的。
原子性的实现
原子性最主要体现在发生异常后的回滚操作。原子性是依靠undo log实现的,undo log是逻辑日志,简单而言一个insert就对应一个delete,一个update对应相反的update…。另外MVCC也是依靠隔离性实现的。
事务的并发访问
MySql是一个CS架构的系统,允许多个会话同时访问,同时发起请求,因此当多个请求操作的是同一个记录的时候,就会有并发读写问题。理论上针对于这种情况(并发修改)为了保证数据的一致性,应该串行化执行,但是这样做对性能影响极大,为此mysql做了一些权衡,以尽可能的保证高性能及其数据的一致。
到底多个事务并发执行会造成什么影响呢?
-
脏读:一个事务读到另一个事务没有提交的数据。
T1读到了T2还没有提交的数据,结果T2还进行了回滚,此时T1第二次查询到的数据就是错误的,这个是比较严重的。 -
不可重复读: 同一个事务内,两次查询同一条记录结果获取到的数据不一样。这个现象便称为不可重复读。与脏读的区别是,不可重复读是读到了另一个事务已经提交的数据。
事务1在两次查询之间,事务2将该记录修改了,因此事务1两次查询的结果不一样。 -
幻读:在同一个事务中,读取到之前没有出现的记录,强调的是记录。
幻读强调的是同一个事务多次读取记录时,出现了之前没有出现过的记录,因此如果事务2删除记录导致数据记录数变少其实不是幻读。
隔离级别
为了尽可能的提升性能,舍弃了一部分隔离性,SQL标准制定了4个隔离级别,隔离级别越低,数据库的性能越高。
- 读未提交
- 读已提交
- 可重复读
- 串行化