关于InnoDB底层原理与MySQL日志机制

关于InnoDB底层原理与MySQL日志机制

  • 一、MySQL内部组件结构图解
  • 二、Server层
  • 三、存储引擎层
    • 1、连接器
    • 2、查询缓存
    • 3、分析器
      • 1、分析器先进行“词法分析”
      • 2、之后分析器进行“语法分析”
    • 4、优化器
    • 5、执行器
  • 四、InnoDB底层原理与MySQL日志机制
    • 1、redo log重做日志关键参数
    • 2、redo log 写入磁盘过程分析
    • 3、binlog二进制归档日志
      • binlog 的日志格式
      • binlog写入磁盘机制
      • 删除 binlog 日志文件
      • 查看 binlog 日志文件
      • binlog日志文件恢复数据
  • 现在需要恢复被删除的两条数据,我们先查看binlog日志文件
      • 面试题:为什么会有 redo log 和 binlog 两份日志呢?
    • 4、undo log回滚日志
      • undo log 日志删除时机:
      • MySQL 不直接更新磁盘上的数据,而是设置一套复杂的机制来执行SQL:
    • 5、错误日志
    • 6、通用查询日志

一、MySQL内部组件结构图解

关于InnoDB底层原理与MySQL日志机制_第1张图片
MySQL 大体可以分为 Server 层和存储引擎层两部分。

二、Server层

主要包括连接器、查询缓存、词法分析器、优化器、执行器等,涵盖 MySQL 的大多数核心服务功能,以及所有的内置函数(如日期、时间、数学和加密函数等),所有跨存储引擎的功能都在这一层实现,例如存储过程、触发器、视图等。

三、存储引擎层

存储引擎层负责数据的存储和提取。其架构模式是插件式的,支持 InnoDB、MyISAM、Memory 等多个存储引擎。

最常用 InnoDB,从 MySQL 5.5.5 版本开始成为默认存储引擎。即在 create table 时,若不指定表的存储引擎类型,则默认设置为 InnoDB。

1、连接器

MySQL 是开源的,因此有非常多种类的客户端:Navicat、MySQL Front、JDBC、SQLyog 等,包括各种编程语言实现的客户端连接程序,这些客户端若要向 MySQL 发起通信都必须先跟 Server 端建立通信连接,而建立连接的工作就是由连接器来完成。

首先在连接到数据库时,会由连接器负责跟客户端建立连接、获取权限、维持和管理连接。

连接命令:

 [root@autumn ~]# mysql -h host[数据库地址] -u root[用户] -p root[密码] -P 3306 

连接命令中的 mysql 是客户端工具,用来跟服务端建立连接。
在完成经典的 TCP 握手后,连接器则开始认证身份,即验证输入的用户名和密码。

1、若用户名或密码不对,则返回 “Access denied for user” 的报错,客户端程序结束执行。
2、若用户名密码认证通过,连接器将通过权限表查出当前用户对应的权限。之后此连接中的权限判断逻辑,都将依赖于此时读到的权限。

一个用户成功建立连接后,即使通过管理员账号对此用户的权限做了修改,也不会影响当前已经存在连接的权限。修改完成后,只有在新建的连接中才会使用新的权限设置。

2、查询缓存

连接建立完成后,便可执行 select 语句。执行逻辑将进行下一步,即查询缓存。

MySQL 获取到一个查询请求后,会先使用查询缓存查询,是否执行过此条语句。之前执行过的语句及其结果可能会以 key-value 的形式,直接缓存于内存中。key 是查询的语句,value 是查询的结果。

  • 若查询能直接在此缓存中找到 key,则此时对应的 value 会直接返回给客户端。
  • 若语句不在查询缓存中,就会继续后面的执行阶段。执行完成后,执行结果会被存入查询缓存中。此时若查询命中缓存,MySQL 不需要执行后面的复杂操作,就可以直接返回结果,因此效率很高。

大多数情况不使用查询缓存:
因为查询缓存往往弊大于利。查询缓存的失效非常频繁,只要出现一个表的更新,此表上所有的查询缓存都会被清空。因此很可能好不容易缓存好结果,但还没来得及使用便被一个更新全清空掉。并且对于更新压力大的数据库来说,查询缓存的命中率也会非常低。

