mysql的日志系统比较庞大,主要为了实现mysql中各种各样的功能。
在mysql中,日志主要分为以下几类:
其中redo log和undo log主要在事务中使用;bin log主要用于主从服务器之间的数据同步,以及服务器遇到故障时数据的无损失恢复;slow query log在我们查询和优化sql时使用。其他的不是很重要。
日志的重要性不言而喻,到那时也会带来一些问题,比如日志功能 降低MySQL数据库的性能 ,占用大量的磁盘空间 。
重点学习bin log、undo log、redo log三大日志,其他了解即可。
错误日志是MySQL中最重要的日志之一,他记录了当mysql启动和停止时,以及服务器在运行过程中发生任何严重错误的相关信息。当数据库出现任何故障导致无法正常使用时,建议首先查看此日志。
该日志默认是开启的,默认存放目录(Linux)/var/log/,默认的日志文件名为mysql.log,
查看日志位置:
show variables like '%log_error%';
指定日志路径两种方法:
慢查询日志记录了所有执行时间超过参数long_query_time设置值并且扫描记录数不小于min_examined_row_limit 的所有的SQL语句的日志,默认未开启。long_query_time默认为10秒,最小为0,精度可以到微秒。
如果需要开启慢查询日志,需要在MySQL的配置文件/etc/my.cnf中配置如下参数
#慢查询日志
slow_query_log=1
#执行时间参数
long_query_time=2
默认情况下,不会记录管理语句,也不会记录不使用索引进行查找的查询。可以使用log_slow_admin_statements
和log_queries_not_using_indexs
更改此行为,如下:
#记录执行较慢的管理语句
log_slow_admin_statements =1
#记录执行较慢的未使用索引的语句
log_queries_not_using_indexes = 1
记录了服务器接收到的每一个查询或是命令,无论这些查询或是命令是否正确甚至是否包含语法错误,general log 都会将其记录下来 ,记录的格式为 {Time ,Id ,Command,Argument }。也正因为mysql服务器需要不断地记录日志,开启General log会产生不小的系统开销。 因此,Mysql默认是把General log关闭的。
查看日志位置:
show variables like '%general%';
通过修改MySQL的配置文件/etc/my.cnf
#该选项用来开启查询日志 , 可选值 :0 或者 1 ;0 代表关闭, 1 代表开启
general_log=1
#设置日志的文件名 , 如果没有指定, 默认的文件名为 host_name.log
general_log_file=mysql_query.log
中继日志只在集群主从或主主服务器架构的从服务器上存在。
MySQL 进行主主复制或主从复制的时候会在要复制的服务器下面产生相应的 relay log。
relay log 是怎么产生的呢?
从服务器 I/O 线程将主服务器的 Binlog 日志读取过来,解析到各类 Events 之后记录到从服务器本地文件,这个文件就被称为 relay log。然后 SQL 线程会读取 relay log 日志的内容并应用到从服务器,从而使从服务器和主服务器的数据保持一致。中继日志充当缓冲区,这样 master 就不必等待 slave 执行完成才发送下一个事件。
在讲bin log、undo log、redo log三大日志前,先来了解下Buffer Pool 缓存是什么。
Innodb 存储引擎设计了一个缓冲池(Buffer Pool),来提高数据库的读写性能。
有了 Buffer Pool 后:
InnoDB 引擎在适当的时候,由后台线程将缓存在 Buffer Pool 的脏页刷新到磁盘里的技术也称为 WAL (Write-Ahead Logging)技术。
InnoDB 会把存储的数据划分为若干个「页」,以页作为磁盘和内存交互的基本单位,一个页的默认大小为 16KB。因此,Buffer Pool 同样需要按「页」来划分。
在 MySQL 启动的时候,InnoDB 会为 Buffer Pool 申请一片连续的内存空间,然后按照默认的16KB
的大小划分出一个个的页, Buffer Pool 中的页就叫做缓存页。此时这些缓存页都是空闲的,之后随着程序的运行,才会有磁盘上的页被缓存到 Buffer Pool 中。
所以,MySQL 刚启动的时候,你会观察到使用的虚拟内存空间很大,而使用到的物理内存空间却很小,这是因为只有这些虚拟内存被访问后,操作系统才会触发缺页中断,申请物理内存,接着将虚拟地址和物理地址建立映射关系。
Buffer Pool 除了缓存「索引页」和「数据页」,还包括了 Undo 页,插入缓存、自适应哈希索引、锁信息等等。
undo log 是一种用于撤销回退的日志。在事务没提交之前,MySQL 会先记录更新前的数据到 undo log 日志文件里面,当事务回滚时,可以利用 undo log 来进行回滚。
每当 InnoDB 引擎对一条记录进行操作(修改、删除、新增)时,要把回滚时需要的信息都记录到 undo log 里,比如:
在发生回滚时,就读取 undo log 里的数据,然后做原先相反操作。
undo Log 存储采用分段(segment)的方式管理和记录。
undo log 的存储控制可以通过下面这条参数实现:
show variables like '%innodb_undo%';
在 InnoDB 存储数据的文件中,包含了一种回滚段(Rollback Segment),每个回滚段中有 1024 个Undo log segment(版本 5.5 后,可以支持 128 个 Rollback Segment)。
每个回滚段都对应一个或多个 Undo log 日志文件,用于记录事务执行前的数据快照、以及存储事务操作的详细信息,例如插入、更新或删除。
一条记录的每一次更新操作产生的 undo log 格式都有一个 roll_pointer 指针和一个 trx_id 事务id:
版本链如下图:
在InnoDB存储引擎中,undo log分为:insert undo log
和 update undo log
insert undo log是指在insert 操作中产生的undo log,因为insert操作的记录,只对事务本身可见,对其他事务不可见。故该undo log可以在事务提交后直接删除,不需要进行purge操作。
而update undo log记录的是对delete 和update操作产生的undo log,该undo log可能需要提供MVCC机制,因此不能再事务提交时就进行删除。提交时放入undo log链表,等待后台 purge 线程进行回收处理的进行最后的删除。
redo log 是物理日志,记录了某个数据页做了什么修改,当数据库进行数据修改操作(如插入、更新、删除)时,会对数据本身进行修改,并生成一个对应的重做日志记录。
这个重做日志记录包含了修改的细节。例如,被修改的数据块位置、修改前后的值等。这些日志记录被持久化保存在磁盘上,确保即使系统崩溃,也能通过重做日志来恢复数据的一致性。
在事务提交时,只要先将 redo log 持久化到磁盘即可,可以不需要等到将缓存在 Buffer Pool 里的脏页数据持久化到磁盘。当系统崩溃时,虽然脏页数据没有持久化,但是 redo log 已经持久化,接着 MySQL 重启后,可以根据 redo log 的内容,将所有数据恢复到最新的状态。这个能力称为 crash-safe(崩溃恢复)。从而保证了事务的持久性。
过程如下:
写入 redo log 的方式使用了追加操作, 所以磁盘操作是顺序写,而写入数据需要先找到写入位置,然后才写到磁盘,所以磁盘操作是随机写。磁盘的「顺序写 」比「随机写」 高效的多,因此 redo log 写入磁盘的开销更小。
Redo log 的存储都是以 块(block) 为单位进行存储的。
每个块由三部分组成:日志块头(log block header)、日志块尾(log block tailer)、日志本身。
每个块是 512 字节,同磁盘扇区大小一致,可以保证块的写入是原子操作。
一个日志文件由多个块所构成,多个日志文件形成一个重做日志文件组(Redo log group)。
redo log 不是直接写入磁盘的,因为这样会产生大量的 I/O 操作,而且磁盘的运行速度远慢于内存。
所以,redo log 也有自己的缓存—— redo log buffer,每当产生一条 redo log 时,会先写入到 redo log buffer,后续在持久化到磁盘如下图:
redo log buffer 默认大小 16 MB,可以通过 innodb_log_Buffer_size
参数动态的调整大小,增大它的大小可以让 MySQL 处理「大事务」是不必写入磁盘,进而提升写 IO 性能。
redo log 刷盘策略
刷盘策略由参数 innodb_flush_log_at_trx_commit
参数控制,可取的值有:0、1、2,默认值为 1,这三个值分别代表的策略如下:
这三个参数的数据安全性和写入性能的比较如下:
InnoDB 的后台线程每隔 1 秒:
write()
写到操作系统的 Page Cache,然后调用 fsync()
持久化到磁盘。所以参数为 0 的策略,MySQL 进程的崩溃会导致上一秒钟所有事务数据的丢失;如何选择刷盘策略:
innodb_flush_log_at_trx_commit
参数需要设置为 1。默认情况下, InnoDB 存储引擎有 1 个重做日志文件组( redo log Group),「重做日志文件组」由有 2 个 redo log 文件组成,这两个 redo 日志的文件名叫 :ib_logfile0
和 ib_logfile1
。
在重做日志组中,每个 redo log File 的大小是固定且一致的,假设每个 redo log File 设置的上限是 1 GB,那么总共就可以记录 2GB 的操作。
重做日志文件组是以循环写的方式工作的,从头开始写,写到末尾就又回到开头,相当于一个环形。
所以 InnoDB 存储引擎会先写 ib_logfile0 文件,当 ib_logfile0 文件被写满的时候,会切换至 ib_logfile1 文件,当 ib_logfile1 文件也被写满时,会切换回 ib_logfile0 文件。
我们知道 redo log 是为了防止 Buffer Pool 中的脏页丢失而设计的,那么如果随着系统运行,Buffer Pool 的脏页刷新到了磁盘中,那么 redo log 对应的记录也就没用了,这时候我们擦除这些旧记录,以腾出空间记录新的更新操作。
redo log 是循环写的方式,相当于一个环形,InnoDB 用 write pos 表示 redo log 当前记录写到的位置,用 checkpoint 表示当前要擦除的位置,如下图:
图中的:
如果 write pos 追上了 checkpoint,就意味着 redo log 文件满了,这时 MySQL 不能再执行新的更新操作,也就是说 MySQL 会被阻塞(因此所以针对并发量大的系统,适当设置 redo log 的文件大小非常重要),此时会停下来将 Buffer Pool 中的脏页刷新到磁盘中,然后标记 redo log 哪些记录可以被擦除,接着对旧的 redo log 记录进行擦除,等擦除完旧记录腾出了空间,checkpoint 就会往后移动(图中顺时针),然后 MySQL 恢复正常运行,继续执行新的更新操作。
所以,一次 checkpoint 的过程就是脏页刷新到磁盘中变成干净页,然后标记 redo log 哪些记录可以被覆盖的过程。
bin log 是 MySQL 服务层实现的日志,记录所有数据库表结构变更以及数据修改的二进制日志(不会记录查询和show这类操作)。
因为binlog占用系统资源较大 1%,所以一般是关闭状态。
当需要主从复制:获数据备份时我们才开启。可使用如下参数开启:
log-bin=/var/lib/mysql/mysql-bin
通过命令行的方式开启 Bin log:
SET SQL_LOG_BIN=1
主数据库将修改操作记录到 bin log 中,然后主库传输到从库上,从数据库通过解析 bin log 实现数据的同步。传输的这个过程一般是异步的,也就是主库上执行事务操作的线程不会等待复制 binlog 的线程同步完成。
还需要配合中继日志完成。
具体详细过程如下:
bin log 记录了每个事务的详细操作,包括数据的修改和事务的状态变更。
使用 Bin log 恢复 MySQL 数据,简单地说,就是让 MySQL 将保存在 Bin log 日志中指定段落区间的 sql 语句,逐个重新执行一次而已。
bin log 日志文件包含了索引文件和具体日志文件。
每个 Bin log 事件由四个部分组成:
在 MySQL 中,默认设置是 sync_Bin log=0 ,即不作任何强制性的磁盘刷新指令。
sync_Bin log=0 的性能最好、但风险也最大,一旦系统绷 Crash ,在文件系统缓存中的所有 Bin log 信息都会丢失。
基于语句的复制模式。
Statement 模式将数据库中执行的修改操作记录为 SQL 语句,再从数据库上执行相同的 SQL 语句来实现数据同步。
优点:简单明了,易于理解和实现。
缺点:在执行涉及非确定性函数、触发器和存储过程等操作时,可能会导致不一致的结果。
适用于大多数情况下的数据库复制需求。
Row 模式是基于行的复制模式,它将数据库中实际修改的行记录写入 Binlog ,从数据库通过解析 Binlog
来逐行执行相应的修改操作。相对 statement ,Row 模式更加精确、安全,能够确保数据的一致性。
优点:能准确复制修改的行记录,避免了语句复制模式下的不确定性问题。
缺点:如果 Binlog 文件较大,传输成本就会很高,在某些情况下,可能会导致性能下降。
Mixed 模式(混合模式)是将语句复制模式和行复制模式结合起来使用。
大多数的修改操作,通常使用 Statement 模式记录对应的 SQL 语句。
一些特殊的操作,涉及非确定性函数和存储过程等,则使用 Row 模式记录修改的行记录。
Mixed 模式综合了语句复制模式和行复制模式的优点,能够在大多数情况下高效地记录修改操作,并在需要时使用行复制模式确保数据的准确性。但 Mixed 模式对一些特殊操作的处理可能会很复杂,需要特别注意下配置和管理。
事务执行过程中,先把日志写到 binlog cache(Server 层的 cache),事务提交的时候,再把 binlog cache 写到 binlog 文件中。
一个事务的 binlog 是不能被拆开的,因此无论这个事务有多大(比如有很多条语句),也要保证一次性写入。这是因为有一个线程只能同时有一个事务在执行的设定,所以每当执行一个 begin/start transaction 的时候,就会默认提交上一个事务,这样如果一个事务的 binlog 被拆开的时候,在备库执行就会被当做多个事务分段自行,这样破坏了原子性,是有问题的。
MySQL 给每个线程分配了一片内存用于缓冲 binlog ,该内存叫 binlog cache,参数 binlog_cache_size 用于控制单个线程内 binlog cache 所占内存的大小。如果超过了这个参数规定的大小,就要暂存到磁盘。
在事务提交的时候,执行器把 binlog cache 里的完整事务写入到 binlog 文件中,并清空 binlog cache。如下图:
虽然每个线程有自己 binlog cache,但是最终都写到同一个 binlog 文件:
MySQL提供一个 sync_binlog 参数来控制数据库的 binlog 刷到磁盘上的频率:
在MySQL中系统默认的设置是 sync_binlog = 0,也就是不做任何强制性的磁盘刷新指令,这时候的性能是最好的,但是风险也是最大的。因为一旦主机发生异常重启,还没持久化到磁盘的数据就会丢失。
而当 sync_binlog 设置为 1 的时候,是最安全但是性能损耗最大的设置。因为当设置为 1 的时候,即使主机发生异常重启,最多丢失一个事务的 binlog,而已经持久化到磁盘的数据就不会有影响,不过就是对写入性能影响太大。
如果能容少量事务的 binlog 日志丢失的风险,为了提高写入的性能,一般会 sync_binlog 设置为 100~1000 中的某个数值。
参考:
内容和图片摘取以下博文
https://mp.weixin.qq.com/s/AhW2VBWuuU4WqtBvvVGUuA
https://xiaolincoding.com/mysql/log/how_update.html
https://mp.weixin.qq.com/s/6Maca3ZX62cRY5-0uNuR4w
https://mp.weixin.qq.com/s/4QHFchIvNh-R4ruuwZahKg
https://mp.weixin.qq.com/s/VXyMS4UHKOHbW2qfffIoQA