数据库系列之MySQL主从复制集群部署
主从复制是MySQL集群部署中常用的架构,本文主要介绍MySQL主从集群架构的部署以及基于GTID主从复制的部署并进行测试验证。
1、MySQL单机环境部署
1.1 环境准备
1)下载MySQL安装包
下载版本:我这里选择的5.7.31,通用版,linux下64位
2)添加用户组和用户
[root@tango-centos01 ~]# groupadd mysql
[root@tango-centos01 ~]# useradd -g mysql mysql
[root@tango-centos01 ~]# passwd mysql
3)检查RPM包,查看是否安装MySQL和MariaDB
[root@tango-centos01 mysql]# rpm -qa|grep mysql
[root@tango-centos01 mysql]# rpm -qa|grep mariadb
mariadb-libs-5.5.56-2.el7.x86_64
如有,则强制卸载
rpm -e --nodeps mysql-libs-5.1.52-1.el6_0.1.x86_64
rpm -e --nodeps mariadb-libs-5.5.56-2.el7.x86_64
rpm -e --nodeps mariadb-5.5.56-2.el7.x86_64
rpm -e --nodeps mariadb-server-5.5.56-2.el7.x86_64
1.2 MySQL安装
1)解压安装包
[root@tango-centos01 src-install]# tar -xzvf mysql-5.7.31-linux-glibc2.12-x86_64.tar.gz
2)复制解压后的mysql目录
[root@tango-centos01 src-install]# cp -r mysql-5.7.31-linux-glibc2.12-x86_64 /usr/local/mysql
3)创建MySQL目录并授权
[root@tango-centos01 local]# cd mysql
[root@tango-centos01 mysql]# mkdir data
[root@tango-centos01 mysql]# mkdir ./data/mysql
[root@tango-centos01 mysql]# chown -R mysql:mysql ./
[root@tango-centos01 mysql]# ll
total 288
drwxr-xr-x. 2 mysql mysql 4096 Aug 29 22:54 bin
drwxr-xr-x. 3 mysql mysql 19 Aug 29 22:55 data
drwxr-xr-x. 2 mysql mysql 55 Aug 29 22:53 docs
drwxr-xr-x. 3 mysql mysql 4096 Aug 29 22:54 include
drwxr-xr-x. 5 mysql mysql 205 Aug 29 22:53 lib
-rw-r--r--. 1 mysql mysql 275393 Aug 29 22:54 LICENSE
drwxr-xr-x. 4 mysql mysql 30 Aug 29 22:53 man
-rw-r--r--. 1 mysql mysql 587 Aug 29 22:54 README
drwxr-xr-x. 28 mysql mysql 4096 Aug 29 22:54 share
drwxr-xr-x. 2 mysql mysql 90 Aug 29 22:54 support-files
4)修改启动脚本
[root@tango-centos01 mysql]# vi /etc/ my.cnf
#修改内容如下
[mysqld]
bind-address=192.168.112.101
port=3306
user=mysql
basedir=/usr/local/mysql
datadir=/usr/local/mysql/data/mysql
socket=/tmp/mysql.sock
symbolic-links=0
character_set_server=utf8mb4
explicit_defaults_for_timestamp=true
log-error=/usr/local/mysql/data/mysql/mysql.err
pid-file=/usr/local/mysql/data/mysql/mysql.pid
5)初始化MySQL
[root@tango-centos01 mysql]# ./bin/mysqld --defaults-file=/etc/my.cnf --basedir=/usr/local/mysql/ --datadir=/usr/local/mysql/data/mysql/ --user=mysql --initialize
2020-08-31T01:18:39.492344Z 1 [Note] A temporary password is generated for root@localhost: qMj6Xi7yr<)4
找到root用户初始密码为qMj6Xi7yr<)4
6)启动服务
[root@tango-centos01 mysql]# cp support-files/mysql.server /etc/init.d/mysql
[root@tango-centos01 mysql]# chmod 755 /etc/init.d/mysql
[root@tango-centos01 mysql]# service mysql start
Starting MySQL.Logging to '/usr/local/mysql/data/mysql/mysql.err'.
. SUCCESS!
7)查看MySQL服务状态
[mysql@tango-centos01 mysql]$ ps -ef|grep mysql
mysql 3335 1 0 09:54 pts/0 00:00:00 /bin/sh /usr/local/mysql/bin/mysqld_safe --datadir=/usr/local/mysql/data/mysql --pid-file=/usr/local/mysql/data/mysql/mysql.pid
mysql 3565 3335 1 09:54 pts/0 00:00:00 /usr/local/mysql/bin/mysqld --basedir=/usr/local/mysql --datadir=/usr/local/mysql/data/mysql --plugin-dir=/usr/local/mysql/lib/plugin --log-error=/usr/local/mysql/data/mysql/mysql.err --pid-file=/usr/local/mysql/data/mysql/mysql.pid --socket=/tmp/mysql.sock --port=3306 [mysql@tango-centos01 mysql]$ service mysql status
SUCCESS! MySQL running (3565)
8)加入环境变量,编辑 /etc/profile,这样可以在任何地方用mysql命令了
[root@tango-centos01 mysql]# export PATH=$PATH:/usr/local/mysql/bin
[root@tango-centos01 mysql]# source /etc/profile
1.3 测试连接
1)连接MySQL
[root@tango-centos01 mysql]# mysql -uroot -p
Enter password:
Welcome to the MySQL monitor. Commands end with ; or \g.
Your MySQL connection id is 4
Server version: 5.7.31
Copyright (c) 2000, 2020, Oracle and/or its affiliates. All rights reserved.
Oracle is a registered trademark of Oracle Corporation and/or its
affiliates. Other names may be trademarks of their respective
owners.
Type 'help;' or '\h' for help. Type '\c' to clear the current input statement.
mysql>
2)设置root用户密码
mysql> SET PASSWORD FOR 'root'@'localhost' = PASSWORD('password');
Query OK, 0 rows affected, 1 warning (0.01 sec)
mysql> FLUSH PRIVILEGES;
Query OK, 0 rows affected (0.00 sec)
3)设置远程访问权限
将host字段的值改为%就表示在任何客户端机器上能以root用户登录到mysql服务器,建议在开发时设为%。
mysql> use mysql;
Database changed
mysql> grant all privileges on *.* to root@'%' identified by "password";
Query OK, 0 rows affected, 1 warning (0.00 sec)
mysql> flush privileges;
Query OK, 0 rows affected (0.00 sec)
mysql> select Host,User,password_expired from user;
+-----------+---------------+------------------+
| Host | User | password_expired |
+-----------+---------------+------------------+
| localhost | root | N |
| localhost | mysql.session | N |
| localhost | mysql.sys | N |
| % | root | N |
+-----------+---------------+------------------+
4 rows in set (0.00 sec)
4)创建database并授权
mysql> create database test;
Query OK, 1 row affected (0.00 sec)
mysql> grant all on test.* to mysql@localhost identified by 'password';
Query OK, 0 rows affected, 1 warning (0.00 sec)
mysql> flush privileges;
Query OK, 0 rows affected (0.00 sec)
5)使用用户mysql登录
[root@tango-centos01 mysql]# mysql -umysql -p
Enter password:
Welcome to the MySQL monitor. Commands end with ; or \g.
Your MySQL connection id is 5
Server version: 5.7.31 MySQL Community Server (GPL)
Copyright (c) 2000, 2020, Oracle and/or its affiliates. All rights reserved.
Oracle is a registered trademark of Oracle Corporation and/or its
affiliates. Other names may be trademarks of their respective
owners.
Type 'help;' or '\h' for help. Type '\c' to clear the current input statement.
mysql> use test;
Database changed
2、MySQL基于主从复制集群环境部署
2.1 主从复制的原理
MySQL主从复制的基础是主库中记录数据库的所有变更记录到binlog,binlog是保存所有更新事件日志的二进制文件。在实施主从复制时候,必须打开Master 端的binary log(bin-log)功能,否则无法实现。
2.1.1 主从复制基本过程
MySQL主从复制是一个异步的复制过程,主库发送更新事件到从库,从库读取更新记录,并执行更新记录,使得从库的内容与主库保持一致。主从复制的基本过程如下图所示:
主库database的更新事件(update、insert、delete)写到binlog
从库上的I/O进程连接主库,并请求从指定日志文件的指定位置(或者从最开始的日志)之后的日志内容
主库接收到来自从库的I/O请求后会创建一个binlog dump thread,把binlog的内容发送到从库。返回信息中除了日志所包含的信息之外,还包括本次返回的信息的bin-log file 以及bin-log position。
从库启动之后,会创建一个I/O线程,用来读取主库传过来的binlog内容并写入到relay log中
从库创建一个SQL线程,从relay log里面读取内容解析成在主库上实际执行过的操作,并在从库的本地database中执行
2.1.2 主从复制中的三个进程
主从复制的完成通过以下三个进程实现的:
主库 binary log dump 线程
当从库连接主库时,主库会创建一个log dump 线程,用于发送bin-log的内容。在读取bin-log中的操作时,此线程会对主库上的bin-log加锁,当读取完成,甚至在发动给从库之前,锁会被释放。
从库I/O线程
当从库执行start slave命令之后,从库会创建一个I/O线程用来连接主库,请求主库中更新的bin-log。I/O线程接收到主库binlog dump 进程发来的更新之后,保存在本地relay-log中。
从库SQL线程
SQL线程负责读取relay log中的内容解析成具体的操作并执行,最终保证主从数据的一致性。
注:对于每一个主从连接,都需要三个进程来完成。当主节点有多个从节点时,主节点会为每一个当前连接的从节点建一个binary log dump 进程,而每个从节点都有自己的I/O进程,SQL进程。从节点用两个线程将从主库拉取更新和执行分成独立的任务,这样在执行同步数据任务的时候,不会降低读操作的性能。比如,如果从节点没有运行,此时I/O进程可以很快从主节点获取更新,尽管SQL进程还没有执行。如果在SQL进程执行之前从节点服务停止,至少I/O进程已经从主节点拉取到了最新的变更并且保存在本地relay日志中,当服务再次起来之后,就可以完成数据的同步。
2.2 主从复制部署
MySQL 主从复制默认是异步的模式,这种模式下,主节点不会主动push bin log到从节点,这样有可能导致failover的情况下,也许从节点没有即时地将最新的bin log同步到本地。
2.2.1 环境配置
本文中MySQL主从复制采取一主两从的架构,服务器配置信息如下:
Hostname | IP | 系统 | 角色 |
---|---|---|---|
tango-centos01 | 192.168.112.101 | Centos7.0-x86_64 | Master |
tango-centos02 | 192.168.112.102 | Centos7.0-x86_64 | Slave1 |
tango-centos03 | 192.168.112.103 | Centos7.0-x86_64 | Slave2 |
2.2.2 主节点安装MySQL
1)MySQL环境安装
MySQL环境的安装同单机环境部署,具体步骤如下:
#解压安装包
[root@tango-centos01 src-install]# tar -xzvf mysql-5.7.31-linux-glibc2.12-x86_64.tar.gz
#复制解压后到mysql目录
[root@tango-centos01 src-install]# cp -r mysql-5.7.31-linux-glibc2.12-x86_64 /usr/local/mysql
#添加用户组和用户[
root@tango-centos01 ~]# groupadd mysql
[root@tango-centos01 ~]# useradd -g mysql mysql
[root@tango-centos01 ~]# passwd mysql
#创建MySQL目录并授权
[root@tango-centos01 local]# cd mysql
[root@tango-centos01 mysql]# mkdir data
[root@tango-centos01 mysql]# mkdir ./data/mysql
[root@tango-centos01 mysql]# chown -R mysql:mysql ./
#修改启动脚本
[root@tango-centos01 mysql]# vi /etc/my.cnf
#初始化MySQL
[root@tango-centos01 mysql]# ./bin/mysqld --defaults-file=/etc/my.cnf --basedir=/usr/local/mysql/ --datadir=/usr/local/mysql/data/mysql/ --user=mysql --initialize
#启动服务
[root@tango-centos01 mysql]# cp support-files/mysql.server /etc/init.d/mysql
[root@tango-centos01 mysql]# chmod 755 /etc/init.d/mysql
[root@tango-centos01 mysql]# service mysql start
[root@tango-centos01 mysql]$ service mysql status
#加入环境变量
[root@tango-centos01 mysql]# export PATH=$PATH:/usr/local/mysql/bin
[root@tango-centos01 mysql]# source /etc/profile
2)开启log-bin日志
[root@tango-centos01 mysql]# vi /etc/my.cnf
log-bin=mysql-bin #开启及设置二进制日志文件名称
server-id=101
binlog_format = MIXED
sync_binlog = 1
expire_logs_days =7 #二进制日志自动删除/过期的天数。默认值为0,表示不自动删除。
binlog_format有三种模式:STATEMENT、ROW和MIXED
STATEMENT:每一条会修改数据的sql语句会记录到binlog中。优点是并不需要记录每一条sql语句和每一行的数据变化,减少了binlog日志量,节约IO,提高性能。缺点是在某些情况下会导致master-slave中的数据不一致
ROW:不记录每条sql语句的上下文信息,仅需记录哪条数据被修改了,修改成什么样了。而且不会出现某些特定情况下的存储过程、或function、或trigger的调用和触发无法被正确复制的问题。缺点是会产生大量的日志,尤其是alter table的时候会让日志暴涨。
MIXED:以上两种模式的混合使用,一般的复制使用STATEMENT模式保存binlog,对于STATEMENT模式无法复制的操作使用ROW模式保存binlog,MySQL会根据执行的SQL语句选择日志保存方式。
sync_binlog参数来控制数据库的binlog刷到磁盘上去
默认sync_binlog=0,表示MySQL不控制binlog的刷新,由文件系统自己控制它的缓存的刷新。这时候的性能是最好的,但是风险也是最大的。因为一旦系统Crash,在binlog_cache中的所有binlog信息都会被丢失
如果sync_binlog>0,表示每sync_binlog次事务提交,MySQL调用文件系统的刷新操作将缓存刷下去。最安全的就是sync_binlog=1了,表示每次事务提交,MySQL都会把binlog刷下去,是最安全但是性能损耗最大的设置
3)启动MySQL,使用mysql用户启动
[mysql@tango-centos01 mysql]$ service mysql start
[mysql@tango-centos01 mysql]$ service mysql status
4)连接MySQL
mysql –uroot -ppassword
5)查看log_bin是否开启
mysql> show variables like 'log_bin';
+---------------+-------+
| Variable_name | Value |
+---------------+-------+
| log_bin | ON |
+---------------+-------+
1 row in set (0.00 sec)
6)查看master状态
mysql> show master status;
+------------------+----------+--------------+------------------+-------------------+
| File | Position | Binlog_Do_DB | Binlog_Ignore_DB | Executed_Gtid_Set |
+------------------+----------+--------------+------------------+-------------------+
| mysql-bin.000002 | 154 | | |
+------------------+----------+--------------+------------------+-------------------+
1 row in set (0.00 sec)
2.2.3 从节点安装MySQL
1)安装步骤同上
2)修改配置信息,分别添加server-id=102和server-id=103即可
[root@tango-centos02 mysql]# vi /etc/my.cnf
bind-address=192.168.112.102
server-id=102
[root@tango-centos03 mysql]# vi /etc/my.cnf
bind-address=192.168.112.103
server-id=103
3)使用MySQL用户启动服务
[mysql@tango-centos02 mysql]$ service mysql start
Starting MySQL. SUCCESS!
[mysql@tango-centos02 mysql]$ service mysql status
SUCCESS! MySQL running (2382)
[mysql@tango-centos03 mysql]$ service mysql start
Starting MySQL. SUCCESS!
[mysql@tango-centos03 mysql]$ service mysql status
SUCCESS! MySQL running (1631)
2.2.4 主节点配置
创建授权用户repl,密码为password
mysql> CREATE USER 'repl'@'%' IDENTIFIED BY 'password';
Query OK, 0 rows affected (0.00 sec)
mysql> GRANT REPLICATION SLAVE,REPLICATION CLIENT ON *.* TO 'repl'@'%';
Query OK, 0 rows affected (0.00 sec)
mysql> flush privileges;
Query OK, 0 rows affected (0.01 sec)
2.2.5 从节点配置
在从节点登录MySQL执行以下命令:
mysql> stop slave;
mysql> CHANGE MASTER TO MASTER_HOST='192.168.112.101',
-> MASTER_PORT=3306,
-> MASTER_USER='repl',
-> MASTER_PASSWORD='password',
-> MASTER_LOG_FILE='mysql-bin.000002',
-> MASTER_LOG_POS=767;
mysql> start slave;
mysql> show slave status\G;
*************************** 1. row ***************************
Slave_IO_State: Waiting for master to send event
Master_Host: 192.168.112.101
Master_User: repl
Master_Port: 3306
Connect_Retry: 60
Master_Log_File: mysql-bin.000002
Read_Master_Log_Pos: 767
Relay_Log_File: tango-centos02-relay-bin.000002
Relay_Log_Pos: 320
Relay_Master_Log_File: mysql-bin.000002
Slave_IO_Running: Yes
Slave_SQL_Running: Yes
以下省略...
1 row in set (0.01 sec)
stop slave; //停止同步
start slave; //开始同步
必须和主库的信息匹配“CHANGE MASTER TO MASTER_HOST='192.168.10.212'”
从主库的该log_bin文件开始读取同步信息,主库show master status返回结果,MASTER_LOG_FILE='mysql-bin.000001'
从文件中指定位置开始读取,主库show master status返回结果,MASTER_LOG_POS=154
2.2.6 主从复制测试
1)在master节点创建表并插入数据
mysql> create database test01;
mysql> use test01;
Database changed
mysql> create table tb01 (username varchar(20) not null,password varchar(20) not null);
mysql> desc tb01;
+----------+-------------+------+-----+---------+-------+
| Field | Type | Null | Key | Default | Extra |
+----------+-------------+------+-----+---------+-------+
| username | varchar(20) | NO | | NULL | |
| password | varchar(20) | NO | | NULL | |
+----------+-------------+------+-----+---------+-------+
2 rows in set (0.02 sec)
mysql> insert into tb01 values('user1','123');
mysql> select * from tb01;
+----------+----------+
| username | password |
+----------+----------+
| user1 | 123 |
+----------+----------+
2)在slave节点查看数据库,发现数据更新
mysql> show databases;
+--------------------+
| Database |
+--------------------+
| information_schema |
| mysql |
| performance_schema |
| sys |
| test01 |
+--------------------+
5 rows in set (0.00 sec)
mysql> select * from test01.tb01;
+----------+----------+
| username | password |
+----------+----------+
| user1 | 123 |
| user2 | 123 |
+----------+----------+
2 rows in set (0.00 sec)
2.3 基于GTID主从复制
GTID是一个全局事务标示符,使用GTID时,每次事务提交都会在binlog里生成1个唯一的标示符,它由UUID和事务ID组成。UUID是服务器的身份ID,在第一次启动MySQL时,会自动生成一个server_uuid, 并且默认写入到数据目录下的auto.cnf文件里。我们一般无需修改,官方也不建议修改。更为详细的可以参考MariaDB官方文档
https://mariadb.com/kb/en/mariadb/mariadb-documentation/replication-cluster-multi-master/replication/parallel-replication/
那么基于GTID的复制有以下好处:
在传统的主从复制里面,当发生故障需要主从切换的时候,需要找到binlog和pos点,然后将master指向新的master,相对来说比较麻烦,也容易出错。在MySQL 5.6里面,不用再找binlog和pos点,我们只需要知道master的ip,端口,以及账号密码就行,因为复制是自动的,MySQL会通过内部机制GTID自动找到同步点。
多线程复制(基于库)。在MySQL 5.6以前的版本,slave的复制是单线程的,而master是并发写入的,所以延时是避免不了的。唯一有效的方法是把多个库放在多台slave,这样又有点浪费服务器。在MySQL 5.6里面,我们可以把多个表放在多个库,这样就可以使用多线程复制,当只有1个库,多线程复制是没有用的。
2.3.1 配置GTID
GTID的相关特性默认是关闭的,需要打开:
mysql> show variables like '%gtid%';
+----------------------------------+-----------+
| Variable_name | Value |
+----------------------------------+-----------+
| binlog_gtid_simple_recovery | ON |
| enforce_gtid_consistency | OFF |
| gtid_executed_compression_period | 1000 |
| gtid_mode | OFF |
| gtid_next | AUTOMATIC |
| gtid_owned | |
| gtid_purged | |
| session_track_gtids | OFF |
+----------------------------------+-----------+
关闭主从节点的MySQL,修改配置文件
1)主节点
[mysql@tango-centos01 mysql]$ service mysql stop
[root@tango-centos01 ~]# vi /etc/my.cnf #更改主的配置文件,启动mysql
gtid_mode = ON
enforce-gtid-consistency = true
[mysql@tango-centos01 mysql]$ service mysql start
查看配置已经修改成功
mysql> show variables like '%gtid%';
+----------------------------------+-----------+
| Variable_name | Value |
+----------------------------------+-----------+
| binlog_gtid_simple_recovery | ON |
| enforce_gtid_consistency | ON |
| gtid_executed_compression_period | 1000 |
| gtid_mode | ON |
| gtid_next | AUTOMATIC |
| gtid_owned | |
| gtid_purged | |
| session_track_gtids | OFF |
+----------------------------------+-----------+
8 rows in set (0.00 sec)
2)从节点:
[mysql@tango-centos02 mysql]$ service mysql stop
[root@tango-centos02 ~]# vi /etc/my.cnf
gtid_mode = ON
enforce-gtid-consistency = true
[mysql@tango-centos01 mysql]$ service mysql start
查看配置已经修改成功
mysql> show variables like '%gtid%';
+----------------------------------+-----------+
| Variable_name | Value |
+----------------------------------+-----------+
| binlog_gtid_simple_recovery | ON |
| enforce_gtid_consistency | ON |
| gtid_executed_compression_period | 1000 |
| gtid_mode | ON |
| gtid_next | AUTOMATIC |
| gtid_owned | |
| gtid_purged | |
| session_track_gtids | OFF |
+----------------------------------+-----------+
8 rows in set (0.00 sec)
2.3.2 在从库进行GTID授权
在从库进入MySQL,关闭slave,进行GTID授权
mysql> stop slave;
Query OK, 0 rows affected (0.00 sec)
mysql> CHANGE MASTER TO MASTER_HOST = '192.168.112.101',
-> MASTER_USER = 'repl',
-> MASTER_PASSWORD = 'password',
-> MASTER_AUTO_POSITION = 1;
Query OK, 0 rows affected, 2 warnings (0.01 sec)
mysql> start slave;
Query OK, 0 rows affected (0.09 sec)
mysql> show slave status\G
*************************** 1. row ***************************
Slave_IO_State: Waiting for master to send event
Master_Host: 192.168.112.101
Master_User: repl
Master_Port: 3306
Connect_Retry: 60
Master_Log_File: mysql-bin.000006
Read_Master_Log_Pos: 154
Relay_Log_File: tango-centos02-relay-bin.000002
Relay_Log_Pos: 367
Relay_Master_Log_File: mysql-bin.000006
Slave_IO_Running: Yes
Slave_SQL_Running: Yes
以下省略...
2.3.3 测试GTID
1)在主库插入信息:
mysql> insert into test01.tb01 values('user3','123');
Query OK, 1 row affected (0.01 sec)
mysql> select * from test01.tb01;
+----------+----------+
| username | password |
+----------+----------+
| user1 | 123 |
| user2 | 123 |
| user3 | 123 |
+----------+----------+
3 rows in set (0.00 sec)
mysql> show master status;
+------------------+----------+--------------+------------------+----------------------------------------+
| File | Position | Binlog_Do_DB | Binlog_Ignore_DB | Executed_Gtid_Set |
+------------------+----------+--------------+------------------+----------------------------------------+
| mysql-bin.000006 | 444 | test01 | test02 | e5fb2194-eb27-11ea-b838-000c2992e812:1 |
+------------------+----------+--------------+------------------+----------------------------------------+
1 row in set (0.00 sec)
2)在从库测试,结果同步
mysql> select * from test01.tb01;
+----------+----------+
| username | password |
+----------+----------+
| user1 | 123 |
| user2 | 123 |
| user3 | 123 |
+----------+----------+
3 rows in set (0.00 sec)
mysql> SELECT * FROM mysql.gtid_executed;
+--------------------------------------+----------------+--------------+
| source_uuid | interval_start | interval_end |
+--------------------------------------+----------------+--------------+
| e5fb2194-eb27-11ea-b838-000c2992e812 | 1 | 1 |
+--------------------------------------+----------------+--------------+
1 row in set (0.00 sec)
gtid_executed表中记录了同步复制信息,同步的源uuid与主节点的uuid相同,说明是基于gtid同步复制。
参考资料
https://www.hoohack.me/2017/07/11/learning-mysql-replication-detail
http://dev.mysql.com/doc/refman/5.6/en/replication-options-gtids.html