事务的实现(redo log ,undo log) 和binlog

事务的实现

       事务的隔离性由锁来实现,原子性,一致性,持久性通过数据库的redo log 和 undo log来完成,redo log称为 重做日志,用来保证事务的原子性和持久性。undo log用来保证事务的一致性。redo 和 undo 的作用都可以视为是一种恢复操作,redo恢复提交事务修改的页操作,而undo 回滚行记录到某个特定版本。因此两者记录的内容不同,redo通常是物理日志,记录的是页的物理修改操作,undo 是逻辑日志,根据每行记录进行记录。

    先了解几个概念: 数据库数据存放的文件为data file;日志文件称为log file;数据库数据是有缓存的,每次都写或者读物理磁盘,那性能就太低了。数据库数据的缓存称为data  buffer,日志(redo)缓存称为log buffer。

   内存缓冲池:buffer pool如果mysql不用内存缓冲池,每次读写数据时,都需要访问磁盘,必定会大大增加I/O请求,导致效率低下。所以InnoDB引擎在读写数据时,把相应的数据和索引载入到内存中的缓冲池(buffer pool)中,一定程度的提高了数据读写的速度。buffer pool:占最大块内存,用来存放各种数据的缓存包括有索引页、数据页、undo页、插入缓冲、自适应哈希索引、innodb存储的锁信息、数据字典信息等。工作方式总是将数据库文件按页(每页16k)读取到缓冲池,然后按最近最少使用(lru)的算法来保留缓冲池中的缓存数据。如果数据库文件需要修改,总是首先修改在缓存池中的页(发生修改即为脏页),然后再按照一定的频率将缓冲池的脏页刷新到文件。

表空间:表空间可以看做是InnoDB存储引擎逻辑结构的最高层。表空间文件:InnoDB默认的表空间文件为ibdata1.

   段:表空间由各个段组成,常见的段有数据段、索引段、回滚段(undo log)等。

   区:由64个连续的页组成,每个页大小为16k,即每个区大小为1M。

   页:每页16K,且不能更改。常见的页类型有:数据页、undo页、系统页、事务数据页、插入缓冲位图页、插入缓冲空闲列表页、未压缩的二进制大对象页、压缩的二进制大对象页。

1)redo 

     当有一条记录需要更新的时候,InnoDB引擎就会先把记录写到redo log里面,并更新内存,这时候更新就算完成了。同时,Innodb引擎会在适当的时候,将这个操作记录更新到磁盘里面,而这个更新往往是在系统比较空闲的时候做。Innodb的redo log是固定大小的,比如可以配置一组4个文件,每个文件的大小是1G,总共就可以记录4GB的操作。从头开始写,写到末尾就又回到开头循环写。

      有了redo log ,InnoDB就可以保证即使数据库发生异常重启,之前提交的记录都不会丢失,这个能力称为crash-safe。redo log是Innodb引擎特有的日志,而server层有自己的日志,称为bin log(归档日志)。

      *  redo log本身也是文件,记录redo log 文件的过程其实也是写磁盘,而写到redo log文件是顺序写,记录更新到磁盘里面是需要找位置,耗时。

  重做日志(redo log)用来实现事务的持久性,即事务ACID中的D。其中两部分组成:

  •     一是内存中的重做日志缓冲(redo  log buffer), 其实容易丢失的;

  •     二是重做日志文件(redo log file),其是持久的。 

    InnoDB是事务的存储引擎,其通过Force log at commit机制实现事务的持久性,即当事务提交(commit)时,必须先将该事务的所有日志写入到redo日志文件进行持久化,待事务的commit操作完成才算完成。

过程: redo log buffer--> redo log file -->记录更新到磁盘

    参数innodb_flush_log_at_trx_commit用来控制重做日志缓冲刷新到磁盘的策略。默认值为1。

     0:表示事务提交时不进行写入重做日志操作,这个操作仅在master thread 中完成,而在master thread中每1秒回进行一次重做日志的fsync操作。

     1:表示每个事务提交时进行写入到重做日志。

     2:表示事务提交时将重做日志写入重做日志文件,但仅写入文件系统的缓存中,不进行fsync操作。在这个设置下,当MySQL数据库发生宕机(就是数据库服务意外停止)而操作系统不发生宕机时,不会导致事务的丢失。而当操作系统宕机时,重启数据库后会丢失未从文件系统缓存刷新到重做日志文件那部分事务。

2)undo

