1. 日志
1.1 错误日志
错误日志是 MySQL 中最重要的日志之一,它记录了当 mysqld 启动和停止时,以及服务器在运行过程中发生任何严重错误时的相关信息。当数据库出现任何故障导致无法正常使用时,建议首先查看此日志。
该日志是默认开启的,默认存放目录 /var/log/,默认的日志文件名为 mysqld.log 。查看日志位置:
#先登录mysql mysql -uroot -p1234 #通过此系统变量查看日志文件的位置 show variables like '%log_error%';
#通过tail指令查看文件尾部的50行日志 tail -n 50 /var/log/mysqld.log
1.2 二进制日志
1.2.1 介绍
二进制日志(BINLOG)记录了所有的 DDL(数据定义语言:创建数据库…)语句和 DML(数据操纵语言:增删改)语句,但不包括数据查询(SELECT、SHOW)语句。
作用:
- ①. 灾难时的数据恢复;
- 因为二进制日志中记录了数据库、表、以及数据的变更。只需要把这里面的语句再次执行就可以恢复数据了。
- ②. MySQL的主从复制。在MySQL8版本中
- 主从复制底层原理就是基于二进制日志的,具体查看下一章。
mysql8.0版本默认二进制日志是开启着的,涉及到的参数如下:
#先登录mysql mysql -uroot -p1234 #通过此系统变量来查看二进制日志相关的参数配置 show variables like '%log_bin%';
参数说明:
log_bin:on代表二进制日志是开着的。
log_bin_basename:最终生成的二进制日志文件就在
/var/lib/mysql
目录下,文件名叫做binlog,但是日志文件有可能有很多,binlog只是它的前缀。- 当前数据库服务器的binlog日志的基础名称(前缀),具体的binlog文件名需要再该basename的基础上加上编号(编号从000001开始往上自增)。
- 第一个日志文件写满了或者日志的格式变更了之后,它会再次开启一个新的文件来写日志。
log_bin_index:binlog的索引文件,里面记录了当前服务器关联的binlog文件有哪些。
测试:
进入到/var/lib/mysql目录查看二进制文件到底有没有·
#不登录mysql执行 cd /var/lib/mysql #可以看到二进制日志文件和索引文件 ll #查看索引文件:里面就记录了当前mysql数据库关联的日志文件有哪些 cat binlog.index
1.2.2 格式
MySQL服务器中提供了多种格式来记录二进制日志,具体格式及特点如下:
日志格式 | 含义 |
---|---|
STATEMENT | 基于SQL语句的日志记录,记录的是SQL语句,对数据进行修改的SQL都会记录在日志文件中。 |
ROW | 基于行的日志记录,记录的是每一行的数据变更。(默认) |
MIXED | 混合了STATEMENT和ROW两种格式,默认采用STATEMENT,在某些特殊情况下会自动切换为ROW进行记录。 |
举例:如果我们执行了一条update语句,这条update语句影响的行数是5行
- STATEMENT:记录的就是这条update语句
- ROW :它记录的是update语句所影响的这五行,每一行的数据内容在变更之前怎么样,在变更之后是什么样。
#先登录mysql mysql -uroot -p1234 #通过此系统变量,查看当前mysql的版本中默认的日志格式是那个 show variables like '%binlog_format%';
如果我们需要配置二进制日志的格式,只需要在 /etc/my.cnf 中配置 binlog_format 参数即可。
vim /etc/my.cnf #在这个文件中添加一行内容 binlog_format=STATEMENT #重新启动mysql服务 systemctl restart mysqld.service cd /var/lib/mysql #可以看到此时重新生成了一个日志文件binlog.000005,原先是1~4。 #因为它的二进制日志格式改了,他不会再往原来的二进制日志文件写入了,而是写到一个新的日志文件中。 ll
再次查看此系统变量,发现日志格式已经修改为STATEMENT
#先登录mysql mysql -uroot -p1234 #通过此系统变量,查看当前mysql的版本中默认的日志格式是哪个 show variables like '%binlog_format%';
1.2.3 查看
由于日志是以二进制方式存储的,不能直接读取,需要通过二进制日志查询工具 mysqlbinlog 来查看,具体语法:
#logfilename:二进制文件名 mysqlbinlog [ 参数选项 ] logfilename 参数选项: -d 指定数据库名称,只列出指定的数据库相关操作。 -o 忽略掉日志中的前n行命令。 -v 将行事件(数据变更)重构为SQL语句 -vv 将行事件(数据变更)重构为SQL语句,并输出注释信息
测试:接下来呢我们就来设置一下这两种日志格式,来看一下它们之间的区别是什么样子的。
情况1:当前的日志格式是row
第一步:以db01数据库下的stu表为例进行演示。
客户端1:就是登录进mysql执行的命令 mysql -uroot -p1234 #当前的日志格式是row show variables like '%binlog_format%'; use db01; #查看db01数据库下面有哪些表 show tables; #查看stu表下面有哪些数据 select * from stu;
第二步:执行更新语句
客户端1: update stu set age = age +1 where id =1;
第三步:查看二进制日志表记录的是什么内容
客户端2:就是没有登录进mysql执行的命令 cd /var/lib/mysql ll #因为二进制日志是第一个日志文件写满了之后会开启一个新的日志文件,所以只需要看最后一个日志文件即可。 #二进制文件不能直接查看使用cat显示的是乱码,需要通过mysqlbinlog 指令来查看 #日志里面是以行的格式显示的,所以看不到sql语句,我们还需要使用-v把它重构为sql语句才能看到 #效果:在日志的最后部分可以看到数据执行前后的变化 mysqlbinlog -v binlog.000004;
可以看出日志格式是row,记录的是记录的是每一行的数据变更,在变更之前怎么样,在变更之后是什么样。
情况2:当前的日志格式是STATEMENT
第一步:修改日志格式为STATEMENT,只需要在 /etc/my.cnf 中配置 binlog_format 参数即可
vim /etc/my.cnf #在这个文件中添加一行内容 binlog_format=STATEMENT #重新启动mysql服务 systemctl restart mysqld.service cd /var/lib/mysql #可以看到此时重新生成了一个日志文件binlog.000005,原先是1~4。 #因为它的二进制日志格式改了,他不会再往原来的二进制日志文件写入了,而是写到一个新的日志文件中。 ll
第二步:再次执行之前的更新语句
mysql -uroot -p1234 use db01; update stu set age = age +1 where id =1;
第三步:再次查看这个新生成的二进制日志表的内容
#进入到二进制日志文件存放的位置 cd /var/lib/mysql #可以看到此目录下有这个日志文件 ll #查看此二进制日志文件 #不需要加-v,因为是STATEMENT它本身记录的就是sql语句 #效果:可以看到此时记录的就是sql语句而不是每一行的数据变化 mysqlbinlog binlog.000005;
1.2.4 删除
对于比较繁忙的业务系统,每天生成的binlog数据巨大,如果长时间不清除,将会占用大量磁盘空间。可以通过以下几种方式清理日志:
指令 | 含义 |
---|---|
reset master | 删除全部 binlog 日志,删除之后,日志编号,将从 binlog.000001重新开始 |
purge master logs to 'binlog.*****' | 删除 ***** 编号之前的所有日志 |
purge master logs before 'yyyy-mm-dd hh24:mi:ss' | 删除日志为 “yyyy-mm-dd hh24:mi:ss” 时间点之前产生的所有日志 |
也可以在mysql的配置文件中配置二进制日志的过期时间,设置了之后,二进制日志过期会自动删除。
mysql -uroot -p1234 #查看系统变量,在mysql命令行中执行 #单位是秒,默认过期时间为30天,到期之后会自动删除 show variables like '%binlog_expire_logs_seconds%';
测试:
客户端1:
mysql -uroot -p1234 #删除000002之前的日志文件,不包含000002 purge master logs to 'binlog.000002';
客户端2:
cd /var/lib/mysql #可以看到二进制日志文件和索引文件 ll
1.3 查询日志
查询日志中记录了客户端的所有操作语句,而二进制日志不包含查询数据的SQL语句。默认情况下, 查询日志是未开启的。
mysql -uroot -p1234 #检查参数查看开关是否开启 #可以看到默认是关闭的以及日志文件所处位置和文件名 show variables like '%general%';
如果需要开启查询日志,可以修改MySQL的配置文件 /etc/my.cnf 文件,添加如下内容:
vim /etc/my.cnf #该选项用来开启查询日志 , 可选值 : 0 或者 1 ; 0 代表关闭, 1 代表开启 general_log=1 #设置日志的文件名 , 如果没有指定, 默认的文件名为 host_name.log general_log_file=mysql_query.log #重启mysql服务 systemctl restart mysqld.service #查看这个目录下是否会生成此日志文件 cd /var/lib/mysql/ ll
开启了查询日志之后,在MySQL的数据存放目录,也就是 /var/lib/mysql/ 目录下就会出现mysql_query.log 文件。之后所有的客户端的增删改查操作都会记录在该日志文件之中,长时间运行后,该日志文件将会非常大。所以用不上此日志文件,我们可以把它关上。
测试:
客户端1:
mysql -uroot -p1234 use db01; #执行查询操作(前提是已经登录) select * from stu; #执行更新操作 update stu set age=100 where id=7;
客户端2:
cd /var/lib/mysql/ #前提是已经进入到了这个目录,并且目录下有这个文件(上面已经配置过了) #实时刷新此日志文件尾部的内容(tail查看文件尾部,-f表示实时刷新) tail -f mysql_query.log
可以看到所有的DDL和DML操作都会在日志表当中记录。
1.4 慢查询日志
慢查询日志记录了所有执行时间超过参数 long_query_time 设置值并且扫描记录数不小于min_examined_row_limit 的所有的SQL语句的日志,默认未开启。long_query_time 默认为10 秒,最小为 0, 精度可以到微秒。
解释:
- 慢查询日志记录了执行效率比较低,执行速度比较慢的sql语句。
- 之前在索引的sql性能分析中讲解过。
如果需要开启慢查询日志,需要在MySQL的配置文件 /etc/my.cnf 中配置如下参数:
#慢查询日志:1代表开启 slow_query_log=1 #执行时间参数:表示执行时间超过2秒就是慢查询日志,此时慢查询日志文件就会记录这条sql. long_query_time=2
默认情况下,不会记录管理语句,也不会记录不使用索引进行查找的查询。可以使用log_slow_admin_statements和 更改此行为 log_queries_not_using_indexes,如下所述。
解释:
- 通过在
vim /etc/my.cnf
配置文件中配置这2个参数,可以改变它的默认行为。 - 如果添加了log_slow_admin_statements =1:表示当我们执行比较慢的管理语句的时候,也会记录在慢查询日志当中。
- 如果添加了log_queries_not_using_indexes = 1:表示如果某一条sql语句,它没有使用索引而造成执行效率比较慢的话,也会记录在慢查询日志当中。
- 通过慢查询日志就可以定位出那些sql执行效率低,从而对这类的sql进行优化。
#记录执行较慢的管理语句 log_slow_admin_statements =1 #记录执行较慢的未使用索引的语句 log_queries_not_using_indexes = 1
上述所有的参数配置完成之后,都需要重新启动MySQL服务器才可以生效。
测试:
客户端1:
mysql -uroot -p1234 #db01数据库下有tb_sku表,存放了1000万条记录 #电脑太卡,所以我没有创建tb_sku表,这里不在演示,只显示最终结果 use db01; #不会记录 select * from tb_user limit 0,10; -- 这条SQL执行效率比较高, 执行耗时 0.01sec #前面学习过SQL优化,分页查询越向后效率越低,此时超过2秒,会记录在慢查询日志中 select * from tb_user limit 1000000,10; -- 由于tb_sku表中, 预先存入了1000w的记录, count一次,耗时4.71sec(秒)
客户端2:
#配置慢查询日志 vim /etc/my.cnf #配置的内容 slow_query_log=1 long_query_time=2 # 重启Mysql服务器 systemctl restart mysqld # 进入到此目录,发现会有一个后缀是-slow.log的日志文件 cd /var/lib/mysql/ ll #实时刷新文件尾部的位置发现: #记录了什么时间哪一个用户在哪一个主机上执行了什么样的sql语句 tail -f mysql8-slow.log
2.主从复制
2.1 概述
主从复制是指将主数据库的 DDL 和 DML 操作通过二进制日志传到从库服务器中,然后在从库上对这些日志重新执行(也叫重做),从而使得从库和主库的数据保持同步。
MySQL支持一台主库同时向多台从库进行复制, 从库同时也可以作为其他从服务器的主库,实现链状复制。
MySQL 复制的优点主要包含以下三个方面:
- 主库出现问题,可以快速切换到从库提供服务。
- 实现读写分离,降低主库的访问压力。
- 增删改操作主库,查询操作从库。
- 可以在从库中执行备份,以避免备份期间影响主库服务。
- 数据备份的时候加上全局锁以防止备份的数据不完整,此时数据库处于只读状态,其它的客户端只能查询不能做增删改。
- 有了主从复制后,可以在从库当中进行备份只需要锁从库就行,主库仍然可以进行增删改等操作。从库加了全局锁后仍然可以查询,只不过在数据备份期间可能存在一定的数据延迟,因为在备份期间从库是不能够执行从主库同步过来的二进制日志的。
- 解决:可以使用single-transaction参数代替加全局锁的方式进行备份,来保证数据的一致性备份------详情查看全局锁。
2.2 原理
MySQL主从复制的核心就是 二进制日志,具体的过程如下:
从上图来看,复制分成三步:
Master 主库在事务提交时,会把数据变更记录在二进制日志文件 Binlog 中。
从库读取主库的二进制日志文件 Binlog ,写入到从库的
中继日志 Relay Log
。- 从库中的IOthread线程:发起一个请求连接主数据库,然后读取主数据库中的 Binlog日志,读取完并返回从库之后,此线程会把Binlog日志写入到从库自身的一份日志(中继日志 Relay Log)中。
slave从库重做中继日志中的事件,将改变反映它自己的数据。
- 从库中的SQLthread线程:读取中继日志当中的数据,然后把中继日志当中所记录的数据变化在反映到自身数据库的数据变化,从而保证主从数据的一致。
举例:主库执行insert语句之后写入到二进制日志中,然后被IOthread线程读取过来之后写入到中继日志,那么SQLthread线程读取中继日志就会读取到这条insert语句,那么接下来在从库当中再去执行这条insert,此时就保证了主从数据的一致。
2.3 搭建
2.3.1 环境准备
准备好两台服务器之后,在上述的两台服务器中分别安装好MySQL,并完成基础的初始化准备(安装、 密码配置等操作)工作(注意要关闭防火墙
)。 其中:
192.168.10.200
作为主服务器master- 主机名:master
192.168.10.201
作为从服务器slave- 主机名:slave
- 主机名:slave
注意事项
:
- 首先配置的ip地址要和虚拟机配置的域名解析要在同一个网段,只有最后一个可以不同。
- 如果重启虚拟机后不显示ens33网卡,需要重启网络服务,当然启动服务时有可能会报错,需要关闭NetworkManger 服务。
ifconfig异常情况不显示ens33:
ifconfig正常情况显示ens33:
#重启网络服务,可能会报错 service network restart #如果报错:可能是和 NetworkManager 服务有冲突 #NetworkManager 是一个为系统提供检测和配置功能以便自动连接到网络的程序。包含一个守护程序、一个命令行界面(nmcli)和一个基于 curses 的界面(nmtui)。 #解决:直接关闭 NetworkManger 服务就好了,并且禁止开机启动,之后在重启网络服务 #关闭NetworkManger 服务 service NetworkManager stop #禁止开机启动 chkconfig NetworkManager off #此时再次启动网络服务就会成功了 service network restart
最后查看2台mysql服务器的运行状态:
systemctl status mysqld
2.3.2 主库配置
1.修改配置文件 vim /etc/my.cnf
#mysql 服务ID,保证整个集群环境中唯一,取值范围:1 – 232-1,默认为1 server-id=1 #是否只读,1 代表只读, 0 代表读写 read-only=0 #以下2个不需要配置,表示创建的所有数据库都需要进行同步 #忽略的数据, 指不需要同步的数据库 #binlog-ignore-db=mysql #指定同步的数据库 #binlog-do-db=db01
2.重启MySQL服务器
#如果没有报错代表配置文件中的配置成功 systemctl restart mysqld
3.登录mysql
,创建远程连接的账号,并授予主从复制权限
解释
- ‘itcast’@‘%’:其中
itcast
是用户名,@'%代表这个用户可以在任意主机上来访问当前服务器 - 密码是:
Root@123456
#需要先登录mysql mysql -uroot -p1234 #创建itcast用户,并设置密码,该用户可在任意主机连接该MySQL服务 #作用:在从库当中连接主库时的账号和密码。 CREATE USER 'itcast'@'%' IDENTIFIED WITH mysql_native_password BY 'Root@123456'; #为 'itcast'@'%' 用户分配主从复制权限 GRANT REPLICATION SLAVE ON *.* TO 'itcast'@'%';
4.通过指令,查看二进制日志坐标
show master status ;
字段含义说明:
- file : 从哪个日志文件开始推送日志文件(写到那个日志文件了)
- position : 从哪个位置开始推送日志
- binlog_ignore_db : 指定不需要同步的数据库
主库配置完后就不要在执行DML增删改以及DDL语句了。
2.3.3 从库配置
1.修改配置文件 vim /etc/my.cnf
#mysql 服务ID,保证整个集群环境中唯一,取值范围:1 – 2^32-1,和主库不一样即可 server-id=2 #是否只读,1 代表只读, 0 代表读写 #从库只需要做查询操作不需要做修改操作,所以设置为1也可以。 #这个选项仅仅代表是普通用户只读,如果这个用户具有超级管理员super的权限,那么他也是可以进行读写的。 read-only=1 #如果想要禁用超级管理员的读写功能,让它也变为只有读的功能,可以设置以下参数 super-read-only=1
2.重新启动MySQL服务
systemctl restart mysqld
3.登录mysql
,设置主库配置
现在主库和从库是没有关系的,并没有产生关联,所以接下来还要在从库中去设置主库的相关配置。
- SOURCE_HOST=‘192.168.200.200’:原主机地址是多少,也就是主库的ip
- SOURCE_USER=‘itcast’:连接这个ip地址对应的mysql,那么我的用户名是多少
- SOURCE_PASSWORD=‘Root@123456’:密码是多少
- SOURCE_LOG_FILE=‘binlog.000004’:从哪个二进制日志文件开始同步
- SOURCE_LOG_POS=663:表示从这份日志文件中的哪个位置开始同步。
mysql -uroot -p1234 CHANGE REPLICATION SOURCE TO SOURCE_HOST='192.168.10.200', SOURCE_USER='itcast',SOURCE_PASSWORD='Root@123456', SOURCE_LOG_FILE='binlog.000012',SOURCE_LOG_POS=663;
上述是8.0.23中的语法。如果mysql是 8.0.23 之前的版本,执行如下SQL:
CHANGE MASTER TO MASTER_HOST='192.168.10.201', MASTER_USER='itcast',MASTER_PASSWORD='Root@123456', MASTER_LOG_FILE='binlog.000012',MASTER_LOG_POS=663;
2个版本的区别是参数名不同:当前使用的8.0.26版本兼容之前的语法,所以执行那一条都可以
参数名 | 含义 | 8.0.23之前 |
---|---|---|
SOURCE_HOST | 主库IP地址 | MASTER_HOST |
SOURCE_USER | 连接主库的用户名 | MASTER_USER |
SOURCE_PASSWORD | 连接主库的密码 | MASTER_PASSWORD |
SOURCE_LOG_FILE | binlog日志文件名 | MASTER_LOG_FILE |
SOURCE_LOG_POS | binlog日志文件位置 | MASTER_LOG_POS |
4.开启同步操作
start replica ; #8.0.22之后 start slave ; #8.0.22之前
5.查看主从同步状态
show replica status ; #8.0.22之后 #表中的数据比较大展示出来的效果比较混乱,可以加上\G把每一列数据转化为每一行显示。 show replica status\G; show slave status ; #8.0.22之前
效果:只需要以下2个选项为yes就代表主从复制正常,IO-Running代表那一组io线程运行是否正常,SQL-Running代表那一组sql线程运行是否正常。
错误情况:如果是克隆的虚拟机,mysql的uuid值是一样的,必须修改从库虚拟机的mysql服务器uuid的值,和主库不能一样
解决:
#不需要登录mysql #修改此文件中的uuid值,随便修改一个字符 vim /var/lib/mysql/auto.cnf #重启mysql服务 systemctl restart mysqld
再次查询主从同步状态,此时都为yes(注意在从库执行)
#登录进mysql后执行,可以开启多个会话窗口这样就不需要多次登陆了 show replica status\G;
2.3.4 测试
先查询此时的数据库状态:
show databases;
1.在主库 192.168.10.200 上创建数据库、表,并插入数据
create database db02; use db02; create table tb_user( id int(11) primary key not null auto_increment, name varchar(50) not null, sex varchar(1) )engine=innodb default charset=utf8mb4; insert into tb_user(id,name,sex) values(null,'Tom', '1'),(null,'Trigger','0'),(null,'Dawn','1');
2.在从库 192.168.10.201 中查询数据,验证主从是否同步
show datables; use db02; show tables; select * from tb_user;
注意:
- 我们刚才所演示的这种主从复制,它是从二进制日志的当前位置往后进行主从复制的,如果我们要把之前的数据也需要同步到从库,那么这个时候我们可以先把主库的数据导出到一个sql脚本当中,然后在从库当中把sql脚本执行,先保证主库和从库的初始数据是一致的,然后再从当前位置往后再进行同步。