因此建议在静态表里使用查询缓存,即极少更新的表。例如系统配置表、字典表等,这类表上的查询才适合使用查询缓存。MySQL 也提供了这种“按需使用”的方式,可将 my.cnf 的参数 query_cache_type 设置成 DEMAND。

my.cnf 
#query_cache_type有3个值 
#	0代表关闭查询缓存OFF,
# 1代表开启ON,
# 2(DEMAND)代表当sql语句中有SQL_CACHE关键词时才缓存 
query_cache_type=2 

此设置对于默认的 SQL 语句都不使用查询缓存。若指定需要使用查询缓存,则 SQL 语句使用 SQL_CACHE 显式地指定:

select SQL_CACHE * from test where ID=5

查看当前 MySQL 实例是否开启缓存机制:

show global variables like "%query_cache_type%"; 

MySQL 8.0 已经移除了查询缓存功能

3、分析器

若没有命中查询缓存,则需开始真正执行语句。

MySQL 会对 SQL 语句做解析:

1、分析器先进行“词法分析”

输入内容是由多个字符串和空格组成的一条 SQL 语句,MySQL 需要识别出里面的字符串分别是什么,代表什么。

MySQL 从输入的 “select” 这个关键字识别出来,其为一个查询语句。然后将字符串“T”识别成“表名 T”,将字符串“ID”识别成“列 ID”。

2、之后分析器进行“语法分析”

根据词法分析的结果,语法分析器会根据语法规则,判断输入的此 SQL 语句是否满足 MySQL 语法。
若语句不对,则会返回“You have an error in your SQL syntax”的错误提示,例如下方例句中 from 写成了 “rom”:

select * fro test where id=1; 
ERROR 1064 (42000): You have an error in your SQL syntax; 
check the manual that corresponds to your MySQL server version for the right syntax to use near 'fro test where id=1' at line 1 

分析器对 SQL 的分析过程步骤:
关于InnoDB底层原理与MySQL日志机制_第2张图片
SQL 语句经过分析器分析之后生成语法树:
关于InnoDB底层原理与MySQL日志机制_第3张图片
分析器的工作任务到此结束,接着进行优化器处理。

4、优化器

  • 在表里面有多个索引时,优化器决定使用哪个索引;
  • 在一个语句有多表关联(join)时,优化器决定各个表的连接顺序;
  • 优化器还处理一些 MySQL 自身内部的优化机制。

5、执行器

执行之前会先判断是否拥有执行查询此表 T 的权限,若没有,则返回无权限的报错 (在工程实现上,若命中查询缓存,则会在查询缓存返回结果时做权限验证)。

select * from test where id=10; 

若有权限,则打开表继续执行。打开表时,执行器就会根据表的引擎定义,去使用这个引擎提供的接口。

四、InnoDB底层原理与MySQL日志机制

关于InnoDB底层原理与MySQL日志机制_第4张图片

1、redo log重做日志关键参数

innodb_log_buffer_size:设置 redo log buffer 大小参数,默认16M ,最大值是 4096M,最小值为 1M。

show variables like '%innodb_log_buffer_size%'; 

innodb_log_group_home_dir:设置 redo log 文件存储位置参数,默认值为"./",即 InnoDB 数据文件存储位置,其中的 ib_logfile0 和 ib_logfile1 即为 redo log 文件。

show variables like '%innodb_log_group_home_dir%'; 

image.png
innodb_log_files_in_group:设置 redo log 文件的个数,命名方式如:ib_logfile0, iblogfile1… iblogfileN。默认 2 个,最大 100 个。

show variables like '%innodb_log_files_in_group%'; 

innodb_log_file_size:设置单个 redo log 文件大小,默认值为 48M。

redo log 总和最大值为 512G,即整个 redo log 系列文件之和,因此 (innodb_log_files_in_group * innodb_log_file_size) 不能大于最大值 512G。

show variables like '%innodb_log_file_size%'; 

2、redo log 写入磁盘过程分析

redo log 从头开始写,写完一个文件继续写另一个文件,写到最后一个文件末尾就又回到第一个文件开头循环写,如下面这个图所示。
关于InnoDB底层原理与MySQL日志机制_第5张图片
write pos:当前记录的位置,一边写一边后移,写到第 3 号文件末尾后就回到 0 号文件开头。

checkpoint:当前要擦除的位置,也是往后推移并且循环的,擦除记录前要把记录更新到数据文件里。