undo log原理:为了满足事务的原子性,在操作任何数据之前,首先将数据备份到一个地方(这个存储数据备份的地方称为undo log)。然后进行数据的修改。如果出现了错误或者执行了rollback语句,系统可以利用undo log中的备份将数据恢复到事务开始之前的状态。

    重做日志记录了事务的行为,可以很好地通过其对页进行“重做”操作,但是事务有时还需要进行回滚操作,这时就需要undo。因为在对数据库进行修改时,InnoDB存储引擎不但会产生redo,还会产生一定量的undo。这样如果用户执行的事务或语句由于原因失败了,又或者用户用一条rollback语句进行回滚,就可以利用这些undo信息将数据回滚到修改之前的样子。

    redo存放在重做日志文件中,与redo不同,undo存放在数据库内部的一个特殊段(segment)中,这个段称为undo段。undo段位于共享表空间内。Innodb 存储引擎有 rollback segment, 每个回滚段中记录了1024个undo log segment,而在每个undo log segment中进行undo页的申请。Innodb支持最大128个(回滚段)rollback segment, 故其支持同时在线的事务 128*1024,但是这些rollback segement都存储于共享表空间中。

   undo是逻辑日志,因此只是将数据库逻辑地恢复到原来的样子。所有修改都被逻辑地取消了,但是数据结构和页本身在回滚之后可能大不相同。这时因为在多用户并发系统中,可能会有数十,数百甚至数千个并发事务。数据库的主要任务就是协调数据记录的并发访问。比如,一个事务在修改当前一个页中某几条记录,同时还有别的事务在同一个页中另几条记录进行修改。因此,不能将一个页回滚到事务开始的样子,因为这样会影响其他事务正在进行的工作。

   undo除回滚操作,undo的另一个作用是mvcc,即在InnoDB存储引擎中mvcc的实现是通过undo来完成。当用户读取一行记录时,若该记录已经被其他事务占用,当前事务可以通过undo读取之前的行版本信息,以此实现非锁定读取。最重要的一点是,undo log会产生redo log,也就是undo log的产生会伴随着redo log的产生,这时因为undo log也需要持久性的保护。最重要的一点是,undo log会产生redo log,也就是undo log的产生会伴随着redo log的产生,这时因为undo log也需要持久性的保护。undo log 和 redo log本身是分开的,innodb的undo log是记录在数据文件(ibd)中的,而且innodb将undo log的内容看作是数据,因此对undo log本身的操作(如向undo log中插入一条undo记录等),都会记录redo log。undo log可以不必立即持久化到磁盘上,即便丢失了,也可以通过redo log 将其恢复。因此插入一条记录时:1、向undo log中插入一条undo log记录。2、向redo log中插入一条“插入undo log记录”的redo log记录。 3、插入数据。4、向redo log中插入一条“insert”的redo log记录。

 undo + redo 事务的简化过程:

  假设有A、B两个数据,值分别为1,2。 进行+2的事务操作。 

  1. 事务开始。

  2. 记录A=1到undo log.

  3. 修改A=3.

  4. 记录A=3到redo log.

  5. 记录B=2到undo log。

  6. 修改B=4.

  7. 记录B=4到redo log.

  8.  将redo log写入磁盘

  9.  事务提交

之所以能同时保证原子性和持久化,是因为以下特点: 

A. 更新数据前记录Undo log。 

B. 为了保证持久性,必须将数据在事务提交前写到磁盘。只要事务成功提交,数据必然已经持久化。 

C. Undo log必须先于数据持久化到磁盘。如果在G,H之间系统崩溃,undo log是完整的,可以用来回滚事务。 

D. 如果在A-F之间系统崩溃,因为数据没有持久化到磁盘。所以磁盘上的数据还是保持在事务开始前的状态。 

缺点:每个事务提交前将数据和Undo Log写入磁盘,这样会导致大量的磁盘IO,因此性能很低。

补充 3)binlog

   binlog是归档日志,是MySQL的server层实现的,所有引擎都可以使用,binlog日志只能用于归档,是没有crash-safe能力的。binlog是逻辑日志,记录的是这个语句的原始逻辑,比如“给ID=2这一行的C字段加1”。binlog是追加写入的,binlog文件写到一定大小之后会切换到下一个,并不会覆盖以前的日志。binlog用于备份,误操作数据库可通过binlog来恢复数据。

4)执行器和Innodb引擎执行简单的update语句时的内部流程(两阶段提交)

   1、执行器先找到引擎取id=2这一行。id是主键,引擎直接用树搜索找到这一行。如果id=2这一行所在的数据页本来就在内存中,就直接返回给执行器;否则,需要先从磁盘读入内存,然后再返回;

   2、执行器拿到引擎给的行数据,把这个值加1,比如原来是N,现在是N+1,得到新的一行数据,再调用引擎接口写入这行新数据。

   3、引擎将这行新数据更新到内存中,同时将这个更新操作记录到redo log里面,此时redo log处于prepare状态。然后告知执行器执行完成了,随时可以提交事务。

   4、执行器生成这个操作的binlog,并把binlog写入磁盘。

   5、执行器调用引擎的提交事务接口,引擎把刚刚写入redo log改成提交(commit)状态,更新完成。

 注:由于redo log 和binlog是两个独立的逻辑,如果不用两阶段提交,要么就是先写完redo log再写binlog,或者采用反过来的顺序都会导致redo log和binlog数据不一致情况,而两阶段提交就是让这两个状态保持逻辑上的一致。

      *  1、prepare    2、写binlog   3、commit

      当在2之前崩溃时,重启恢复:发现没有commit,回滚。备份恢复:没有binlog.     一致

      当在3之前崩溃时,重启恢复:虽没有commit,但满足prepare和binlog完整,所以重启后会自动commit。 备份恢复:有binlog.   一致

 

你可能感兴趣的:(MySQL)