Mysql的日志那些事
Mysql中日志还是挺多的,主要包含以下几个常用的日志:
-
binlog:归档日志, Server层的日志。 -
redo log:重做日志,InnoDB存储引擎层的日志。 -
undo log:回滚日志,提供回滚操作,InnoDB存储引擎层的日志。 -
relay log:中继日志,主从复制原理日志。 -
slow_query_log:慢查询日志,用于慢sql。
下面我们就来聊一聊这几个日志的原理和作用。
binlog和redo log
redo log日志也叫做WAL技术(Write- Ahead Logging),他是一种先写日志,并更新内存,最后再更新磁盘的技术,为了就是减少sql执行期间的数据库io操作,并且更新磁盘往往是在Mysql比较闲的时候,这样就大大减轻了Mysql的压力。
redo log是固定大小,是物理日志,属于InnoDB引擎的,并且写redo log是环状写日志的形式:
如上图所示:若是四组的redo log文件,一组为1G的大小,那么四组就是4G的大小,其中write pos是记录当前的位置,有数据写入当前位置,那么write pos就会边写入边往后移。
check point记录擦除的位置,因为redo log是固定大小,所以当redo log满的时候,也就是write pos追上check point的时候,需要清除redo log的部分数据,清除的数据会被持久化到磁盘中,然后将check point向前移动。
redo log日志实现了即使在数据库出现异常宕机的时候,重启后之前的记录也不会丢失,这就是crash-safe能力。
binlog称为归档日志,是逻辑上的日志,它属于Mysql的Server层面的日志,记录着sql的原始逻辑,主要有两种模式:一个是statement格式记录的是原始的sql,而row格式则是记录行内容。
redo log和binlog记录的形式、内容不同,这两者日志都能通过自己记录的内容恢复数据。
之所以这两个日志同时存在,是因为刚开始Mysql自带的引擎MyISAM就没有crash-safe功能的,并且在此之前Mysql还没有InnoDB引擎,Mysql自带的binlog日志只是用来归档日志的,所以InnoDB引擎也就通过自己redo log日志来实现crash-safe功能。
undo log
undo log是回滚日志,是实现MVCC的一部分,要详细了解undo log,我们先来详细了解MVCC的原理。
MVCC叫做多版本控制,实现MVCC时用到了一致性视图,用于支持读提交和可重复读的实现。
对于一行数据若是想实现可重复读取或者能够读取数据的另一个事务未提交前的原始值,那么必须对原始数据进行保存或者对更新操作进行保存,这样才能够查询到原始值。
在Mysql的MVCC中规定每一行数据都有多个不同的版本,一个事务更新操作完后就生成一个新的版本,并不是对全部数据的全量备份,因为全量备份的代价太大了:
如图中所示,假如三个事务更新了同一行数据,那么就会有对应的v1、v2、v3三个数据版本,每一个事务在开始的时候都获得一个唯一的事务id(transaction id),并且是顺序递增的,并且这个事务id最后会赋值给row trx_id,这样就形成了一个唯一的一行数据版本。
实际上版本1、版本2并非实际物理存在的,而图中的U1和U2实际就是undo log日志(回滚日志),这v1和v2版本是根据当前v3和undo log计算出来的。
InnoDB引擎就是利用每行数据有多个版本的特性,实现了秒级创建“快照”,并不需要花费大量的是时间。
relay log
relay log中继日志用于实现主从复制,我们先来了解一下主从复制原理。
在实际的生产中,为了解决Mysql的单点故障已经提高MySQL的整体服务性能,一般都会采用「主从复制」。
比如:在复杂的业务系统中,有一句sql执行后导致锁表,并且这条sql的的执行时间有比较长,那么此sql执行的期间导致服务不可用,这样就会严重影响用户的体验度。
主从复制中分为「主服务器(master)「和」从服务器(slave)」,「主服务器负责写,而从服务器负责读」,Mysql的主从复制的过程是一个「异步的过程」。
这样读写分离的过程能够是整体的服务性能提高,即使写操作时间比较长,也不影响读操作的进行。
首先放一张Mysql主从复制的原理图,总的来说Mysql的主从复制原理还是比较好理解的,原理非常的简单。
Mysql的主从复制中主要有三个线程:master(binlog dump thread)、slave(I/O thread 、SQL thread),Master一条线程和Slave中的两条线程。
master(binlog dump thread)主要负责Master库中有数据更新的时候,会按照binlog格式,将更新的事件类型写入到主库的binlog文件中。
并且,Master会创建log dump线程通知Slave主库中存在数据更新,这就是为什么主库的binlog日志一定要开启的原因。
I/O thread线程在Slave中创建,该线程用于请求Master,Master会返回binlog的名称以及当前数据更新的位置、binlog文件位置的副本。
然后,将binlog保存在 「relay log(中继日志)」 中,中继日志也是记录数据更新的信息。
SQL线程也是在Slave中创建的,当Slave检测到中继日志有更新,就会将更新的内容同步到Slave数据库中,这样就保证了主从的数据的同步。
以上就是主从复制的过程,当然,主从复制的过程有不同的策略方式进行数据的同步,主要包含以下几种:
-
「同步策略」:Master会等待所有的Slave都回应后才会提交,这个主从的同步的性能会严重的影响。 -
「半同步策略」:Master至少会等待一个Slave回应后提交。 -
「异步策略」:Master不用等待Slave回应就可以提交。 -
「延迟策略」:Slave要落后于Master指定的时间。
对于不同的业务需求,有不同的策略方案,但是一般都会采用最终一致性,不会要求强一致性,毕竟强一致性会严重影响性能。
slow_query_log
在mysql中有一个动态参数long_query_time表示慢查询的阈值,表示当执行的sql超过这个这个参数的时间,就数据慢查询的范围就会将此纪录存入该日志中,该值默认为10,通以下命令进行查看。
在实际的开发中,一般不会有超过10秒的的sql,所以我们将该值可以设置小一点,设置为1秒,通过命令 SET long_query_time=1将该值设置为1秒,但是默认慢查询的日志记录功能是关闭的。如下图所示:
通过命令SET GLOBAL slow_query_log=ON将该功能开启,最后做一个测试执行sql:select sleep(10);然后来到mysql的datadir目录下就会有该慢查询文件:
执行cat + 改文件名,就可以看到该sql执行的时间,以及执行慢的sql语句:
另外通过设置log_queries_not_using_indexes参数,也可以将没有使用索引的sql记录收到该文件中。