write pos 和 checkpoint 之间的部分就是空着的可写部分,可以用来记录新的操作。若 write pos 追上 checkpoint,表示 redo log 写满了,此时不能再执行新的更新,需停下先擦掉一些记录,将 checkpoint 推进一下。

innodb_flush_log_at_trx_commit:控制 redo log 的写入策略,三种取值:

  • 设置为 0:表示每次事务提交时都只是把 redo log 留在 redo log buffer 中,数据库宕机可能会丢失数据。
  • 设置为 1(默认值):表示每次事务提交时都将 redo log 直接持久化到磁盘,数据最安全,不会因为数据库宕机丢失数据,但是效率略差,线上系统推荐此设置。
  • 设置为 2:表示每次事务提交时都只是把 redo log 写到操作系统的缓存 page cache 里,这种情况若数据库宕机并不会丢失数据;但若操作系统宕机,page cache 里未写入磁盘文件的数据则会丢失。

InnoDB 有一个后台线程,每隔 1 秒,就会把 redo log buffer 中的日志,调用操作系统函数 write 写入文件系统的 page cache,再调用操作系统函数 fsync 持久化到磁盘文件。

redo log写入策略参看下图:
关于InnoDB底层原理与MySQL日志机制_第6张图片

# 查看innodb_flush_log_at_trx_commit参数值: 
show variables like 'innodb_flush_log_at_trx_commit'; 
# 设置innodb_flush_log_at_trx_commit参数值(也可以在my.ini或my.cnf文件里配置): 
set global innodb_flush_log_at_trx_commit=1; 

3、binlog二进制归档日志

binlog 二进制日志记录保存了所有执行过的修改操作语句,但不保存查询操作。若 MySQL 服务意外停止,可通过二进制日志文件排查,用户操作或表结构操作,从而恢复数据库数据。

启动 binlog 记录功能,会影响服务器性能,但若需要恢复数据或主从复制功能,其好处则大于对服务器的影响。

# 查看binlog相关参数 
show variables like '%log_bin%'; 

关于InnoDB底层原理与MySQL日志机制_第7张图片
MySQL 5.7 版本中,binlog 默认关闭;8.0 版本默认开启。上图中 log_bin 的值为 OFF 即表示 binlog 为关闭状态;若要打开 binlog 功能,则需修改配置文件 my.ini(Windows)或 my.cnf(Linux),再重启数据库。

在配置文件中的 [mysqld] 部分,增加如下配置:

# log-bin设置binlog的存放位置,可以是绝对路径,也可以是相对路径;
# 此处为相对路径,则 binlog 文件默认会放在 data 数据目录下 
log-bin=mysql-binlog 
# Server Id 是数据库服务器 id,随便写一个数即可,此id 用来在 MySQL 集群环境中标记唯一 MySQL 服务器;
# 集群环境中每台 MySQL 服务器的 id 必须不同,否则启动报错 
server-id=1 
# 其他配置 
binlog_format = row # 日志文件格式
expire_logs_days = 15 # 执行自动删除距离当前 15 天以前的 binlog 日志文件的天数, 默认为0, 表示不自动删除 
max_binlog_size = 200M # 单个 binlog 日志文件的大小限制,默认为 1GB 

重启数据库后再看 data 数据目录则会多出两个文件,第一个为 binlog 日志文件,第二个为 binlog 文件的索引文件,此文件管理了所有的 binlog 文件的目录。
关于InnoDB底层原理与MySQL日志机制_第8张图片
查看 binlog 文件数量的执行命令:

show binary logs; 
show variables like '%log_bin%'; 

关于InnoDB底层原理与MySQL日志机制_第9张图片

log_bin:binlog 日志是否打开的状态 
log_bin_basename:binlog 日志的基本文件名,后面追加标识来表示每一个文件,binlog 日志文件会滚动增加 
log_bin_index:指定 binlog 文件的索引文件,这个文件管理了所有的 binlog 文件的目录。 
sql_log_bin:SQL 语句是否需要写入 binlog 文件,ON 代表需要写入,OFF 代表不需要写入。
若要在主库上执行一些操作,但不复制到 slave 库上,可通过修改参数 sql_log_bin 来实现。使用场景例如模拟主从同步复制异常。 

binlog 的日志格式

