MySQL的日志体系

1. Redo Log

熟悉MySQL InnoDB引擎的人都知道,InnoDB有一个最重要的概念就是缓冲池,这是在内存中分配的一个区域,InnoDB会将数据首先缓存在此,请求首先去命中缓冲池,无法命中缓冲池的才会在磁盘上进行检索,被检索到的数据还是会缓存在缓冲池中。

但是缓冲池依赖的内存是一种易失性的存储介质,掉电以后所有的数据都会被抹掉,为了数据的持久性,任何在缓冲池中做出的变更操作,都要持久化到磁盘上,只有这样,数据库才能实现持久性,用户也才能放心的将数据放在数据库上。

现在来看看这样一条SQL语句是如何执行的:

update table1 set table1.col1 = 1 where table1.col2 = 2;

用一个比较直观的流程图表示其过程如下:

image.png
image.png

我这里选择性的忽略了一步,就是有一个线程,采用异步的方式,慢慢的将脏数据页从内存中刷入磁盘的数据文件中。这种commit之后首先写redo,然后异步写数据文件的方式,叫做WAL,即预写日志方式。

有了WAL方式,我们的数据只要commit成功就绝对不会丢失了。InnoDB的REDO LOG有两个可以控制的参数,规定了日志文件最大的大小和一共有多少日志文件。

但是不管有多少个文件,都可以看做是一个文件,redo文件是循环利用的,即文件写满了,就会回收空间。在很多资料上,Redo Log文件都会被画成一个环,实际上也确实如此。

我们知道,Redo的存在保证了持久性,所谓持久性,一般都可以理解为只要提交的事务就一定会持久化。那么Redo是如何保证持久化的呢?设想这样一种情况,在某一时刻,数据库崩溃了,mysqld进程异常退出,此时DBA将数据库重启,会发生什么事情?

这里就要引入一个叫做LSN的概念,即Log Sequence Number,可以理解唯一个序列号或者坐标。标记了日志或者数据文件中最新的位置。一般来说,磁盘文件中最新的LSN都会小于Redo中最新的LSN,那么这两个LSN之间的数据块,就是没有刷入磁盘数据文件的数据块了。

在崩溃重启后,数据库只需要将redo中这部分没有刷入磁盘的数据块刷到数据文件中就可以了,这样崩溃恢复的速度就会大大提高。

image.png
image.png

上图是redo文件的逻辑示意图,这里又引入了一个概念叫checkpoint,实际上是一个动作,即每次读取最老的脏页,确保这个脏页对应的LSN之前的LSN都已经写入了数据文件,这个脏页的LSN作为checkpoint点记录到日志文件中。write pos指写入的位置,也是一个LSN值,这两个LSN值之间的部分是可写部分,如果一旦write pos要赶上checkpoint了,就再做一次checkpoint。

至此我们已经讨论了redo log的一般套路,但是实际使用MySQL过程中还会遇到两个参数:

  • innodb_flush_method
  • innodb_flush_log_at_trx_commit

这两个参数是控制事务提交时的刷磁盘策略的,通常第一个参数在Linux系统上我们都会设置成O_DIRECT,至于这个O_DIRECT的实现方式,可以参考任何一本Linux内核编程的书,这里只需要知道这个O_DIRECT代表了不走OS cache,直接将缓存中的数据块刷到磁盘中。

下图说明了实际情况:

image.png
image.png

而第二个参数,则是控制了commit时redo刷盘的时机:

  • 1:每次commit,都会将redo buffer中的数据块写入redo并立刻刷磁盘;
  • 0:每隔一秒,将redo buffer中的数据块写入redo并刷盘;
  • 2:每次commit都会将redo buffer中的数据块写入redo log,但是每隔1s才会刷盘

2. Binlog

Redo其实是InnoDB特有的一种日志,这也是和Oracle学来的技术,因此学完了InnoDB以后再去学Oracle会感觉很轻松,因为基本原理都是一样的。

Binlog又是一种重要的日志,只不过这种日志是MySQL提供的,什么引擎都可以用。Binlog记录了数据的实际变更(当然如果binlog格式是mixed或者statement,那么大部分情况下记录的是SQL语句),当然有了这种日志,就能够实现复制功能了。而事实上大部分人用binlog都会去做复制,以及备份,当然也有很多人基于binlog开发了类似Oracle的闪回工具。

既然有了binlog,那么什么时候写binlog又是一个值得探究的事情,这就可以引出一个参数,sync_binlog,这个参数控制了binlog和事务提交的关系,取值范围是0-N:

  • 0:不去强制要求,由系统自行判断何时写入;
  • 1:每次commit的时候都要写binlog;
  • N:每N个事务,才会去写binlog

如此看来,最安全的选择还是1,加上之前的innodb_flush_log_at_trx_commit参数,如果选择最安全的方式也是1,在很多材料中都会写MySQL配置为双1,就是指的这两个参数的配置。

既然有两种日志,就会有一个写入的策略问题,这个问题也就引出了另一个概念——两阶段提交。所谓两阶段提交,其实就是将redo的提交拆分成了prepare和commit两个阶段,注意这里的commit不是commit语句,是一种状态。

当事务发起commit的时候,根据上面的描述,首先会将脏数据块写入redo log,但是此时还没有写binlog,因此阶段处于prepare阶段,只有当binlog完成了写操作之后,才会将redo log标记为commit,这个事务才算是真的完结了。

这时,我们来思考一个问题,如果写binlog的时候crash了,怎么办?因为redo log还是处于prepare状态,实际上事务没有真的commit,因此是会回滚的。

Binlog有一个比较好玩的参数:binlog_format,这个参数有三个选项,分别是ROW,STATEMENT和MIXED。

