暂无图片
暂无图片
暂无图片
暂无图片
暂无图片

MySQL事务的实现原理(上)

大数据记事本 2020-06-27
191
    上一篇《MySQL事务,不成功便成仁》中讲到了MySQL事务的四大特性:原子性、一致性、隔离性、持久性。那么MySQL的这些事务特性是如何实现的呢?
    一、持久性的实现原理——redo log

    什么是redo log?redo log称为重做日志,用于记录对数据页做了什么修改(而不是记录修改后的数据页状态),通常是物理日志,用来恢复提交后的物理数据页。

    打个比方,redo log类似饭馆赊账的小黑板,而磁盘相当于账本。当有人赊账时,可以先写到小黑板,然后等打烊了以后再更新到账本。这种小黑板和账本配合的方式就是WAL(Write-Ahead Logging),关键在于先写日志,再写磁盘,这样可以提高效率。redo log的容量是固定的,写到末尾就要回到开头循环写,假设配置了一组4个文件,每个文件大小1G,则redo log的容量为4G,如下图:

    write pos是当前日志的位置,边写边后移,写到file-3末尾就要回到file-0;check point是当前要檫除的位置,檫除前要将数据更新到磁盘。而write pos和check point之间的空间就是redo log剩余可用的空间。如果 write pos 追上 checkpoint,表示"小黑板"满了,这时候不能再执行新的更新,得停下来先擦掉一些记录,把 checkpoint 推进一下。

    有了 redo log,InnoDB 就可以保证即使数据库发生异常重启,之前提交的记录都不会丢失,这个能力称为 crash-safe

    redo log包括两部分:一是内存中的日志缓冲(redo log buffer),该部分日志是易失性的;二是磁盘上的重做日志文件(redo log file),该部分日志是持久的。

    在概念上,InnoDB通过force log at commit机制实现事务的持久性,即在事务提交的时候,必须先将该事务的所有事务日志写入到磁盘上的redo log file和undo log file中进行持久化

    为了确保每次日志都能写入到事务日志文件中,在每次将log buffer中的日志写入日志文件的过程中都会调用一次操作系统的fsync操作(即fsync()系统调用)。因为MariaDB/MySQL是工作在用户空间的,MariaDB/MySQL的log buffer处于用户空间的内存中。要写入到磁盘上的log file中(redo:ib_logfileN文件,undo:share tablespace或.ibd文件),中间还要经过操作系统内核空间的os buffer,调用fsync()的作用就是将OS buffer中的日志刷到磁盘上的log file中。

    也就是说,从redo log buffer写日志到磁盘的redo log file中,过程如下: 

    MySQL支持用户自定义在commit时如何将log buffer中的日志刷log file中。这种控制通过变量 innodb_flush_log_at_trx_commit 的值来决定。该变量有3种值:0、1、2,默认为1。但注意,这个变量只是控制commit动作是否刷写log buffer到磁盘。
  • 当设置为1的时候,事务每次提交前都会将log buffer中的日志写入os buffer并调用fsync()刷到log file on disk中。这种方式即使系统崩溃也不会丢失任何数据,但是因为每次提交都写入磁盘,IO的性能较差。(建议设置为1)
  • 当设置为0的时候,事务提交时不会将log buffer中日志写入到os buffer,而是每秒写入os buffer并调用fsync()写入到log file on disk中。也就是说设置为0时是(大约)每秒刷新写入到磁盘中的,当系统崩溃,会丢失1秒钟的数据。
  • 当设置为2的时候,每次提交都仅写入到os buffer,然后是每秒调用fsync()将os buffer中的日志写入到log file on disk。

设置为0和2的异同:

    为0时,事务提交只写log buffer,为2时,事务提交只写os buffer,1秒后都刷写磁盘;如果服务器发生宕机,两者都丢失1秒数据;而如果只是MySQL服务挂掉,为0时会丢失1秒数据,为2时由于写了os buffer,不会丢失数据。

    上面说到事务提交前会将log buffer中的日志刷写到磁盘,然后再提交。那么假设刷写磁盘后还未提交,这时服务重启了,如何判断redo log file中哪些是已提交事务写的,哪些是未提交事务写的?这时用到了事务ID,每个事务都有ID,提交后的事务ID都有标记。重启服务后检查redo log的时候,发现这个事务没有对应的标记,则为未完成事务。
二、原子性的实现原理——undo log

    什么是undo log?undo log叫做回滚日志,用于记录数据被修改前的信息。undo log主要记录的是数据的逻辑变化,为了在发生错误时回滚之前的操作,需要将之前的操作都记录下来,然后在发生错误时才可以回滚。

    假设通过SQL:update tb set name = 'lisi' where id = 1 将原始数据的name由zhangsan改为了lisi,那么undo log就会记录一条类似"将id为1的数据的name修改为zhangsan"功能的逻辑日志,这样在发生回滚操作的时候,就可以将数据的状态回滚为事务开始前的状态。
    可以简单的认为:当delete一条记录时,undo log中会记录一条对应的insert记录;当insert一条记录时,undo log会记录一条对应的delete记录;当update一条记录时,会记录一条对应相反功能的update语句。


文章转载自大数据记事本,如果涉嫌侵权,请发送邮件至:contact@modb.pro进行举报,并提供相关证据,一经查实,墨天轮将立刻删除相关内容。

评论