用参数 binlog_format 可设置 binlog 日志的记录格式,MySQL 支持三种格式类型:

  • STATEMENT:基于 SQL 语句的复制,每条涉及修改数据的 SQL 都会记录到 master 机器的 bin-log 中,此方式日志量小,节约 IO 开销,提高性能,但对于一些执行过程中才能确定结果的函数,例如 UUID()、SYSDATE() 等,若随 SQL 同步到 slave 机器去执行,则结果会跟 master 机器执行的不一样。
  • ROW:基于行的复制,日志中会记录成每一行数据被修改的形式,然后在 slave 端再对相同的数据进行修改,记录下每一行数据修改的细节,可解决函数、存储过程等在 slave 机器的复制问题,但此方式日志量较大,性能不如 STATEMENT。例如,假设 update 语句更新 10 行数据,STATEMENT 方式只记录此条 update 语句,而 Row 方式则会记录被修改的 10 行数据。
  • MIXED:混合模式复制,实际就是前两种模式的结合,在Mixed模式下,MySQL 会根据执行的每条具体的 SQL 语句来区分对待记录的日志形式,即在 STATEMENT 和 ROW 之间选择一种,若 SQL 中有函数或一些在执行时才知道结果的情况,则会选择 ROW,其它情况则选择 STATEMENT,因此推荐使用此种方式。

binlog写入磁盘机制

binlog 写入磁盘机制主要通过 sync_binlog 参数控制,默认值是 0。

  • 为 0 时:表示每次提交事务都只 write 到 page cache,由系统自行判断什么时候执行 fsync 写入磁盘。虽然性能得到提升,但是机器宕机,page cache 中的 binlog 便会丢失。
  • 设置为 1:表示每次提交事务都会执行 fsync 写入磁盘,此种方式最安全。
  • 折中方式:设置为 N(N>1),表示每次提交事务都 write 到 page cache,但累积 N 个事务后才 fsync 写入磁盘,此种方式若机器宕机则会丢失 N 个事务的 binlog。

触发 binlog 日志文件重新生成的条件:

  • 服务器启动或重新启动
  • 服务器刷新日志,执行命令 flush logs
  • 日志文件大小达到 max_binlog_size 值,默认值为 1GB

删除 binlog 日志文件

# 删除当前的 binlog 文件 
reset master; 
# 删除指定日志文件之前的所有日志文件,以下指令为删除 6 之前的所有日志文件,为 6 的文件不删除 
purge master logs to 'mysql-binlog.000006'; 
# 删除指定日期前的日志索引中 binlog 日志文件 
purge master logs before '2023-01-21 14:00:00'; 

查看 binlog 日志文件

可用 MySQL 自带的命令工具 mysqlbinlog 查看 binlog 日志内容:

# 查看bin-log二进制文件(命令行方式,不用登录mysql) 
mysqlbinlog --no-defaults -v --base64-output=decode-rows D:/dev/mysql-5.7.25-winx64/data/mysql-binlog.000007 

# 查看bin-log二进制文件(带查询条件) 
mysqlbinlog --no-defaults -v --base64-output=decode-rows D:/dev/mysql-5.7.25-winx64/data/mysql-binlog.000007 start-datetime="2023-01-21 00:00:00" stop-datetime="2023-02-01 00:00:00" start-position="5000" stop-position="20000" 

执行 mysqlbinlog 命令:

mysqlbinlog --no-defaults -v --base64-output=decode-rows D:/dev/mysql-5.7.25-winx64/data/mysql-binlog.000007

查出来的binlog日志文件内容如下:

