今天我们通过mysql日志了解mysqld的错误日志、慢查询日志、二进制日志,redolog, undolog等。揭示它们的作用和用途,让我们工作中更能驾驭mysql。
如果mysql事务提交后发生了宕机现象,那怎么保证数据的持久性与完整性?
mysql 提供了 redo 来防止 数据丢失。
redo 日志(也叫重做日志)是一种基于磁盘的数据结构,用于记录事务操作变化,记录的是数据被修改之后的值,在崩溃恢复后恢复事务写入的数据。特别注意:redo 日志是inndb 引擎独有的一种日志
mysql 读取数据不是一条一条读取的,而是会加载 硬盘中的一页数据放到 缓冲区中,后续读取会先看缓冲区是否命中,然后决定是否在冲磁盘。在redo 日志里,也是会有缓冲区的概念,下面我们看看 redo 日志是由哪几个组成的?
有日志缓冲区,有磁盘文件,那么他们是怎么更新的呢,下面我们就看看redo 日志的刷盘策略
每次数据更新会先更新 redo log buffer,然后根据 innodb_flush_log_at_trx_commit 来控制 redo log buffer 更新到 redo log file 的时机。innodb_flush_log_at_trx_commit 有三个值可选:
innodb_flush_log_at_trx_commit=0:如果数据库奔溃,有一秒的数据丢失。
innodb_flush_log_at_trx_commit=1: InnoDB的默认配置,为的是保证事务ACID特性。
innodb_flush_log_at_trx_commit=2: 如果操作系统奔溃,最多有一秒的数据丢失。
1- 如果刷脏页还未完成,MySQL这时候因为某些原因宕机,重启后 Buffer Pool中修改的数据还没有及时的刷到磁盘中,就会导致数据丢失,无法保证事务的持久性。
为redo 就可以解决这个问题:redo 记录的是数据库中每个页的修改,而不是某一行修改成怎样.。 这样就可以用来恢复提交后的数据(物理数据页)且只能恢复到最后一次提交的位置。
这样再修改数据时,InnoDB引擎会把更新记录先写在redo log中。在修改Buffer Pool中的数据,当提交事务时,调用fsync把redo log刷入磁盘。
2- 保证事务的持久性:对于一个已经提交的事务,在事务提交后即使系统发生了崩溃,这个事务对数据库中所做的更改也不能丢失。
我很重要:
1- 这里redo 日志用到了 WAL(Write-Ahead Logging)技术,这个技术的核心就在于修改记录前,一定要先写日志,并保证日志先落盘,才能算事务提交完成。
2- innodb 因为 redo 日志 具有了crash-safe 的能力( MySQL宕机,重启后会自动去检查redo log,将修改还未写入磁盘的数据从 redo log 恢复到MySQL中)
innodb_log_group_home_dir :定义InnoDB日志文件的目录路径。如果未配置此选项, InnoDB则会在 MySQL 数据目录 ( datadir) 中创建日志文件。
innodb_log_file_size:要更改日志文件大小
innodb_log_files_in_group:要增加日志文件的数量,默认和推荐值为 2。
从上面配置可以看出,redo日志不是一个文件出现的,而是以一个文件组出现的。他是一一个环形,从头开始写,写完又从头开始写:
在个日志文件组中还有两个重要的属性,分别是 write pos、checkpoint
3.1 Redo log buffer空间不足时
3.2 事务commit
3.3 mysql 重启
3.4 binlog切换时
binlog是数据库Server层(和引擎无关),它记录了所有的 DDL(数据定义语句)和 DML(数据操纵语句),但是不包括 select 和 show 操作(可以看常规查询日志)。
优点 |
缺点 |
|
Statement |
1- 只需要记录执行sql ,避免了记录每一行的变化,相较于row 能大大减少binlog日志量,节约IO,提高性能 2- 实时的还原 3- 主从版本可以不一样,从服务器版本可以比主服务器版本高 |
如果sql中包含函数,可能会出现执行结果不一致。这样就会导致数据不一致(主从,备份等等) |
row |
清晰的记录每行数据的修改细节,任何情况都可以被复制且能加快从库重放日志的效率,保证从库数据的一致性 |
日志量太大了:特别是批量 update、整表 delete、alter 表等操作,由于要记录每一行数据的变化,此时会产生大量的日志,大量的日志也会带来 IO 性能问题。 此外,新版的MySQL中对row级别也做了一些优化,当表结构发生变化的时候,会记录语句而不是逐行 |
Mixed |
Mixed level对以上两种类型的结合。不过,新版本的MySQL对row level模式也被做了优化: 1- 并不是所有的修改都会以row 格式记录,如果遇到表结构变更的时候就会以statement模式来记录; 2- 如果sql语句确实就是update或者delete等修改数据的语句,那么还是会记录所有行的变更;因此,现在一般使用row level即可 3- 选取规则如果是采用 INSERT,UPDATE,DELETE 直接操作表的情况,则日志格式根据 binlog_format 的设定而记录 |
需要判断使用使用那种模式,需要判断使用哪种格式,所以更慢。 |
二进制日志文件默认的情况下是没有启动的,我们需要手动配置log-bin[=name]进行启动二进制日志。如果我们不指定name,则默认二进制日志文件名为主机名,后缀名为二进制日志的序列号。虽然开启二进制日志会对mysql数据库性能有所影响,但是这个影响是有限的,相对于可以使用复制和point-in-time的恢复,这些性能可以接受。
查看当前是否开启
mysql> SHOW VARIABLES LIKE 'log_bin';
+---------------+-------+
| Variable_name | Value |
+---------------+-------+
| log_bin | OFF |
+---------------+-------+
注意:
sync_binlog=0:表示每次提交事务都只write,不fsync
sync_binlog=1:表示每次提交事务都会执行fsync;
sync_binlog=N(N>1):表示每次提交事务都write,累积N个事务后才fsync 。
注意:当sync_binlog=1时,使用innodb存储引擎时,在一个事务发出commit动作之前,由于sync_binlog=1,因此会立即将二进制日志文件写入磁盘。如果这时已经写入二进制日志,但没有提交,并且此时发生宕机,那么下次数据库启动时候,因commit操作没有发生,所以这个事务会被回滚,但是二进制日志已经记录了该事务信息,不能回滚。这个问题可以通过将参数innodb_support_xa设为1解决。
Undo: 逻辑日志(回滚日志),将数据库逻辑地恢复到原来的样子,所有修改都被逻辑地取消了。Undo存在数据库内部的一个特殊的段(undo 段)中,undo段位于共享表空间内。undo 日志保证事务的原子性,也就是事务中的操作要么全部完成,要么什么也不做。
undo日志一般被称为回滚日志,一般执行ROLLBACK时,会将数据恢复到事务开始的状态。注意我们这里并不是将数据库物理的恢复到执行语句或事务之前的样子,而是我们操作一步就会生成一个相反的操作放入undo 段中。比如说:当我们insert一条记录的时候,那么undo段中也会生成对应delete 一条记录。
InnoDB存储引擎中Undo Log可以分为以下两种类型:
innodb_max_undo_log_size:Undo日志文件的最大值,默认1GB,初始化大小10M
innodb_undo_log_truncate:标识是否开启自动收缩Undo Log表空间的操作
innodb_undo_tablespaces:设置独立表空间的个数,默认为0,标识不开启独立表空间,Undo日志保存在ibdata1中
innodb_undo_directory:Undo日志存储的目录位置 innodb_undo_logs: 回滚的个数 默认128
执行时间超过 long_query_time 且至少需要 min_examined_row_limit(默认是0) 检查行的 SQL 语句组成的日志文件,这个日志文件就叫做慢查询日志。
慢查询日志的主要作用:帮助我们发现那些特别耗时SQL,让我们有针对性地进行优化,从而提高系统的整体效率。如果发现数据库服务器发生阻塞、运行变慢的时候,检查一下慢查询日志,找到那些慢查询,对解决问题很有帮助(一般需要结合explain进行全面分析)。
默认情况下,MySQL数据库没有开启慢查询日志,需要我们手动来设置这个参数。如果不是调优需要的话,一般不建议启动该参数,因为开启慢查询日志会或多或少带来一定的性能影响慢查询日志支持将日志记录写入文件。
查看慢日志相关配置
# 查看慢SQL是否开启
show variables like "slow_query_log%";
# 查看慢查询设定的阈值 单位:秒
show variables like "long_query_time";
# 慢日志开关: 0为关闭,1为开启
set global slow_query_log='ON';
# 日志文件
set global slow_query_log_file='/xxx/xxx/xxx/host_name-slow.log';
# 设置慢日志阈值的时间
set global long_query_time=2;
也可以在/etc/my.cnf配置
slow_query_log = ON
slow_query_log_file = /xx/xx/xx/host_name-slow.log
# 定义SQL的执行时间阈值(单位为秒),默认是10,最小值为0,可以指定0.1表示100ms。
long_query_time = 2
# 输出(有效值:TABLE|FILE|NONE)
log_output = "FILE"
# 查询扫描过的最少记录数
min_examined_row_limit = 0
# 该参数决定是否记录未使用索引的SQL
log_queries_not_using_indexes = OFF
# 该参数决定是否记录管理类的命令:ALTER TABLE,ANALYZE TABLE、CHECK TABLE、CREATE INDEX、DROP INDEX、OPTIMIZE TABLE,REPAIR TABLE
log_slow_admin_statements = OFF # 默认是不记录这一类语句到慢日志
# 该参数决定每分钟记录未使用索引的SQL的数量上限,因为未使用索引的SQL可能会非常多,导致慢日志空间增长飞快。
log_throttle_queries_not_using_indexes = 0
# 该参数在从库上设置,决定是否记录在复制过程中超过long_query_time的SQL,如果binlog格式是row,则即使开启了该参数,也不会记录相关SQL。
log_slow_slave_statements = OFF
服务器按以下顺序使用控制参数来确定是否将查询写入慢查询日志:
1. 管理类语句不会记录,除非开启了log_slow_admin_statements;
2. 执行时间需要超过long_query_time,或者log_queries_not_using_indexes 开启的,并且记录数量在log_throttle_queries_not_using_indexes之下;
3. SQL需要读取数据行数超过min_examined_row_limit;
4. 从库的复制语句默认不记录,除非binlog格式是statement且开启log_slow_slave_statements;
通用查询日志是用户所做事情的所有记录。当客户端连接或断开连接时,服务器将信息写入此日志,并记录从客户端接收到的每个 SQL 语句。当你怀疑客户端中存在错误并想要准确了解客户端发送到mysqld的内容时,常规查询日志可能非常有用。
SHOW VARIABLES LIKE '%general%';
返回结果
general_log: 日志开关
general_log_file: 日志文件(默认名称为 host_name.log)
刚在我们在客户端执行了 SHOW VARIABLES LIKE '%general%'; , 打开日志可以看到这条记录
错误日志(Error Log) 是 MySQL 中最常用的一种日志,主要记录 MySQL 服务器启动和停止过程中的信息。它还包含服务器启动和关闭期间以及服务器运行期间发生的错误、警告和注释等诊断消息。例如,如果mysqld注意到需要自动检查或修复某个表,它就会向错误日志写入一条消息。
错误日志默认是开启的,没有关闭这个操作。可以通过 show variables like 'log_err%'; 查看 错误日志配置,默认是stderr, 我们需要去/etc/my.cnf 进行配置 log_error = '文件目录' 。
log_error_verbosity日志记录等级:
log_error_verbosity Value |
Permitted Message Priorities |
1 |
ERROR |
2 |
ERROR, WARNING |
3 |
ERROR, WARNING, INFORMATION |
log_timestamps控制写入错误日志(以及一般查询日志和慢查询日志文件)的消息中时间戳的时区。
允许的log_timestamps值为UTC(默认值)和 SYSTEM(本地系统时区)。时间戳使用 ISO 8601 / RFC 3339 格式编写: 加上表示 Zulu 时间 (UTC) 的尾值或(指示相对于 UTC 的本地系统时区调整的偏移量)。例如: YYYY-MM-DDThh:mm:ss.uuuuuuZ±hh:mm
2020-08-07T15:02:00.832521Z (UTC)
2020-08-07T10:02:00.832521-05:00 (SYSTEM)
# my.cnf 配置
[mysqld]
log_timestamps=system
错误日志文件刷新和重命名
可以将这些错误日志删除,以保证 MySQL 服务器上的硬盘空间。MySQL 的错误日志是以文本文件的形式存储在文件系统中的,可以直接删除。
mysqladmin -uroot -p flush-logs
中继日志(relay log)只在主从服务器架构的从服务器上存在。从服务器为了与主服务器保持一致,要从主服务器读取二进制日志的内容,并且把读取到的信息写入本地的日志文件中,这个从服务器本地的日志文件就叫中继日志。然后从服务器读取中继日志,并根据中继日志的内容对从服务器的数据进行更新,完成主从服务器的数据同步。
1- 脏页:当内存数据页跟磁盘数据页内容不一致的时候,我们称这个内存页为“脏页”。
2- 干净页:内存数据写入到磁盘后,内存和磁盘上的数据页的内容就一致了,称为“干净页”。
3- LSN:称为日志的逻辑序列号(log sequence number),在innodb存储引擎中,lsn占用8个字节。LSN的值会随着日志的写入而逐渐增大。事务中更新操作会产生一个新的LSN。LSN不仅存在于redo log中,还存在于数据页中。
4- 物理日志和逻辑日志
逻辑日志就是sql语句,记录这个逻辑操作
物理日志就是具体到某个磁盘页