作为开发,可能很少涉及到运维相关知识的学习,一般都是有运维同学进行部署,但既然要整理mysql全系列,那就一并整理了吧。
日志
在mysql中,主要涉及到的是四个日志,即错误日志、二进制日志、查询日志以及慢查询日志。
错误日志
我们在开发中,遇到问题,都是先看日志。在mysql中也不例外,一旦mysql出现任何故障,建议先查看错误日志。它记录了当前mysqld启动和停止时,以及服务器在运行过程中发生任何严重错误时的相关信息。
错误日志正常来说都是默认开启的,默认存放路径为 /var/log,默认的日志文件名为 mysqld.log。如果不确定在哪,可通过变量来模糊查询,如:
mysql> show variables like '%log_error%';
+---------------------+---------------------+
| Variable_name | Value |
+---------------------+---------------------+
| binlog_error_action | ABORT_SERVER |
| log_error | /var/log/mysqld.log |
| log_error_verbosity | 3 |
+---------------------+---------------------+
3 rows in set (0.00 sec)
出现问题后,直接来该目录下查看[ERROR]
标签
binlog 二进制日志
binlog记录了所有的DDL(数据定义语言)语句和DML(数据操作语言)语句,但不包含查询语句(select,show),也就是包含了全部的数据、表的变更记录。
它的作用,主要是:
- 灾难时的数据恢复。
- mysql的主从复制。
- 其他,如有用到过canal做数据变化的监听从而同步数据到es中。
在mysql8中,默认binlog日志是开启的。可通过如下查看:
mysql> show variables like '%log_bin%';
+---------------------------------+----------------------------------+
| Variable_name | Value |
+---------------------------------+----------------------------------+
| log_bin | ON | # 是否开启
| log_bin_basename | /www/server/data/mysql-bin | # binlog存放的路径,文件名mysql-bin开头,会有很多序号递增文件
| log_bin_index | /www/server/data/mysql-bin.index | # 记录了当前数据库的索引信息
| log_bin_trust_function_creators | OFF |
| log_bin_use_v1_row_events | OFF |
| sql_log_bin | ON |
+---------------------------------+----------------------------------+
6 rows in set (0.00 sec)
开启binlog: etc/my.cnf下配置如下,然后重启mysql
[mysqld]
log-bin=mysql-bin # 启用二进制日志,指定日志文件的前缀
server-id=1 # 为 MySQL 服务器分配唯一的 ID,每个 MySQL 实例应该有不同的 server-id
mysql服务器中提供了多种格式来记录二进制日志,具体格式和特点如下: 假设执行了一个update语句,影响的行数是5行,则:
日志格式 | 含义 | 举例 |
---|---|---|
statement | 基于sql语句的日志记录,记录的是sql语句,对数据进行修改的sql都会记录在日志文件中 | 记录的是update的语句 |
row | 基于行的日志记录,记录的是每一行的数据变更 | 记录影响的五行数据在变更前以及变更后的具体数据 |
mixed | 混合以上两种格式,默认采用statement,特殊情况下会自动切换为row进行记录 |
可通过如下查看:
mysql> show variables like '%binlog_format%';
+---------------+-------+
| Variable_name | Value |
+---------------+-------+
| binlog_format | ROW |
+---------------+-------+
1 row in set (0.00 sec)
设置格式
有两种方式,一种永久的,一种session的。
- 永久的:在etc/my.cnf中设置/增加:binlog_format=STATEMENT 或者是 ROW/MIXD
- session:
set binlog_format='ROW';
如果通过修改配置文件,则会触发生成新的binlog文件,如果是session则不会
立个flag,出一篇讲解canal的文章
来吧,例子
首先介绍下二进制的日志查看工具:
由于日志是二进制的方式存储,不能直接读取,需要通过二进制日志查询工具mysqlbinlog来查看,如:
mysqlbinlog [参数选项] logfilename
参数选项:
-d 指定数据库名称,只列出指定的数据相关操作
-o 忽略掉日志中的前n行命令
-v 将行事件重构为sql语句
-vv 将行事件重构为sql语句,并输出注释信息
ROW格式
我们先执行一条sql,更新下数据。 update tb_user set age=age+1 where id=1;
然后再通过mysqlbinlog工具查看 mysqlbinlog -v -d study mysql-bin.000010
### UPDATE `study`.`tb_user`
### WHERE
### @1=1
### @2='ab'
### @3='226888272@qq.com'
### @4='+8613884895573'
### @5=1
### @6='pwd783012301'
### @7=13
### @8=2
### @9=431028199051500240
### @10='2023-09-18 17:50:36'
### @11=1696649138
### SET
### @1=1
### @2='ab'
### @3='226888272@qq.com'
### @4='+8613884895573'
### @5=1
### @6='pwd783012301'
### @7=14
### @8=2
### @9=431028199051500240
### @10='2023-09-18 17:50:36'
### @11=1696649760
# at 1617412
其中,where就是条件,也就是更新之前的数据,set是更新之后的数据。 比如第7个字段是年龄,变更前是13,变更后就是14了
STATEMENT格式
修改完格式后,继续执行sql语句 update tb_user set age=age-1 where id=1;
然后再通过mysqlbinlog工具查看mysqlbinlog -v -d study mysql-bin.000010
看最后
SET TIMESTAMP=1696650403/*!*/;
update tb_user set age=age-1 where id=1 # 来来来,这里这里 只是记录了具体的sql语句
/*!*/;
# at 1618288
#231007 11:46:43 server id 1 end_log_pos 1618319 CRC32 0x7c9b83db Xid = 22982
COMMIT/*!*/;
SET @@SESSION.GTID_NEXT= 'AUTOMATIC' /* added by mysqlbinlog */ /*!*/;
DELIMITER ;
# End of log file
日志删除
对于比较繁忙的业务系统,每天生成的binlog数据巨大,如果长时间不清除,将会占用大量磁盘空间。可以通过以下几种方式清理日志:
reset master
删除全部binlog日志,删除之后,日志编号将从binlog.0000001重新开始
purge master log to 'binlog.***'
删除 ******编号之前所有的日志
purge master logs before 'yyyy-mm-dd hh24:mi:ss'
删除日志为'yyyy-mm-dd hh24:mi:ss'之前产生的所有日志
当然, mysql肯定不指望手动删除咯,可以在mysql的配置文件中配置二进制日志的过期时间,设置了之后,二进制日志过期会自动删除。
参数为:binlog_expire_logs_seconds
show variables like '%binlog_expire_logs_seconds%';
查询日志
查询日志中记录了客户端的所有操作语句,而二进制日志不包含查询数据的SQL语句。默认情况下,查询日志是未开启的,因为大部分业务都是查询,则涉及到查询语句比较多。如果需要开启查询日志,可以设置一下配置:
同样是在中增加my.cnf
general_log=1 general_log_file=....
mysql> show variables like '%general%';
+------------------+------------------------------------+
| Variable_name | Value |
+------------------+------------------------------------+
| general_log | OFF |
| general_log_file | /www/server/data/VM-4-9-centos.log |
+------------------+------------------------------------+
2 rows in set (0.00 sec)
慢查询日志
我们之前也是有总结过慢查询日志的,在sql优化那一章节。此次,再次回顾下吧。
慢查询日志记录了所有执行时间超过参数 long_query_time设置值并且扫描记录数不小于min_examined_row_limit的所有sql语句的日志,默认未开启。long_query_time默认为10秒,最小为0,精度可以为微秒。
参数:
#慢查询日志
slow_query_log=1
#执行时间参数
long_query_time=2
查询十万条数据 select * from tb_user LIMIT 100000; 在mysql-slow.log下面就会有慢查询的记录了.
Time Id Command Argument
# Time: 2023-09-22T14:27:12.520739Z
# User@Host: milo[milo] @ [119.123.32.87] Id: 2
# Query_time: 25.903382 Lock_time: 0.005001 Rows_sent: 99606 Rows_examined: 99606
use study;
SET timestamp=1695392832;
select * from tb_user LIMIT 100000;
默认情况下,不会记录管理语句(show/use/create等等),也不会记录不使用索引进行查找的查询。可以使用log_slow_admin_statements和更改此行为log_queries_not_using_indexes。如:
#记录执行较慢的管理语句
log_slow_admin_statements=1
#记录执行较慢的未使用索引的语句
log_queries_not_using_indexes=1
主从复制
概述
主从复制,就是将主库(master)的DDL和DML操作通过binlog日志传到对应的从数据库(slave)中,然后在从库上对这些日志重新执行(也叫重做),从而使得从库和主库的数据保持同步。
mysql支持一台主库同时向多台从库进行复制,从库也可以作为其他服务器的主库,实现链状复制。
主从复制主要有以下几个作用:
- 主库出现问题,可以快速切换到从库提供服务,因为主从数据是一致的。
- 实现读写分离,主库可以做增、删、改操作,从库可以分流查操作,降低主库的访问压力。
- 在备份的时候会触发全局锁,此时可以在从库中进行数据备份,以避免备份期间影响主库服务。但从库在备份的时候,同步数据可能会出现延迟。
原理
直接上图吧
- 主库当中一旦发生增、删、改以及发生DDL语句的时候,会把所有的数据变更写入到binlog日志里面
- 从库中有两个线程,其中IOthread会读取master中的binlog日志,当读取到binlog日志后,iothread会继续把日志写入到slave自身的日志 relay log(中继日志)。
- 从库中的另外一个线程SQLthread,会读取自身的relay log(中继日志)的数据,把中继日志中的数据变化,反应到当前slave数据库中的数据变化(重做)。从而保证了主从同步。
搭建
准备工作
我本身有一台服务器,就是搭建这个博客的。现在在这个服务器上搭建一个docker容器作为slave。不影响搭建。
有个声音,为什么不用另外一个服务器作为slave?答:还不是因为穷。。。
1:获取镜像:docker pull mysql:5.7
2:启动镜像并挂载数据卷:docker run -d -p 33061:3306 -v /home/mysql01:/etc/mysql -e MYSQL_ROOT_PASSWORD=123456 --name mysql01 mysql:5.7
则登录进来后为全新的:
[root@VM-4-9-centos mysql01]# docker exec -it 02c6d435dd41 /bin/bash
bash-4.2# mysql -uroot -p123456
mysql> show databases;
+--------------------+
| Database |
+--------------------+
| information_schema |
| mysql |
| performance_schema |
| sys |
+--------------------+
4 rows in set (0.00 sec)
主库配置
修改配置文件 /etc/my.cnf
[mysql] #mysql服务id,保证整个集群环境中唯一,取值范围:1-2^32-1,默认为1 server-id=1 #是否只读,1表示只读,0表示读写 read-only=0 #忽略的数据,指不需要同步的数据库 #binlog-ignore-db=mysql #指定同步的数据库 #binlog-do-db=db01
配置完后重启
systemctl restart mysqld
创建远程远程连接的账号,并授予主从复制的权限,后面从库就使用该账号。
#创建milo01用户,设置密码 CREATE USER 'milo01'@'%' IDENTIFIED WITH mysql_native_password BY '123456'; #为milo01用户分配主从复制权限 GRANT REPLICATION SLAVE ON *.* TO 'milo01'@'%';
查看二进制日志坐标
mysql> show master status; +------------------+----------+--------------+------------------+-------------------+ | File | Position | Binlog_Do_DB | Binlog_Ignore_DB | Executed_Gtid_Set | +------------------+----------+--------------+------------------+-------------------+ | mysql-bin.000002 | 599 | | | | +------------------+----------+--------------+------------------+-------------------+ 1 row in set (0.00 sec)
file:从哪个日志文件开始推送日志文件
position:从哪个位置开始推送日志
binlog_ignore_db:指定不需要同步的数据库
从库配置
修改配置文件
[mysql] #mysql服务id,保证整个集群环境中唯一,取值范围:1-2^32-1,默认为1 server-id=2 #是否只读,1表示只读,仅仅是普通用户,但是超级管理员是可以读写的,除非设置super-read-only=1 read-only=1
修改完后重启
docker restart fb53f6fa1b77
在从库中设置相关配置
版本高于8.0.23执行:
CHANGE REPLICATION SOURCE TO SOURCE_HOST='XXX.XXX.XXX.XXX',SOURCE_USER='XXX',SOURCE_PASSWORD='XXX',SOURCE_LOG_FILE='XXX',SOURCE_LOG_POS=XXX
;CHANGE REPLICATION SOURCE TO SOURCE_HOST='124.223.47.250',SOURCE_USER='milo01',SOURCE_PASSWORD='123456',SOURCE_LOG_FILE='mysql-bin.000002',SOURCE_LOG_POS=599;
版本低于8.0.23执行:
CHANGE MASTER TO MASTER_HOST='主库IP',MASTER_USER='主库用户名',MASTER_PASSWORD="主库密码",MASTER_LOG_FILE='binlog文件名',MASTER_LOG_POS=binlog文件位置;
参数名 含义 SOURCE_HOST 主库ip地址 SOURCE_USER 链接主库的用户名 SOURCE_PASSWORD 链接主库的密码 SOURCE_LOG_FILE binlog日志文件名 SOURCE_LOG_POS binlog日志文件位置 启动主从复制
start replica; # 8.0.23之后 start slave; # 8.0.23之前
查看从库状态
show replica status\G; # 8.0.23之后 show slave status\G; # 8.0.23之前
mysql> show slave status\G; *************************** 1. row *************************** Slave_IO_State: Waiting for master to send event Master_Host: 124.223.47.250 Master_User: milo01 Master_Port: 3306 Connect_Retry: 60 Master_Log_File: mysql-bin.000002 Read_Master_Log_Pos: 29704 Relay_Log_File: fb53f6fa1b77-relay-bin.000002 Relay_Log_Pos: 320 Relay_Master_Log_File: mysql-bin.000002 Slave_IO_Running: Yes Slave_SQL_Running: Yes
只是需要关注
Slave_IO_Running
和Slave_SQL_Running
这两个为yes就可以,如果为no的话就去排查吧。可能是主库的log_file或者是pos不对,需要停掉slave再去看看。 排错相关命令:STOP SLAVE SQL_THREAD; #停止重做线程 SET GLOBAL SQL_SLAVE_SKIP_COUNTER = 1; # 跳过一个错误信息 START SLAVE; # 开启同步
验证
我们在主库中创建一个db01的库,创建一个tb_user的表,并且insert一条数据后。我们在从库中:
mysql> show databases;
+--------------------+
| Database |
+--------------------+
| information_schema |
| db01 |
| mysql |
| performance_schema |
| sys |
+--------------------+
5 rows in set (0.00 sec)
mysql> use db01;
Database changed
mysql> select * from tb_user;
+----+------+------+
| id | name | age |
+----+------+------+
| 1 | milo | 32 |
+----+------+------+
1 row in set (0.00 sec)
那么问题来了,我之前的数据如何同步呢?由于我们的主从是通过binlog来的,而binlog日志会定时清理,所以没法通过binlog进行全量的数据导入。我们可以先从主库dump一份出来后,再导入到从库中,这样两边数据一直后,才启用主从复制。