/*!50530 SET @@SESSION.PSEUDO_SLAVE_MODE=1*/; 
/*!50003 SET @OLD_COMPLETION_TYPE=@@COMPLETION_TYPE,COMPLETION_TYPE=0*/; 
DELIMITER /*!*/; 
# at 4 
#230127 21:13:51 server id 1 end_log_pos 123 CRC32 0x084f390f Start: binlog v 4, server v 5.7.25-log created 230127 21:13:51 at startup 
# Warning: this binlog is either in use or was not closed properly. 
ROLLBACK/*!*/; 
# at 123 
#230127 21:13:51 server id 1 end_log_pos 154 CRC32 0x672ba207 Previous-GTIDs 
# [empty] 
# at 154 
#230127 21:22:48 server id 1 end_log_pos 219 CRC32 0x8349d010 Anonymous_GTID last_committed=0 sequence_number=1 rbr_only=yes 
/*!50718 SET TRANSACTION ISOLATION LEVEL READ COMMITTED*//*!*/; 
SET @@SESSION.GTID_NEXT= 'ANONYMOUS'/*!*/; 
# at 219 
#230127 21:22:48 server id 1 end_log_pos 291 CRC32 0xbf49de02 Query thread_id=3 exec_time=0 error_code=0 
SET TIMESTAMP=1674825768/*!*/; 
SET @@session.pseudo_thread_id=3/*!*/; 
SET @@session.foreign_key_checks=1, @@session.sql_auto_is_null=0, @@session.unique_checks=1, @@session.autocommit=1/*!*/; 
SET @@session.sql_mode=1342177280/*!*/; 
SET @@session.auto_increment_increment=1, @@session.auto_increment_offset=1/*!*/; 
/*!\C utf8 *//*!*/; 
SET @@session.character_set_client=33,@@session.collation_connection=33,@@session.collation_server=33/*!*/; 
SET @@session.lc_time_names=0/*!*/; 
SET @@session.collation_database=DEFAULT/*!*/; 
BEGIN 
/*!*/; 
# at 291 
#230127 21:22:48 server id 1 end_log_pos 345 CRC32 0xc4ab653e Table_map: `test`.`account` mapped to number 99 
# at 345 
#230127 21:22:48 server id 1 end_log_pos 413 CRC32 0x54a124bd Update_rows: table id 99 flags: STMT_END_F 
### UPDATE `test`.`account` 
### WHERE ### @1=1 
### @2='lilei' 
### @3=1000 
### SET 
### @1=1 
### @2='lilei' 
### @3=2000 
# at 413 
#230127 21:22:48 server id 1 end_log_pos 444 CRC32 0x23355595 Xid = 10 
COMMIT/*!*/; 
# at 444 
。。。 

可看到具体执行的修改伪 SQL 语句以及执行时的相关情况

binlog日志文件恢复数据

用 binlog 日志文件恢复数据其实就是回放执行之前记录在 binlog 文件里的 SQL,例如:

# 先执行刷新日志的命令生成一个新的binlog文件mysql-binlog.000008,后面我们的修改操作日志都会记录在最新的这个文件里 
flush logs; 
# 执行两条插入语句 
INSERT INTO `test`.`account` (`id`, `name`, `balance`) VALUES ('4', 'zhuge', '666'); 
INSERT INTO `test`.`account` (`id`, `name`, `balance`) VALUES ('5', 'zhuge1', '888'); 
# 假设现在误操作执行了一条删除语句把刚新增的两条数据删掉了 
delete from account where id > 3; 

现在需要恢复被删除的两条数据,我们先查看binlog日志文件

mysqlbinlog --no-defaults -v --base64-output=decode-rows D:/dev/mysql-5.7.25-winx64/data/mysql-binlog.000008 

文件内容如下:

