4.MySQL日志
MySQL日志:包括错误日志、查询日志、慢查询日志、事务日志、二进制日志几大类。
Binlog(二进制日志、归档日志) redo log(事务日志、重做日志) undo log(回滚日志)
(1)Redo log
重做日志是InnoDB独有的,它让MySQL拥有了崩溃恢复的能力。
Buffer Pool是MySQL存储引擎为了加速数据的读取速度而设计的缓冲机制,在存储引擎层。
查询缓存的cache在server层。
MySQL 中数据是以页为单位,
你查询一条记录,会从硬盘把一页的数据加载出来,加载出来的数据叫数据页,会放入到 Buffer Pool 中。
后续查询先从Buffer Pool中找,没有命中再去硬盘加载,减少硬盘IO开销,提升性能。
更新表数据的时候,发现Buffer Pool里存在要更新的数据,就直接在Buffer Pool里更新。
然后会把“在某个数据页上做了什么修改”记录到重做日志缓存(redo log buffer)里,接着刷盘到redo log文件里。
每条redo记录由“表空间号+数据页号+偏移量+修改数据长度+具体修改的数据”组成
刷盘时机:
InnoDB 存储引擎为redo log的刷盘策略提供了 innodb_flush_log_at_trx_commit 参数,它支持三种策略:
0 :设置为 0 的时候,表示每次事务提交时不进行刷盘操作
1 :设置为 1 的时候,表示每次事务提交时都将进行刷盘操作(默认值)
2 :设置为 2 的时候,表示每次事务提交时都只把redo log buffer内容写入 page cache
innodb_flush_log_at_trx_commit 参数默认为 1 ,InnoDB 存储引擎有一个后台线程,每隔1 秒,就会把 redo log buffer 中的内容写到文件系统缓存(page cache),然后调用 fsync 刷盘。也就是说,一个没有提交事务的 redo log 记录,也可能会刷盘。
除了后台线程每秒1次的轮询操作,还有一种情况,当 redo log buffer 占用的空间即将达到 innodb_log_buffer_size 一半的时候,后台线程会主动刷盘。
只要事务提交成功,redo log记录就一定在硬盘里,不会有任何数据丢失。
如果事务执行期间MySQL挂了或宕机,这部分日志丢了,但是事务并没有提交,所以日志丢了也不会有损失。只要事务提交成功,redo log buffer中的内容只写入文件系统缓存(page cache)。
如果仅仅只是MySQL挂了不会有任何数据丢失,但是宕机可能会有1秒数据的丢失。
日志文件组:(redo log存储形式)
硬盘上存储的redo log日志文件不只一个,而是以一个日志文件组的形式出现的,每个的redo日志文件大小都是一样的。
比如可以配置为一组4个文件,每个文件的大小是 1GB,整个 redo log 日志文件组可以记录4G的内容。
它采用的是环形数组形式,从头开始写,写到末尾又回到头循环写,如下图所示。
在个日志文件组中还有两个重要的属性,分别是 write pos、checkpoint
write pos 是当前记录的位置,一边写一边后移
checkpoint 是当前要擦除的位置,也是往后推移
如果 write pos 追上 checkpoint ,表示日志文件组满了,这时候不能再写入新的 redo log 记录,MySQL 得停下来,清空一些记录,把 checkpoint 推进一下。
只要每次把修改后的数据页直接刷盘不就好了,还有redo log什么事?
它们不都是刷盘么?差别在哪里?
数据页大小是16KB,刷盘比较耗时,可能就修改了数据页里的几 Byte 数据,有必要把完整的数据页刷盘吗?
而且数据页刷盘是随机写,因为一个数据页对应的位置可能在硬盘文件的随机位置,所以性能很差。
如果是写 redo log,一行记录可能就占几十 Byte,只包含表空间号、数据页号、磁盘文件偏移量、更新值,再加上是顺序写,所以刷盘速度很快。
所以用 redo log 形式记录修改内容,性能会远远超过刷数据页的方式,这也让数据库的并发能力更强。
redo log 如何保证事务的持久性?
Redo Log包括两部分:
一是内存中的重做日志缓冲(Redo Log Buffer),该部分日志是易失性的;
二是磁盘上的重做日志文件(Redo Log File),该部分日志是持久的。
redo log的主要功能是用于数据库崩溃时的数据恢复。
保证持久性,其实很简单,只用把该事务在内存中修改的全部页面刷新到磁盘就可以了,但是,这些页面可能并不相邻,需要进行很多随机 IO,对于传统的机械硬盘就会特别慢,所以,为了提升效率,设计 MySQL 的大叔就引入了 redo log,它只用来保存事务对数据页所做的修改.
比如,当一条记录需要更新时,Innodb 引擎会先把对这个数据页的修改 写到 redo log buffer 中,事务提交时再把 redo log buffer 刷到磁盘文件 redo log 中,这样是追加写,顺序 IO 速度很快.
将redo log buffer携带trx_id=10写⼊到redo log⽂件,持久化到磁盘,这步操作叫做redo log prepare
当有一条记录更新的时候, InnoDB 引擎就会先把记录写到 redo log 里面去,同时更新内存,这样就算是更新这条数据成功了。
但是此时,它并没有更新到磁盘上去对吧?别担心, InnoDB 会在恰当的时候,把这条及记录更新到磁盘上去,而这个更新往往是在系统比较空闲的时候做。
(2)Binlog
redo log是物理日志,记录“在某个数据页上做了什么修改”,属于InnoDB存储引擎。
binlog是逻辑日志,记录内容是语句的原始逻辑,它记录了数据库上的所有改变,并以二进制的形式保存在磁盘中,类似于“给 ID=2这一行的c字段加 1”,属于MySQL Server层。
不管用什么存储引擎,只要发生了表数据更新,都会产生 binlog 日志。
可以说MySQL数据库的数据备份、主备、主主、主从都离不开binlog,需要依靠binlog来同步数据,保证数据一致性。binlog会记录所有涉及更新数据的逻辑操作,并且是顺序写。
记录格式:可以通过binlog_format参数指定。
Statement:记录的内容是SQL语句原文,update_time=now()这里会获取当前系统时间,直接执行会导致与原库的数据不一致。
Row:为了解决这种问题,我们需要指定为row,记录的内容不再是简单的SQL语句了,还包含操作的具体数据。row格式记录的内容看不到详细信息,要通过mysqlbinlog工具解析出来。这样可以为数据库的恢复与同步带来更好的可靠性。
但是这种格式,需要更大的容量来记录,比较占用空间,恢复与同步时会更消耗IO资源,影响执行速度。
Mixed:一种折中的方案,指定为mixed,记录的内容是前两者的混合。
MySQL会判断这条SQL语句是否可能引起数据不一致,如果是,就用row格式,否则就用statement格式。
写入机制:
事务执行过程中,先把日志写到binlog cache,事务提交的时候,再把binlog cache写到binlog文件中。
因为一个事务的binlog不能被拆开,无论这个事务多大,也要确保一次性写入,所以系统会给每个线程分配一个块内存作为binlog cache。
我们可以通过binlog_cache_size参数控制单个线程 binlog cache 大小,如果存储内容超过了这个参数,就要暂存到磁盘(Swap)。
上图write,指把日志写入到文件系统的page cache,并没有把数据持久化到磁盘,速度比较快。上图fsync,才是将数据持久化到磁盘的操作。
write和fsync的时机,可以由参数sync_binlog控制,默认是0。
0:表示每次提交事务都只write,由系统自行判断什么时候执行fsync
虽然性能得到提升,但是机器宕机,page cache里面的 binlog 会丢失。
1:表示每次提交事务都会执行fsync,就如同 redo log 日志刷盘流程一样。(安全)
N(N>1):表示每次提交事务都write,但累积N个事务后才fsync。同样的,如果机器宕机,会丢失最近N个事务的binlog日志。(折中)
两阶段提交:
redo log(重做日志)让InnoDB存储引擎拥有了崩溃恢复能力。
binlog(归档日志)保证了MySQL集群架构的数据一致性。
在执行更新语句过程,会记录redo log与binlog两块日志,以基本的事务为单位,redo log在事务执行过程中可以不断写入,而binlog只有在提交事务时才写入,所以redo log与binlog的写入时机不一样。
为了解决两份日志之间的逻辑一致问题,InnoDB存储引擎使用两阶段提交方案:将redo log的写入拆成了两个步骤prepare和commit,这就是两阶段提交:
二阶段提交流程:
-
在准备阶段,MySQL先将数据修改写入redo log,并将其标记为prepare状态,表示事务还未提交。然后将对应的SQL语句写入bin log。
-
在提交阶段,MySQL将redo log标记为commit状态,表示事务已经提交。然后根据sync_binlog参数的设置,决定是否将bin log刷入磁盘。
通过这样的流程,MySQL可以保证在任何时刻,redo log和bin log都是逻辑上一致的。如果MySQL发生崩溃,可以根据redo log恢复数据页的状态,也可以根据bin log恢复SQL语句的执行。
使用两阶段提交后,写入binlog时发生异常也不会有影响,因为MySQL根据redo log日志恢复数据时,发现redo log还处于prepare阶段,并且没有对应binlog日志,就会回滚该事务
redo log设置commit阶段发生异常,那会不会回滚事务呢?并不会回滚事务,它会执行上图框住的逻辑,虽然redo log是处于prepare阶段,但是能通过事务id找到对应的binlog日志,所以MySQL认为是完整的,就会提交事务恢复数据。
(3)Undo log日志:
在 MySQL 中,恢复机制是通过 回滚日志(undo log) 实现的,所有事务进行的修改都会先记录到这个回滚日志中,然后再执行相关的操作。(如何保证事务的原子性?)如果执行过程中遇到异常的话,我们直接利用 回滚日志 中的信息将数据回滚到修改之前的样子即可!并且,回滚日志会先于数据持久化到磁盘上。这样就保证了即使遇到数据库突然宕机等情况,当用户再次启动数据库的时候,数据库还能够通过查询回滚日志来回滚将之前未完成的事务。
MySQL InnoDB 引擎使用 redo log(重做日志) 保证事务的持久性,使用 undo log(回滚日志) 来保证事务的原子性。
MySQL数据库的数据备份、主备、主主、主从都离不开binlog,需要依靠binlog来同步数据,保证数据一致性。
2.慢查询日志什么用?
用来记录在MySQL中响应时间超过阀值的语句long_query_time
慢查询日志可以帮助dba找出执行效率缓慢的sql语句,为数据库的优化工作提供帮助
3.binlog 和 redolog 有什么区别?
(1)redo log 是innodb独有的,binlog是server层实现的,所有引擎都能使用;
(2)redo log大小固定(可以设置),buffer pool的记录落盘后,日志就可以被覆盖了,无法保证用于数据回滚;
(3)redolog 更多是为了保证,异常宕机之后我们可以根据redo log把没有刷到磁盘中的数据恢复。因为我们的数据都是先写到buffer pool的,然后刷到磁盘中,俗称脏页吧~
(4)redo log有上限,写满了之后就会覆盖,binlog不会
(5)binlog是追加方式实现的,默认1G,当文件大于给定值后,日志会发生滚动,生成新的文件继续记录;
(6)恢复数据时我们需要用mysqlbinlog来截取binlog的部分数据;
(7)redo log 和 binlog必须保持一致
(8)最开始 MySQL 里并没有 InnoDB 引擎。MySQL 自带的引擎是 MyISAM,但是 MyISAM 没有 crash-safe 的能力,binlog 日志只能用于归档。而 InnoDB 是另一个公司以插件形式引入 MySQL 的,既然只依靠 binlog 是没有 crash-safe 能力的,所以 InnoDB 使用另外一套日志系统——也就是 redo log 来实现 crash-safe 能力。
(9)redo log 是物理日志(其实准确讲是物理加逻辑),记录的是“在某个数据页上做了什么修改”;binlog 是逻辑日志,记录的是这个语句的原始逻辑,比如“给 ID=2 这一行的 c 字段加 1 ”。