mysql中事务

371 阅读5分钟

数据库

数据库和文件系统都有持久化的功能,但是拥有事务的数据库相对文件系统来说具有几个优势:

  1. 事务使数据库中的修改要么全部成功,要么全部失败。(原子性)
  2. 数据只会从一个状态到另一个一致的状态。(一致性)
  3. 每个读写事务的对象对其他事务的操作对象能够分离,该事务提交之前对其他事务都不可见,通常用锁实现(隔离性)
  4. 事务一旦提交,结果就是持久的,不会丢失(持久性)

redo 和 undo

redo log用来保证事务的原子性和持久性,undo log用来保证事务的一致性。redo 和 undo都提供了一种数据恢复的功能。redo的目的是让事务的修改恢复(比如事务提交了,但是数据没有及时修改,可以根据redo log来再次修改数据,即为恢复到事务提交后的状态),undo log用来将一个行回滚到某个特定的版本。redo通常是物理日志,记录页的物理操作,undo是逻辑日志,根据没有记录进行记录。

redo

事务提交以后,怎么才能让改的数据持久化,永不丢失呢?我们知道,mysql修改数据之前首先得从磁盘中将页加载进入内存,然后对内存中数据进行修改,再在适当的时机将页写到磁盘中。如果我们在事务提交以后,就将改动后的数据写入磁盘,貌似就能保住数据不丢失了。这样考虑是可以的,但是效率会很低,因为数据的改动会带来磁盘的随机读写,速度非常低。那么我们只能暂时将数据保留在内存中,但是这时候数据库宕机,改动的数据丢了怎么办呢?我们可以用redo来记录到底修改了什么内容,把这个日志写到磁盘不就好了。这个redo log文件属于顺序读写,速度比上面的随机读写快多了。 redo log就是帮助我们用持久化落地的方式记录了数据的改动过,因此我们要保证这个日志是更新到磁盘的(一开始是写入内存中)。innodb引擎里面给了用户一些选项,通过调整innodb_flush_log_at_trx_commit这个参数,

  1. 设置为1,表示每次事务提交,都必须调用fsync操作将日志落地到磁盘
  2. 设置为0,表示事务提交的时候不写入重做日志(仅仅写入缓存中)
  3. 设置为2,表示事务提交的时候补调用fsync,仅仅写入操作系统的文件缓存

redo日志的小细节

  1. redo log按照512个字节,分为一个个log block。如果一个页中产生的重做日志大于512字节,就要分割为多个重做日志进行存储
  2. 重做日志块有一些块头和块尾共20字节
  3. 重做日志文件中存储log block,log buffer根据一定规则将log block刷新到磁盘。比如事务提交,log buffer被使用超过一般空间,log checkpoint时

LSN

LSN是日志的序列号,存在多个地方,也就拥有了多重含义

  1. 由事务刚刚新生成出来的日志的LSN
  2. LSN记录重做日志的总量(字节数),这个表示已经存在于磁盘中的日志的数目
  3. 存在于每个页的头部,表示该页最后刷新时LSN的大小。通过这个能判断页是否需要进行恢复操作。如果页的LSN低于日志的LSN,并且事务已经提交了,就有必要进行恢复。
  4. Pages flushed up to:当前最旧的脏页数据对应的LSN,写Checkpoint的时候直接将此LSN写入到日志文件;
  5. checkpoint lsn: 表示这个LSN是一个checkpoint,在它之前的LSN对应的数据已全部刷新到磁盘里了。恢复数据文件的时候,Innodb扫描日志文件,当发现LSN小于Checkpoint对应的LSN,就认为恢复已经完成。

从上面的含义可以看出,一条重做日志会经历几个阶段

  1. 从事务中创建出来
  2. 刷新到磁盘
  3. 这个日志对应的脏页被刷新到磁盘
  4. 这个日志被作为checkpoint

恢复

Innodb在启动 时候就会尝试恢复。恢复的时候因为是物理日志,所以速度比逻辑日志快很多,比如binlog。另外由于checkpoint机制的存在,也只需要恢复checkpoint开始的日志部分。checkpoint被记录在重做日志里某个固定的地方。 重做日志是幂等的,因为是物理日志,binlog不是幂等的。

undo log

undolog不同于redolog,undo存放在数据库内部一个特殊段(segment)中。undo并不是将数据库物理的恢复到原来的样子,因为事务可能是并发的,只是简单恢复到原来样子,会影响其他事务的工作,所以undo只是逻辑上将先前的操作取消掉,数据结构和页可能已经发生很大的变化了。除了回滚,undo另一个作用是MVCC。当用户读取一行记录时,如果该记录已经被其他事务占用,当前事务可以通过undo读取之前的行版本信息,以此实现非锁定读取。最后,undolog的修改需要redo做持久性的保护。

undolog共分两类

  1. insert undo log:insert过程中产生的undo。因为事务是隔离的,所以要回滚的话直接删除即可
  2. update undo log: delete和update操作产生的undo。因为该undo可能需要提供mvcc机制,因此在事务提交的时候也不能删除。在提交的Hi好放入undolog链表中,等待purge线程删除。

需要注意的是,当删除数据的时候,只是将对应记录的delete flag设置为1,其实并没有删除,真正删除的操作由purge线程完成。这样设计由于innnodb支持mvcc,因此在事务提交的时候不能立即处理,其他事务可能正引用这一行。