事务
在讨论事务之前,我们要知道只有在InnoDB存储引擎下才能执行事务。
首先,让我们思考一个问题,在我们写下SQL语句,按下回车键之后,MySQL究竟是怎么来完成我们想要执行的语句呢?
通过这张图,我们可以看到最主要的步骤就是缓存机制,其中MySQL支持一种Buffer pool机制,主要的处理模式是MySQL是一种文件类型数据库即MySQL将数据都存储在文件中,对数据的任何操作都意味着要对文件操作,那必须要进行文件IO(磁盘IO),但是磁盘IO非常耗时,如果将文件中的一部分数据拿到缓存中存储,再去操作数据的时候就可以只操作缓存。
InnoDB作为MySQL的存储引擎,数据是存放在磁盘中的,但如果每次读写数据都需要磁盘IO,效率会很低。为此,InnoDB提供了缓存(Buffer Pool),Buffer Pool中包含了磁盘中部分数据页的映射,作为访问数据库的缓冲:当从数据库读取数据时,会首先从Buffer Pool中读取,如果Buffer Pool中没有,则从磁盘读取后放入Buffer Pool;当向数据库写入数据时,会首先写入Buffer Pool,Buffer Pool中修改的数据会定期刷新到磁盘中(这一过程称为刷脏)。Buffer Pool的使用大大提高了读写数据的效率,但是也带了新的问题:如果MySQL宕机,而此时Buffer Pool中修改的数据还没有刷新到磁盘,就会导致数据的丢失,事务的持久性无法保证,所以我们需要使用mysql中的日志来保证数据持久性的。
我们接着分析这张图,可以看到除了缓存外,还有很多其他比较重要的操作,比如:
- 解释器:是通过关键字将SQL语句进行解析,并生成对应的解析树。MySQL解析器将使用MySQL语法规则验证和解析查询。
- 解析树:判断SQL语法是否正确。
- 预处理器:则是根据一些MySQL规则进行进一步检查解析树是否合法,例如检查数据表和数据列是否存在,还会解析名字和别名,看看它们是否有歧义。
- 查询优化器:查询优化器会将解析树转化成执行计划。一条查询可以有多种执行方法,最后都是返回相同结果。优化器的作用就是找到这其中最好的执行计划。
在这里我们也要注意,不同的存储引擎数据存储的方式是不同,操作数据时操作数据的方式也是不同。
那么我们分析一下,如果我们在使用MySQL时,SQL语句被执行到了缓存中,但是并没有进行刷脏操作,此时因为停电导致MySQL异常中断,导致数据丢失应该怎么办?
这里我们是可以不用担心的,因为MySQL在底层已经考虑到了这种问题,在这时MySQL是会保证数据的持久性,其中最主要的机制就是数据库管理机制(DBMS),它保证了对数据库中的数据的修改是永久性的,即使数据库因为故障出错,也应该能够恢复数据!
那么具体又是怎么实现的呢?这里就引出了MySQL的持久性实现原理:即redo log 日志。
当数据修改时,除了修改Buffer Pool中的数据,还会在redo log记录这次操作;当事务提交时,会调用fsync接口对redo log进行刷盘。如果MySQL宕机,重启时可以读取redo log中的数据,对数据库进行恢复。redo log采用的是WAL(Write-ahead logging,预写式日志),所有修改先写入日志,再更新到Buffer Pool,保证了数据不会因MySQL宕机而丢失,从而满足了持久性要求。
接下来,我们分析以下生活中的实例,我们都知道MySQL的SQL语句在按下回车键之后就会执行,那么如果我们在对银行卡里面的钱数进行操作时,所执行的钱数与银行卡里的钱数不匹配,造成转账过程出错,此时对于数据库来说,应该怎么处理呢?
这里就用到了事务。那么事务又是什么呢?
事务的定义
一个事务是由一条或者多条对数据库操作的SQL语句所组成的一个不可分割的单元,只有当事务中的所有操作都正常执行完了,整个事务才会被提交给数据库;如果有部分事务处理失败,那么事务就要回退到最初的状态,因此,事务要么全部执行成功,要么全部失败。
一旦开启事务就不是按下回车键就会造成持久性修改的操作了,即MySQL将不会在第一时间自动提交SQL语句,而是要进行二次判断,如果发现SQL语句中有执行错误的SQL 语句那么可以直接调用 rollback 退回至初始状态,如果没有发现任何执行错误的SQL语句才会执行 commit 进行手动提交,这个时候才会对数据造成持久性的影响。
而此时,事务的操作利用了其本身的特性之一:原子性,它的实现原理是undo log 日志,undo log中会记录一条和你执行的sql语句意义相反的sql语句,当你rollback时就执行undo log中的sql语句。例如:
- 当执行insert语句时,undo log日志中会记录对应的delete语句。
- 当执行delete,会记录所对应的insert语句。
- 执行update 语句时,会记录相反的update语句。
当我们执行rollback时,可以读取undo log 日志然后回滚数据。当我们执行commit之后就会对undo log 日志进行数据清空,此时如果在想rollback就不行了。
实现原子性的关键,是当事务回滚时能够撤销所有已经成功执行的sql语句。InnoDB实现回滚,靠的是undo log:当事务对数据库进行修改时,InnoDB会生成对应的undo log;如果事务执行失败或调用了rollback,导致事务需要回滚,便可以利用undo log中的信息将数据回滚到修改之前的样子。undo log属于逻辑日志,它记录的是sql执行相关的