。。。。。。
SET @@SESSION.GTID_NEXT= 'ANONYMOUS'/*!*/;
# at 219
#230127 23:32:24 server id 1 end_log_pos 291 CRC32 0x4528234f Query thread_id=5 exec_time=0 error_code=0
SET TIMESTAMP=1674833544/*!*/;
SET @@session.pseudo_thread_id=5/*!*/;
SET @@session.foreign_key_checks=1, @@session.sql_auto_is_null=0, @@session.unique_checks=1, @@session.autocommit=1/*!*/;
SET @@session.sql_mode=1342177280/*!*/;
SET @@session.auto_increment_increment=1, @@session.auto_increment_offset=1/*!*/;
/*!\C utf8 *//*!*/;
SET @@session.character_set_client=33,@@session.collation_connection=33,@@session.collation_server=33/*!*/;
SET @@session.lc_time_names=0/*!*/;
SET @@session.collation_database=DEFAULT/*!*/;
BEGIN
/*!*/;
# at 291
#230127 23:32:24 server id 1 end_log_pos 345 CRC32 0x7482741d Table_map: `test`.`account` mapped to number 99
# at 345
#230127 23:32:24 server id 1 end_log_pos 396 CRC32 0x5e443cf0 Write_rows: table id 99 flags: STMT_END_F
### INSERT INTO `test`.`account`
### SET
### @1=4
### @2='zhuge'
### @3=666
# at 396
#230127 23:32:24 server id 1 end_log_pos 427 CRC32 0x8a0d8a3c Xid = 56
COMMIT/*!*/;
# at 427
#230127 23:32:40 server id 1 end_log_pos 492 CRC32 0x5261a37e Anonymous_GTID last_committed=1 sequence_number=2 rbr_only=yes
/*!50718 SET TRANSACTION ISOLATION LEVEL READ COMMITTED*//*!*/;
SET @@SESSION.GTID_NEXT= 'ANONYMOUS'/*!*/;
# at 492
#230127 23:32:40 server id 1 end_log_pos 564 CRC32 0x01086643 Query thread_id=5 exec_time=0 error_code=0
SET TIMESTAMP=1674833560/*!*/;
BEGIN
/*!*/;
# at 564
#230127 23:32:40 server id 1 end_log_pos 618 CRC32 0xc26b6719 Table_map: `test`.`account` mapped to number 99
# at 618
#230127 23:32:40 server id 1 end_log_pos 670 CRC32 0x8e272176 Write_rows: table id 99 flags: STMT_END_F
### INSERT INTO `test`.`account`
### SET
### @1=5
### @2='zhuge1'
### @3=888
# at 670
#230127 23:32:40 server id 1 end_log_pos 701 CRC32 0xb5e63d00 Xid = 58
COMMIT/*!*/;
# at 701
#230127 23:34:23 server id 1 end_log_pos 766 CRC32 0xa0844501 Anonymous_GTID last_committed=2 sequence_number=3 rbr_only=yes
/*!50718 SET TRANSACTION ISOLATION LEVEL READ COMMITTED*//*!*/;
SET @@SESSION.GTID_NEXT= 'ANONYMOUS'/*!*/;
# at 766
#230127 23:34:23 server id 1 end_log_pos 838 CRC32 0x687bdf88 Query thread_id=7 exec_time=0 error_code=0
SET TIMESTAMP=1674833663/*!*/;
BEGIN
/*!*/;
# at 838
#230127 23:34:23 server id 1 end_log_pos 892 CRC32 0x4f7b7d6a Table_map: `test`.`account` mapped to number 99
# at 892
#230127 23:34:23 server id 1 end_log_pos 960 CRC32 0xc47ac777 Delete_rows: table id 99 flags: STMT_END_F
### DELETE FROM `test`.`account`
### WHERE
### @1=4
### @2='zhuge'
### @3=666
### DELETE FROM `test`.`account`
### WHERE
### @1=5
### @2='zhuge1'
### @3=888
# at 960
#230127 23:34:23 server id 1 end_log_pos 991 CRC32 0x386699fe Xid = 65
COMMIT/*!*/;
SET @@SESSION.GTID_NEXT= 'AUTOMATIC' /* added by mysqlbinlog */ /*!*/;
DELIMITER ;
# End of log file
。。。。。。

找到两条插入数据的 SQL,每条 SQL 的上下都有 BEGIN 和 COMMIT,找到第一条 SQL 的 BEGIN 前面的文件位置标识 at 219(文件的位置标识),再找到第二条 SQL 的 COMMIT 后面的文件位置标识 at 701,便可以根据文件位置标识来恢复数据:

mysqlbinlog --no-defaults --start-position=219 --stop-position=701 --database=test D:/dev/mysql-5.7.25-winx64/data/mysql-binlog.000009 | mysql -uroot -p123456 -v test 

# 补充一个根据时间来恢复数据的命令,我们找到第一条sql BEGIN前面的时间戳标记 SET TIMESTAMP=1674833544,再找到第二条sql COMMIT后面的时间戳标记 SET TIMESTAMP=1674833663,转成datetime格式 
mysqlbinlog --no-defaults --start-datetime="2023-1-27 23:32:24" --stop-datetime="2023-1-27 23:34:23" --database=test D:/dev/mysql-5.7.25-winx64/data/mysql-binlog.000009 | mysql -uroot -p123456 -v test 

此操作便恢复被删除的数据。

注意:若要恢复大量数据,例如传说中的删库跑路,若数据库之前没有备份,但所有的 binlog 日志都在,此时只需从 binlog 第一个文件开始逐个恢复每个 binlog 文件里的数据即可,但这种过于理想化,因为 binlog 日志比较大,早期的 binlog 文件一般会定期删除,因此很难依靠 binlog 文件来恢复整个数据库。

