MySQL主从备份
服务器有可能突然挂机,如果是docker部署MySQL则也有可能容器突然坏了,这样对于数据库的使用自然会造成不少的影响,因此,需要对数据库采用一个高可用架构。
一个比较常见的场景就是主从备份,通常来说都是一主一从或者一主多从。主机进行工作,从机备份数据,如果主机突然宕机了,从机可以立刻开始工作而不会导致数据丢失。
主从备份原理
replication,可以实现将数据从⼀台数据库服务器(master)复制到多台数据库服务器slave上。并且,replication属于异步复制,因此是无需长连接的。
从工作上来说,复制是基于主服务器在其二进制日志中跟踪对其数据库的所有更改(更新、删除等)。
二进制日志作为从服务器启动开始修改数据库结构或内容(数据)的所有事件的书面记录。不过,二进制日志binlog通常不记录SELECT语句,因为它们既不修改数据库结构,也不修改内容。
服务器使用二进制日志来记录数据库的变动情况,从服务器通过读取和执行该日志文件来保持和主服务器的数据一致。在使用二进制日志时,主服务器的所有操作都会被记录下来,然后从服务器会接收到该日志的一个副本。从服务器可以指定执行该日志中的哪一类事件(譬如只插入数据或者只更新数据),默认会执行日志中的所有语句。
每一个从服务器会记录关于二进制日志的信息:文件名和已经处理过的语句,这样意味着不同的从服务器可以分别执行同一个二进制日志的不同部分,并且从服务器可以随时连接或者中断和服务器的连接。
主服务器和每一个从服务器都必须配置一个唯一的ID号。
在my.cnf文件的[mysqld]模块下有一个server-id配置项,这个配置项便是唯一的ID号,另外,每一个从服务器还需要通过CHANGE MASTER TO语句来配置它要连接的主服务器的ip地址、日志文件名称和该日志里面的位置,这些信息会被储存在主服务器的数据库中。
详细的讲,主从复制的基本过程如下:
主数据库保存binlog:首先,主数据库会将对数据库的更新操作以二进制格式保存在binlog中。
从数据库请求:而从数据库则会通过IO进程连接上主数据库,并请求从指定的日志文件的指定位置之后的内容开始进行同步。
主数据库返还:主数据库收到了来自从数据库IO请求后,通过负责复制的IO进程根据请求信息读取指定日志的指定位置的日志信息,返还给从数据库的IO进程。
从数据库设置:从数据库的IO进程,收到这个信息后,就会将接收到了的内容依次添加到relay-log文件的末端,并将读取到的主数据库的binlog文件的位置和名称记录到master-info文件中去,方便下一次读取时能清楚告诉主数据库需要从哪个位置开始往后继续读。
从数据库执行备份:从数据库的sql进程检测到了relay-log新增了内容之后,就会马上解析relay-log的内容并在自身进行执行。
主从备份的配置
主从备份的配置主要分为以下几个步骤:
1. 在主服务器上开启二进制日志机制,并配置一个独立的ID号。
2. 在每一个从服务器上配置一个唯一的ID号。
3. 创建一个专门用来复制主服务器数据的账号。
4. 在主服务器上授予从服务器该账号的复制权限。
5. 在从服务器上配置主服务器的连接信息,包括主机名、账号和密码。
6. 启动从服务器的复制进程,确保其可以成功连接到主服务器。
7. 验证主从复制的状态,确保数据一致性与实时同步。
主从备份实践
简单来说,配置主从备份同步主要分为以下几个步骤。首先,在主服务器上,需要开启二进制日志机制并且配置一个独立的ID,这个ID是从0-255的。在每一个从服务器上,配置一个唯一的ID,创建一个用来专门复制主服务器数据的账号。在开始服务进程之前,主服务器上会记录二进制文件的位置信息。如果说,在开始服务之前,主数据库上以及有了数据,那么就必须要先创建一个数据快照,比如说先使用mysqldump导出数据库(这个非常重要!!!)。最后配置从服务器要连接的主服务器的IP地址和授权登陆,二进制日志文件名和位置。
接下来,对主从备份的详细方法做一个阐述。
首先,主从的服务器可以自由指定,现在以docekr容器来进行实践,以mysql:8.3为例,建立一个一主一从的简单mysql主从备份集群。
docker pull mysql:8.3
创建一个docker网络组(当然,如果是分布式的部署,我认为是不需要的,这里是用于在一台机器上演示多个docker容器来进行部署。)
docker network create mysql-net
在此基础上,创建好所有挂载的目录。
启动部署主服务器:
docker run --net=mysql-net -v /etc/mysql/log:/var/log \
-v /etc/mysql/data:/var/lib/mysql \
-v /etc/mysql/conf.d/mysql.cnf:/etc/mysql/conf.d/mysql.cnf \
--restart=always -p 3308:3306 --name mysql-master \
-e MYSQL_ROOT_PASSWORD=123qweasd -d mysql:8.3
这样可以运行主服务器容器,并将其加入到mysql-net网络中。
接着编辑主服务器MySQL的配置文件/etc/mysql/conf.d/mysql.cnf:
vim /etc/mysql/conf.d/mysql.cnf
[mysqld]
server-id = 1
log-bin = mysql-master-bin
binlog-ignore-db = mysql,information_schema,performance_schema,sys
接着进入MySQL服务器并配置root账户:
docker exec -it mysql-master bash
mysql -uroot -p123qweasd
ALTER USER 'root'@'%' IDENTIFIED WITH mysql_native_password BY '123qweasd';
FLUSH PRIVILEGES;
exit
重启应用以启用配置:
docker restart mysql-master
查看主服务器的状态:
docker exec -it mysql-master mysql -uroot -p123qweasd -e "SHOW MASTER STATUS;"
记下 File 和 Position 字段的值,这些将在从服务器配置时用到。
下一步,就是部署从服务器。
首先,还是运行MySQL从服务器容器并加入网络。
docker run --net=mysql-net -v /etc/mysql2/log:/var/log \
-v /etc/mysql2/data:/var/lib/mysql \
-v /etc/mysql2/conf.d/mysql.cnf:/etc/mysql/conf.d/mysql.cnf \
--restart=always -p 3307:3306 --name mysql-slave1 \
-e MYSQL_ROOT_PASSWORD=123qweasd -d mysql:8.3
编辑从服务器的配置文件 /etc/mysql2/conf.d/mysql.cnf。注意id不要一样。
[mysqld]
server-id = 2
进入从服务器容器并设置主从复制的配置:
docker exec -it mysql-slave1 bash
mysql -uroot -p123qweasd
ALTER USER 'root'@'%' IDENTIFIED WITH mysql_native_password BY '123qweasd';
FLUSH PRIVILEGES;
exit
# 退出重启容器
docker restart mysql-slave1
# 再次进入,设置主从配置
docker exec -it mysql-slave1 bash
mysql -uroot -p123qweasd
CHANGE MASTER TO
MASTER_HOST='mysql-master', -- 使用主机容器名称替代 IP
MASTER_PORT=3306,
MASTER_USER='root',
MASTER_PASSWORD='123qweasd',
MASTER_LOG_FILE='mysql-master-bin.000004', -- 根据主服务器的实际值
MASTER_LOG_POS=158; -- 根据主服务器的实际值
START SLAVE;
exit
检查从服务器状态:
docker exec -it mysql-slave1 mysql -uroot -p123qweasd -e "SHOW SLAVE STATUS \G"
验证主从同步:
首先,在主服务器上创建一个数据库或者表,之后检查从服务器上是否同步成功。
# 创建一个测试库
CREATE DATABASE repl_test;
USE repl_test;
# 创建一个测试表
CREATE TABLE users (
id INT AUTO_INCREMENT PRIMARY KEY,
name VARCHAR(50) NOT NULL,
email VARCHAR(50) NOT NULL UNIQUE,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
# 插入一些测试数据
INSERT INTO users (name, email) VALUES
('Alice', '[email protected]'),
('Bob', '[email protected]'),
('Charlie', '[email protected]');
# 查看插入的数据
SELECT * FROM users;
而后进入从数据库,看具体的情况,可以看到从数据库是同步了主数据库的变化的。
进一步插入一些数据或者做一些修改,可以看到进一步的变化。
这样,一个一主一从的MySQL数据库主从备份就实现了。
主从备份可能出现的问题
主从备份的设置中,可能会出现一些问题,比如主从不同步,这时候就需要进行一些处理。
Q1 配置信息有问题
首先,就是设置主数据库信息出现问题时候的从数据库:
可以看到的是,当主数据库信息,比如在CHANGE MASTER TO中把IP或者端口设置错误的时候,slave-io-running出现了connecting的错误。所以一定要保证自己的信息是对的。
除了这种IP或者端口设置错误的情况,还有一些其他的情况:
比如,mysql的id应该是需要不一样的,假如说mysql的id出现了相同的情况,那么slave-io-running出现了no并爆出对应的报错。
再比如position不一致的情况,会导致slave-sql-running出现了no的错误,有对应的报错,也需要进行修正
Q2 SLAVE启动报错
当start slave的时候,有时会出现报错:
ERROR 1872 (HY000): Replica failed to initialize applier metadata structure from the repository
这样一个报错是表示在复制的过程中,从储存库初始化元数据结构的时候出现了问题,可能原因很多,比如存储库中的元数据损坏、复制过程中可能存在网络问题,导致从主服务器获取复制数据失败、主服务器的配置可能存在问题,导致无法正确提供复制数据等等。
要解决这个问题,可以尝试以下几个步骤:
首先是可以尝试修复存储库中的元数据,或者重新创建存储库。
其次要确保主服务器和从服务器之间的网络连接正常,并且没有任何阻塞或延迟。
更要确保主服务器的配置正确,并且可以正确提供复制数据。
重启设置启动一下slave。
# RESET SLAVE 可以用来清除从服务器上的复制状态
reset slave;
start slave;
如果重置复制状态后问题仍然存在,可能需要删除并重新创建 MySQL 中用于存储复制元数据的表。
STOP SLAVE;
# 删除用于存储复制元数据的表
DROP TABLE IF EXISTS mysql.slave_master_info;
DROP TABLE IF EXISTS mysql.slave_relay_log_info;
DROP TABLE IF EXISTS mysql.slave_worker_info;
# 重启 MySQL 服务
service mysql restart
最后再重新配置从服务器的复制,如上一步中的配置过程。
Q3 数据库复制出现问题
在某些情况下,往往是主数据库进行了一些操作后,才开始做从数据库,又或者一个数据库操作了很久后,才决定变成另外一个数据库的从数据库,这个样子会出现一定问题。比如:
Last_Error: Coordinator stopped because there were error(s) in the worker(s). The most recent failure being: Worker 1 failed executing transaction 'ANONYMOUS' at source log mysql-master-bin.000004, end_log_pos 358. See error log and/or performance_schema.replication_applier_status_by_worker table for more details about this failure or others, if any.……
这种情况就是数据库的复制出现了问题。
比如这个错误提示表明,在 MySQL 主从复制过程中,从服务器的某个 Worker 线程在执行事务时遇到了问题,导致复制失败。具体错误信息指出了事务在 mysql-master-bin.000004 二进制日志文件中的位置 end_log_pos 358 出现了问题。
一般来说,出现这个问题的情况有以下几种:
一种是数据不一致,主服务器上的某个事务可能试图修改从服务器上不存在的数据,或者主从服务器的数据已经发生了不一致的情况。这会导致从服务器无法正确执行该事务,并出现复制错误。
一种是主从服务器上的表结构不一致,主服务器和从服务器的表结构可能存在差异,比如字段类型不同、索引不一致等。这会导致从服务器在应用主服务器的事务时出错。
还有可能是主数据库配置上的差异和事务处理上的不一致。对于这样的情况来说,要进行解决。
首先,要查看详细的错误日志。
错误日志通常位于 /var/log/mysql/error.log,也可以通过 MySQL 变量 log_error 查看日志路径:
SHOW VARIABLES LIKE 'log_error';
mysql> SHOW VARIABLES LIKE 'log_error';
+---------------+--------+
| Variable_name | Value |
+---------------+--------+
| log_error | stderr |
+---------------+--------+
1 row in set (0.01 sec)
进一步的检查复制状态,可以通过 MySQL 的 performance_schema.replication_applier_status_by_worker 表来查看具体的错误:
SELECT * FROM performance_schema.replication_applier_status_by_worker WHERE LAST_ERROR_NUMBER != 0;
这将显示具体哪个 Worker 出现了错误及其详细信息。从这里可以看到在新建database repl_test的过程中出现了一些问题。
便可针对其进行排查。
最后,就是修复数据不一致问题。
主从服务器数据不一致时,可以手动检查从服务器上的数据是否与主服务器一致,必要时可以在从服务器上手动修复数据,或从主服务器重新同步。
如果发现数据不一致,可以在从服务器上插入或更新相应的记录。
如果你确定某个事务并不重要,可以通过跳过有问题的事务来继续复制进程。可以使用 SET GLOBAL sql_slave_skip_counter 命令跳过有问题的事务:
STOP SLAVE;
SET GLOBAL sql_slave_skip_counter = 1;
START SLAVE;
SHOW SLAVE STATUS \G;
最后,就是检查以下主从数据库的表结构等等并重新初始化配置,解决问题。