我最开始学习MySQL的时候看到这个MIXED便被他的名字迷惑了,这个参数一定是智能的,一定是最好的。但是事实证明我还是想错了,这个参数其实是为了兼容老旧的STATEMENT参数设计的,大部分情况下,binlog里记录都是STATEMENT格式。这里就要说一下STATEMENT格式记录了什么了。

如果是我来设计怎么将主库的事件发送到从库,那么我在设计时一定会首先想到将主库上执行过的所有的SQL都发给从库,这样就可以了。

这样的确可以,而且还很简单,但是确实有隐患。举一个简单的例子来说,如果主库上的SQL语句里有讲sysdate()插入表的语句,那么在从库上执行的时候,这个sysdate()获取到的实际上是从库的时间,这就存在主库和从库不一致的情况了,谁也不能保证语句立刻就能发送到从库并立刻执行,这一切操作保证在1s之内是很难的,存在网络问题,存在单线程复制回放问题的限制。

因此这种方式后来的MySQL开发者也觉得不好,所以设计了一种新的binlog格式,即ROW格式。这种格式记录了实际的数据变更,这样就解决了上面说的问题。不过,为了兼容旧版,MySQL的设计者设计了一个颇具迷惑性的MIXED选项,这个选项大部分情况下,都会把SQL语句记录在binlog里,而且这个选项也没有办法支持最新的GTID特性。

综上所述,一个系统要做复制,一定要使用ROW格式。

不过STATEMENT格式也不是没有其好处,至少binlog很好读,里面都是明文记录的SQL语句,要追查什么很方便,而ROW都是二进制加密的,可读性非常差,这里提供一个一般性的语句:

mysqlbinlog --base64-output=decode-rows -vv binlogXXXX

binlog是实现复制的重要日志,Master负责将事件记录在binlog中,并且通知slave将日志取走,Slave的IO线程将数据取走之后,将binlog中的事件保存在本地的中继日志中,由一个叫做sql线程的线程开始依次将中继日志中的事件回放在本地。

这个过程就是复制的基本原理,虽然现在有异步复制,半同步复制,增强型半同步复制,但是复制的基本流程和原理却始终没有变。

在此需要引入一个重要的概念——GTID,即全局事务ID。当然本文并不是论述GTID及其运维的,因此只是简单提要。

GTID的出现大大简化了基于binlog的复制配置和运维难度,配置复制的时候不再需要直接指定pos等信息,而是可以自动化的进行。

下图是binlog中记录的事件,可以看出来,每一个事务之前都会有一个set GTID的语句,因此从库在回放的时候,首先会执行该语句,那么这个GTID就会被从库维护起来,表示这个GTID已经执行过了。

image.png
image.png

假如我需要将slave节点挂载在主备上,那么基于GTID的复制会简化我的操作,因为slave上记录了所有已经执行过的GTID,此时是不需要手动干预去指定pos之类的值的,slave自己就可以判断要从哪里开始继续复制。

<a name="9d4fa2ba"></a>

3. Undo Log

Undo日志顾名思义是用来做回滚的,其实Undo的作用不止于此,MVCC这种重要的概念也是基于Undo实现的。

为了阐述MVCC,这里需要引入一个比较重要的概念——read view。我们知道,一个事务读取到的数据实际上是一个快照,这是MVCC的基本功能,只有这样,才能保证并发能力,即一个事务拿到记录的X锁之后,并不会阻塞其他事务读取数据,即便X锁和其他的锁是互斥的。

每一次的数据更新,都会生成一个read view,比如下面的图:

image.png
image.png

undo里面有按照顺序排列的read view,一个事务开始的时候,系统就会分给它一个read view,这样也就实现了MVCC。

那么谁去读当前值呢?看起来似乎每个读请求都是平行宇宙一样互不干扰,这就又引出了两个新的名词:

  • 当前读
  • 快照读

快照读读取到的就是read view,一般都会写的select一定是快照读了,但是如果你加上了for update,那么就一定是当前读了。

根据undo的生成机制,每次修改数据都会生成一个read view,那么在一个很多写操作的系统上,或者进行过大量批量修改数据的系统上,undo表空间就会变得非常大。在MySQL5.5版本以前,undo是放在共享表空间里的,而且这个共享表空间文件很有意思,一旦变大了就再也不会变小,虽然undo里的日志会被系统自动择机删除,但是其申请的空间就再也不会回缩了。

这是一个不好的设计,因此在后来的版本中,undo已经可以独立表空间文件了,而且这个文件也是可以回收空间的。

4. 其他日志

其他日志包括文本格式的error log,慢查日志和general log,还有中继日志。

其中中继日志顾名思义,就是将主库发送来的binlog先保存在本地,然后按循序进行回放。

文本格式的日志里,error log记录了MySQL运行过程中打印出来的日志,包括warning,error或者info,这些都是排查问题时的参考。

慢查日志记录了符合条件(慢查时间阈值,是否使用索引)的SQL,这是很重要的性能优化和排查依据。

general log一般用来调试的时候使用,记录了所有的数据库操作明细,开启以后会大量降低数据库系统性能,不建议在生产环境上开启。

5. 小结

MySQL的日志系统并不比索引,事务等系统简单,而且是十分重要的组成部分,也恰恰是因为日志系统的存在,MySQL或者说InnoDB才能保证用户数据的安全,保证较高的性能。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 155,958评论 4 358
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 66,179评论 1 286
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 105,792评论 0 237
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,409评论 0 202
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 51,723评论 3 285
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,174评论 1 204
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,543评论 2 306
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,270评论 0 194
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 33,899评论 1 236
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,235评论 2 240
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 31,771评论 1 255
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,139评论 2 249
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 32,688评论 3 228
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 25,936评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,646评论 0 192
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,194评论 2 265
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,145评论 2 258

推荐阅读更多精彩内容