今天我们通过mysql日志了解mysqld的错误日志、慢查询日志、二进制日志,redolog, undolog等。揭示它们的作用和用途,让我们工作中更能驾驭mysql。
redo 日志
如果mysql事务提交后发生了宕机现象,那怎么保证数据的持久性与完整性?
mysql 提供了 redo 来防止 数据丢失。
什么是 redo 日志
redo 日志(也叫重做日志)是一种基于磁盘的数据结构,用于记录事务操作变化,记录的是数据被修改之后的值,在崩溃恢复后恢复事务写入的数据。特别注意:redo 日志是inndb 引擎独有的一种日志
mysql 读取数据不是一条一条读取的,而是会加载 硬盘中的一页数据放到 缓冲区中,后续读取会先看缓冲区是否命中,然后决定是否在冲磁盘。在redo 日志里,也是会有缓冲区的概念,下面我们看看 redo 日志是由哪几个组成的?
组成
- 内存中的重做日志缓冲(redo log buffer)
- 重做日志文件(redo log file)
有日志缓冲区,有磁盘文件,那么他们是怎么更新的呢,下面我们就看看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:事务提交时,在事务提交时,每秒触发一次 redo log buffer 写磁盘操作,并调用操作系统 fsync 刷新 IO 缓存。
- innodb_flush_log_at_trx_commit=1:事务提交时,InnoDB 立即将缓存中的 redo 日志写到日志文件中,并调用操作系统 fsync 刷新 IO 缓存;
- innodb_flush_log_at_trx_commit=2:事务提交时,InnoDB 立即将缓存中的 redo 日志写到日志文件中,但不是马上调用 fsync 刷新 IO 缓存,而是每秒只做一次磁盘 IO 缓存刷新操作
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: 如果操作系统奔溃,最多有一秒的数据丢失。
为什么需要 redo
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 文件怎么样的
从上面配置可以看出,redo日志不是一个文件出现的,而是以一个文件组出现的。他是一一个环形,从头开始写,写完又从头开始写:
在个日志文件组中还有两个重要的属性,分别是 write pos、checkpoint
- write pos 是当前记录的位置,一边写一边后移
- checkpoint 是当前要擦除的位置,也是往后推移
额外补充
- 预写日志方式(WAL): 数据落盘前,需要先写日志,比如说当一个事务开始时,会记录该事物的LSN(日志序列号),当事务执行的时候,会往日志缓存中插入事务日志,提交的时候将日志缓存落盘, 这种方式称为预写日志方式
- checkpoint: checkpoint是为了定期将db buffer的内容刷新到data file。当遇到内存不足、db buffer已满等情况时,需要将db buffer中的内容/部分内容(特别是脏数据)转储到data file中。在转储时,会记录checkpoint发生的”时刻“。在故障回复时候,只需要redo/undo最近的一次checkpoint之后的操作
- 以下场景出发redo 日志 写文件:
3.1 Redo log buffer空间不足时
3.2 事务commit
3.3 mysql 重启
3.4 binlog切换时
binlog
什么是binlog
binlog是数据库Server层(和引擎无关),它记录了所有的 DDL(数据定义语句)和 DML(数据操纵语句),但是不包括 select 和 show 操作(可以看常规查询日志)。
作用
- 恢复:某些数据的恢复需要二进制日志,如当一个数据库全备文件恢复后,我们可以通过二进制日志进行point-in-time的恢复
- 复制:通过复制和执行二进制日志使的一台远程的mysql数据库(salve)与一台mysql数据库(master)进行实时同步
数据类型
- Statement(Statement-Based Replication,SBR):每一条会修改数据的 SQL 都会记录在 binlog 中。
- Row(Row-Based Replication,RBR):不记录 SQL 语句上下文信息,仅保存哪条记录被修改。
- Mixed(Mixed-Based Replication,MBR):Statement 和 Row 的混合体。
优点 | 缺点 | |
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 的设定而记录 | 需要判断使用使用那种模式,需要判断使用哪种格式,所以更慢。 |
启动binlog
二进制日志文件默认的情况下是没有启动的,我们需要手动配置log-bin[=name]进行启动二进制日志。如果我们不指定name,则默认二进制日志文件名为主机名,后缀名为二进制日志的序列号。虽然开启二进制日志会对mysql数据库性能有所影响,但是这个影响是有限的,相对于可以使用复制和point-in-time的恢复,这些性能可以接受。
查看当前是否开启
mysql> SHOW VARIABLES LIKE 'log_bin';
+---------------+-------+
| Variable_name | Value |
+---------------+-------+
| log_bin | OFF |
+---------------+-------+
注意:
- 根据mysql官方手册的测试表名,开启二进制日志会使得性能下降1%
- SHOW VARIABLES LIKE "datadir" 可以查看数据库所在目录
- 写入二进制日志的语句中的密码由服务器重写,不会以纯文本形式出现
- 5.7 启动的时候报错:You have enabled the binary log, but you haven't provided the mandatory server-id. 在配置log-bin同时加一个 server-id
binlog配置
- max_binlog_size: 单个二进制日志文件的最大值(mysql5.0开始默认1G),超过该值就产生新的日志文件,后缀名+1,并记录到.index文件
- binlog_cache_size: 当一个线程开始一个事务的时候,mysql会自动分配一个大小为binlog_cache_size(默认32KB)的缓存,等到事务提交的时候直接将缓冲中的二进制日志写入二进制日志文件中。另外如果事务记录大于binlog_cache_size时,mysql会将缓冲中的日志写入一个临时文件中。为了更好的设置binlog_cache_size值的大小,可以通过SHOW GLOBAL STATUS查看binlog_cache_use, binlog_cache_disk_use来判断当前的的设置是否合理
- binlog_cache_use: 记录使用缓冲写二进制日志的次数
- binlog_cache_disk_use: 记录了使用临时文件写二进制日志的次数
- sync_binlog: 表示每写缓冲多少次就同步到磁盘中:
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解决。
- binlog-do-db: 需要写入哪些库的日志
- binlog-ignore-db: 忽略写入哪些库的日志
- binlog_format: 记录二进制日志的格式(5.1版本后引入)
redo 日志和 binlog日志的区别
- redolog 在InnoDB引擎中才有,binlog 是MySQL的服务器层实现的(任何储存引擎都有binlog)
- Redo日志主要用于崩溃恢复过程,保证MySQL宕机也不会影响持久性。Binlog日志主要用于数据恢复、主从复制和数据同步
- redo log 是物理日志,内容基于磁盘的页Page;bin log的内容是二进制,可以根据binlog_format参数自行设置。
- redo log在事务开始时即开始写入并 采用循环写的方式记录;binlog 在事务提交时写入并通过追加的方式记录,当文件大小大于给定值后,后续的日志会记录到新的文件上。
- redo日志能维持crash-safe 能力
undo log
什么是undo 日志
Undo: 逻辑日志(回滚日志),将数据库逻辑地恢复到原来的样子,所有修改都被逻辑地取消了。Undo存在数据库内部的一个特殊的段(undo 段)中,undo段位于共享表空间内。undo 日志保证事务的原子性,也就是事务中的操作要么全部完成,要么什么也不做。
原理
undo日志一般被称为回滚日志,一般执行ROLLBACK时,会将数据恢复到事务开始的状态。注意我们这里并不是将数据库物理的恢复到执行语句或事务之前的样子,而是我们操作一步就会生成一个相反的操作放入undo 段中。比如说:当我们insert一条记录的时候,那么undo段中也会生成对应delete 一条记录。
作用
- 实现MVCC::隐藏字段、Read View、undo log。在内部实现中,InnoDB 通过数据行的 DB_TRX_ID 和 Read View 来判断数据的可见性,如不可见,则通过数据行的 DB_ROLL_PTR 找到 undo log 中的历史版本。
- 回滚数据:undo log记录了每个操作的逆操作,可以逻辑恢复数据
undo 类型
InnoDB存储引擎中Undo Log可以分为以下两种类型:
- insert Undo Log
insert Undo Log是指在insert操作中产生的Undo Log。事务具有隔离的特性,所以insert操作的记录,只对事务本身可见,故该Undo Log可以在事务提交后直接删除。 - update Undo Log
update Undo Log记录的是对delete和update操作产生的Undo Log。该Undo Log可能需要提供MVCC机制,因此不能在事务提交时就进行删除。提交时放入Undo Log链表,等待purge线程进行最后的删除。
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;
查看慢查询日志
- 可以通过mysqldumpslow查看,比如说查看时间最长的10条sql语句: mysqldumpslow -s al -n 10 日志文件
- 我们可以通过设置慢查询输出到表中。通过SHOW VARIABLES LIKE "log_output" 查看当前是输入file还是表,通过 set global log_output ="TABLE"设置输入表中查看
通用查询日志
什么是通用查询日志
通用查询日志是用户所做事情的所有记录。当客户端连接或断开连接时,服务器将信息写入此日志,并记录从客户端接收到的每个 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语句,记录这个逻辑操作
物理日志就是具体到某个磁盘页