推荐:每天的凌晨之后,便做一次全量数据库备份,若要恢复数据库便可使用最近的一次全量备份,再加上备份时间点之后的 binlog 来恢复数据。

备份数据库一般可以用 mysqldump 命令工具:

mysqldump -u root 数据库名>备份文件名; #备份整个数据库 
mysqldump -u root 数据库名 表名字>备份文件名; #备份整个表 

mysql -u root test < 备份文件名 #恢复整个数据库,test为数据库名称,需要自己先建一个数据库test 

面试题:为什么会有 redo log 和 binlog 两份日志呢?

因为最开始 MySQL 里并没有 InnoDB 引擎。MySQL 自带的引擎是 MyISAM,但是 MyISAM 没有 crash-safe 的能力,binlog 日志只能用于归档。而 InnoDB 是另一个公司以插件形式引入 MySQL 的,既然只依靠 binlog 是没有 crash-safe 能力的,那么 InnoDB 便使用另外一套日志系统,即 redo log 来实现 crash-safe 能力。

有了 redo log,InnoDB 便可保证即使数据库发生异常重启,之前提交的记录也不会丢失,此能力称为 crash-safe。

4、undo log回滚日志

InnoDB 对 undo log 文件的管理采用段的方式,即回滚段(rollback segment) 。每个回滚段记录了 1024 个 undo log segment ,每个事务只会使用一个 undo log segment。

在 MySQL 5.5 中,只有一个回滚段,因此最大同时支持的事务数量为 1024 个;从 MySQL 5.6 开始,InnoDB 支持最大 128 个回滚段,故其支持同时在线的事务限制提高到了 128*1024 。

innodb_undo_directory:设置undo log文件所在的路径。该参数的默认值为"./",即innodb数据文件存储位置,目录下ibdata1文件就是undo log存储的位置。 
innodb_undo_logs: 设置undo log文件内部回滚段的个数,默认值为128。 
innodb_undo_tablespaces: 设置undo log文件的数量,这样回滚段可以较为平均地分布在多个文件中。设置该参数后,会在路径innodb_undo_directory看到undo为前缀的文件。 

undo log 日志删除时机:

  • 新增类型的,在事务提交之后就可清除。
  • 修改类型的,事务提交之后不能立即清除,因为这些日志会用于 MVCC,因此需等到没有事务使用该版本信息后才可清除。

MySQL 不直接更新磁盘上的数据,而是设置一套复杂的机制来执行SQL:

  1. 若来一个请求就直接对磁盘文件进行随机读写,接着再更新磁盘文件里的数据,性能可能极差。
  2. 若磁盘随机读写的性能极差,则直接更新磁盘文件无法让数据库抗住很高的并发。
  3. MySQL 这套机制虽然看起来复杂,但可保证每个更新请求都是更新内存 BufferPool,然后顺序写日志文件,同时还能保证各种异常情况下的数据一致性。
  4. 更新内存的性能极高,之后的顺序写磁盘上的日志文件的性能也极高,相比之下远高于随机读写磁盘文件。
  5. 通过这套机制,MySQL 数据库才得以在较高配置的机器上,每秒可抗下几干甚至上万的读写请求。

5、错误日志

MySQL 还有一个比较重要的日志是错误日志,它记录了数据库启动和停止,以及运行过程中发生任何严重错误时的相关信息。当数据库出现任何故障导致无法正常使用时,建议首先查看此日志。

在 MySQL 数据库中,错误日志功能是默认开启的,而且无法被关闭:

# 查看错误日志存放位置 
show variables like '%log_error%'; 

6、通用查询日志

通用查询日志记录用户的所有操作,包括启动和关闭 MySQL 服务、所有用户的连接开始时间和截止时间、发给 MySQL 数据库服务器的所有 SQL 指令等,例如 select、show 等,无论 SQL 的语法正确与否、也无论 SQL 执行成功与否,此日志都会将其记录下来。

通用查询日志用来还原操作时的具体场景:可以准确定位一些疑难问题,比如重复支付等问题。

general_log:是否开启日志参数,默认为 OFF,处于关闭状态,因为开启会消耗系统资源并且占用磁盘空间。因此不建议开启,只在需要调试查询问题时开启。

general_log_file:通用查询日志记录的位置参数。

show variables like '%general_log%'; 
# 打开通用查询日志 
SET GLOBAL general_log=on; 

你可能感兴趣的:(MySQL专栏,mysql,数据库,java)