前言
听说大家现在都在积极响应国家号召,没事的时候会推着三轮车去街角、天桥摆个地摊。博主刚刚收摊回来,顺手写了篇MySQL。
MySQL相信大家都很熟悉了,单节点MySQL出现性能瓶颈的时候,大家首先想到的是优化SQL。但是单节点毕竟能力有限,所以在优化之后,还是无法满足性能要求时,就会想到部署MySQL读写分离,也就是主从复制。除了性能瓶颈之外,还有单节点故障、单节点容量等问题,都必须依靠集群才能解决。本篇主要讲解MySQL从单机到集群的原理和实践。
环境
- VMware Workstation 15
- CentOS Linux release 7.7.1908
- MySQL 5.7.30
注意事项
- 三个节点
ip
分别为192.168.1.101
、192.168.1.102
、192.168.1.103
- 确保三个节点都能访问互联网,并且三个节点能够相互通信
- 确保Linux的
yum
、wget
等基础命令可用 - 建议先关闭防火墙,Centos 7操作如下
firewall-cmd --state ## 查看防火墙状态 not running表示已经关闭 systemctl stop firewalld.service ## 关闭防火墙 systemctl disable firewalld.service ## 禁止开机启动防火墙
需要搭建的主从复制集群如下
单机安装
安装
整个过程请保持网络畅通
wget -i -c http://dev.mysql.com/get/mysql57-community-release-el7-10.noarch.rpm
yum -y install mysql57-community-release-el7-10.noarch.rpm
yum -y install mysql-community-server
到这一步,MySQL服务的安装已经完成
启动
启动MySQL
systemctl start mysqld.service
查看运行状态
systemctl status mysqld.service
执行结果
● mysqld.service - MySQL Server
Loaded: loaded (/usr/lib/systemd/system/mysqld.service; enabled; vendor preset: disabled)
Active: active (running) since 二 2020-06-02 21:58:25 CST; 11s ago
Docs: man:mysqld(8)
http://dev.mysql.com/doc/refman/en/using-systemd.html
Process: 2160 ExecStart=/usr/sbin/mysqld --daemonize --pid-file=/var/run/mysqld/mysqld.pid $MYSQLD_OPTS (code=exited, status=0/SUCCESS)
Process: 2111 ExecStartPre=/usr/bin/mysqld_pre_systemd (code=exited, status=0/SUCCESS)
Main PID: 2163 (mysqld)
CGroup: /system.slice/mysqld.service
└─2163 /usr/sbin/mysqld --daemonize --pid-file=/var/run/mysqld/mysqld.pid
6月 02 21:58:18 localhost.localdomain systemd[1]: Starting MySQL Server...
6月 02 21:58:25 localhost.localdomain systemd[1]: Started MySQL Server.
根据日志可以看到MySQL Server已经成功启动
找到临时密码
在MySQL的启动日志中,会打印一个临时密码用于登录,用户名是root
cat /var/log/mysqld.log |grep password
执行结果
2020-06-02T13:58:21.946584Z 1 [Note] A temporary password is generated for root@localhost: cScTwtyMl4;*
可以看到我此处的临时密码是cScTwtyMl4;*
用这个密码登录,登录之后需要修改密码,才能进行其他的操作,且密码要满足一定的复杂度。
alter user 'root'@'localhost' identified by 'your password';
因为安装了Yum Repository,以后每次yum
操作都会自动更新,需要把这个卸载掉
yum -y remove mysql57-community-release-el7-10.noarch
至此,此时MySQL的单机安装已经完成。其余节点均可按照此方法安装。
异步复制
原理
不仅是MySQL,几乎所有的主从复制集群,都是master/slave
模式,也就是slave
节点从master
节点上同步数据。MySQL主从复制的整个过程如下图所示
整个过程可以分为三个步骤
master
节点把对数据的修改记录到bin log
中,所以master
节点必须开启bin log
slave
节点会从指定的位置(logfile
和偏移量pos
)开始读取master
的bin log
,把读取到的日志写入自己的relay log
中slave
节点根据relay log
中的日志进行重放(replay
),slave
节点可以配置是否需要写入自己的bin log
了解了MySQL主从复制的基本原理,再来进行主从复制的搭建,就会容易理解很多。
异步复制缺点
为了了解异步复制的缺点,先看如下图
如图所示,描述了一主两从的异步复制集群,相信大家也很容易看出缺点。假设master
节点commit
之后就宕机了,而此时slave
节点可能还没有读到master
的全部bin log
,就会导致数据丢失。
异步复制不仅不能保证master
和slave
之间的数据一致性,甚至不能保证slave
与slave
之间的数据一致性。
半同步复制
MySQL5.6对异步复制做了改进,引入半同步复制。
半同步复制过程如下
半同步复制在MySQL5.6中被引入,相比于异步复制,主要的改进就是在master
写完bin log
之后不会直接commit
,而是收到slave
节点的ACK
之后才会commit
,期间master
的commit
操作被阻塞。当然,为了防止部分slave
节点故障导致master
迟迟收到不ACK
。master
的commit
操作可以设置超时时间,超时之后,半同步复制降级为异步复制。
有经验的同学应该可以看出来,这其实就是两阶段提交。master
节点commit
的时候,slave
已经读取了master
的完整bin log
。即使此时master
宕机,slave
节点也能通过重放,实现和master
节点的数据同步。
基于GTID复制
前文说讲的主从复制集群是依靠logfile + pos
的方式实现,除了这种方式外,还有一种就是基于GTID
的主从复制。GTID
(Global Transaction ID
)是全局事务ID。
GTID
的结构如下:source_id:transaction_id
,组成分成两个部分source_id
和transaction_id
,分别代表执行事务的主机UUID,和事务ID。事务ID是递增的,保证不重复。
master
节点修改数据时,把GTID
写入bin log
slave
读取master
节点的bin log
,写入到自己的relay log
slave
节点的sql_thread
从relay log
获取GTID
,查找自己的bin log
中是否有对应的记录- 如果有,说明该
GTID
的事务已经在slave
上执行,slave
会忽略该事务 - 如果没有,
slave
就会从relay log
中执行该事务,并记录到bin log
整个过程与logfile + pos
方式几乎一致,只是获取执行偏移的方式不同。并且slave
节点必须开启bin log
。
基于
GTID
的复制和基于日志点的复制有什么区别?
- 基于日志点的复制是MySQL实现的第一种复制方式,几乎所有的MySQL分支版本都支持
- 基于日志点的复制,
slave
请求master
的增量日志依赖于日志偏移量 - 基于日志点的复制配置复制链路时,需要指定
master_log_file
和master_log_pos
参数,一旦master
宕机,很难从新的master
中找到正确的偏移量的值
基于日志点的复制 | 基于GTID 的复制 |
---|---|
兼容性好 | 老版本MySQL 及MariaDB 不兼容 |
支持MMM 和MHA 架构 | 仅支持MHA 架构 |
主从切换后很难找到新的同步点 | 基于事务ID 的复制,可以很方便的找到未完成的同步的事务ID |
可以方便的跳过复制错误 | 只能置入空事务的方式跳过复制 |
优先选择基于GTID
的复制,不能选择GTID
的复制方式时,再使用基于日志点的复制。
实践
异步复制
-
开启
bin log
,使用如下命令查看bin log
是否开启show variables like '%log_bin%'; +---------------------------------+-------+ | Variable_name | Value | +---------------------------------+-------+ | log_bin | OFF | | log_bin_basename | | | log_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)
log_bin
为OFF
表示未开启,可以使用如下方式开启bin log
vim /etc/my.cnf
配置如下内容
## 开启bin log log-bin=/var/lib/mysql/mysql-bin ## 服务节点ID,每个节点不一样 server-id=101 ## 开启GTID复制模式(如果只想用基于日志点复制,不需要配置这里) gtid-mode=on enforce-gtid-consistency=1
重启MySQL之后,再次查看
bin log
开启状态,可以看到bin log
已经开启show variables like '%log_bin%'; +---------------------------------+--------------------------------+ | Variable_name | Value | +---------------------------------+--------------------------------+ | log_bin | ON | | log_bin_basename | /var/lib/mysql/mysql-bin | | log_bin_index | /var/lib/mysql/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)
slave
节点均按照此方式配置 -
master
创建复制账号并授权
需要自己指定密码create user repl@'192.168.1.%' identified by 'your password'; grant replication slave on *.* to repl@'192.168.1.%';
-
备份
master
数据mysqldump --single-transaction -uroot -p --routines --triggers --events --master-data=2 --all-databases > sicimike.sql
可以查看备份的文件中,有
GTID
和logfile:pos
相关的信息
将备份的文件导入到slave
节点的数据库。 -
在
slave
节点上配置复制链路## MASTER_LOG_FILE和MASTER_LOG_POS的值在前一步中可以找到 change master to master_host='192.168.1.101', MASTER_LOG_FILE='mysql-bin.000002', MASTER_LOG_POS=2615;
执行成功后,可以查看
slave
节点的状态show slave status\G; *************************** 1. row *************************** Slave_IO_State: Master_Host: 192.168.1.101 Master_User: Master_Port: 3306 Connect_Retry: 60 Master_Log_File: mysql-bin.000002 Read_Master_Log_Pos: 2615 Relay_Log_File: localhost-relay-bin.000001 Relay_Log_Pos: 4 Relay_Master_Log_File: mysql-bin.000002 Slave_IO_Running: No Slave_SQL_Running: No ......
-
启动复制链路
## 用户名和密码就是在master上配置的用户名和密码 start slave user='repl' password= 'your password';
再次查看
slave
节点的状态show slave status\G; *************************** 1. row *************************** Slave_IO_State: Waiting for master to send event Master_Host: 192.168.1.101 Master_User: repl Master_Port: 3306 Connect_Retry: 60 Master_Log_File: mysql-bin.000002 Read_Master_Log_Pos: 2615 Relay_Log_File: localhost-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
状态,这两个线程就是前文主从复制原理图中io_thread
和sql_thread
其余的节点也是以同样的方式配置成
slave
节点。
至此,基于logfile:pos
模式的异步复制已经配置完成。在master
节点上进行数据的修改,马上就会同步到slave库
。
可以在master
节点上执行show slave hosts
查看追随自己的所有slave
节点
show slave hosts;
+-----------+------+------+-----------+--------------------------------------+
| Server_id | Host | Port | Master_id | Slave_UUID |
+-----------+------+------+-----------+--------------------------------------+
| 103 | | 3306 | 101 | d6532e2a-a592-11ea-99c3-000c297f5b55 |
| 102 | | 3306 | 101 | 1dbd5375-a4d9-11ea-9eef-000c29cf4cca |
+-----------+------+------+-----------+--------------------------------------+
2 rows in set (0.00 sec)
接下来开始配置半同步复制。
半同步复制
- 首先查看
master
是否安装了半同步复制插件rpl_semi_sync_master
,可以使用如下命令查看
如果没有安装的话,使用如下命令安装show plugins;
install plugin rpl_semi_sync_master soname 'semisync_master.so';
- 配置
master
相关变量
首先查看半同步复制相关的变量show variables like 'rpl%'; +-------------------------------------------+------------+ | Variable_name | Value | +-------------------------------------------+------------+ | rpl_semi_sync_master_enabled | OFF | | rpl_semi_sync_master_timeout | 10000 | | rpl_semi_sync_master_trace_level | 32 | | rpl_semi_sync_master_wait_for_slave_count | 1 | | rpl_semi_sync_master_wait_no_slave | ON | | rpl_semi_sync_master_wait_point | AFTER_SYNC | | rpl_stop_slave_timeout | 31536000 | +-------------------------------------------+------------+ 7 rows in set (0.01 sec)
rpl_semi_sync_master_enabled
变量需要改成ON
rpl_semi_sync_master_timeout
表示半同步复制的超时时间,可以适当修改
依然是修改/etc/my.cof
文件,配置如下内容
配置完成后重启MySQL即可rpl_semi_sync_master_enabled=on rpl_semi_sync_master_timeout=500
- 查看
slave
是否安装了半同步复制插件rpl_semi_sync_slave
可以使用如下命令
如果没有安装的话,使用如下命令安装show plugins;
install plugin rpl_semi_sync_slave soname 'semisync_slave.so';
- 配置
slave
相关变量
首先查看半同步复制相关的变量show variables like 'rpl%'; +---------------------------------+----------+ | Variable_name | Value | +---------------------------------+----------+ | rpl_semi_sync_slave_enabled | OFF | | rpl_semi_sync_slave_trace_level | 32 | | rpl_stop_slave_timeout | 31536000 | +---------------------------------+----------+ 3 rows in set (0.01 sec)
rpl_semi_sync_slave_enabled
变量需要改成ON
,需要修改/etc/my.cof
文件,配置如下内容rpl_semi_sync_slave_enabled=on
slave
完成配置,重启MySQL后,再次查看slave
的状态
可以看到,show slave status\G; *************************** 1. row *************************** Slave_IO_State: Master_Host: 192.168.1.101 Master_User: Master_Port: 3306 Connect_Retry: 60 Master_Log_File: mysql-bin.000003 Read_Master_Log_Pos: 194 Relay_Log_File: localhost-relay-bin.000005 Relay_Log_Pos: 4 Relay_Master_Log_File: mysql-bin.000003 Slave_IO_Running: No Slave_SQL_Running: Yes .....
Slave_SQL_Running
依然是Yes
,只有Slave_IO_Running
是NO
,所以只需要重启slave
的IO线程即可
启动完成后,基于start slave io_thread user='repl' password = 'your password';
logfile:pos
模式的半同步复制也就配置完成。
GTID复制
由于上面的实验,我们已经启动了logfile + pos
方式的复制链路,所以要改成GTID
方式,先要停止slave
,再重新配置复制链路
- 停止
slave
在slave
节点上执行如下命令stop slave;
- 配置复制链路
change master to master_host='192.168.1.101', master_user='repl', master_password='your password', master_auto_position=1;
- 启动复制链路
start slave;
查看链路状态
show slave status \G;
*************************** 1. row ***************************
Slave_IO_State: Waiting for master to send event
Master_Host: 192.168.1.101
Master_User: repl
Master_Port: 3306
Connect_Retry: 60
Master_Log_File: mysql-bin.000003
Read_Master_Log_Pos: 811
Relay_Log_File: localhost-relay-bin.000002
Relay_Log_Pos: 414
Relay_Master_Log_File: mysql-bin.000003
Slave_IO_Running: Yes
Slave_SQL_Running: Yes
......
Master_SSL_Crl:
Master_SSL_Crlpath:
Retrieved_Gtid_Set:
Executed_Gtid_Set: 1dbd5375-a4d9-11ea-9eef-000c29cf4cca:1,
81502f9e-a592-11ea-b912-000c2928707c:1-11
Auto_Position: 1
......
可以看到Auto_Position
变成了1
,说明启动了基于GTID
方式的复制。
主从复制延迟
主从复制是一种异步复制模型,延迟自然是不可避免。只要延迟在业务能够接受的范围之内,都是可以容忍的。但是有时候主从复制的延迟会很大,可以总结为以下原因
master
上执行了大事务,大事务是指耗时很长的事务。改进办法是把大事务转换成多个小事务。master
和slave
之间网络波动- 单个
master
节点下挂载的slave
节点过多,可能会把master
网卡带宽打满。改进办法就是减少单个master
挂载slave
的数量 master
节点会有多个线程同时写入,而slave
节点进行replay
的sql_thread
只有一个。改进办法就是使用MySQL 5.7
的多线程复制的方式、或者使用MGR
复制架构。
总结
本篇主要从理论到实践,完整的讲解了MySQL的几种主从模型。根据复制点的选取方式,可以分成基于日志点的方式和基于GTID的方式。根据master
节点事务的提交方式可以分成异步复制和半同步复制。
至于文中提到的MMM
和MHA
架构,留待下一篇再详细讲解。
参考
- https://www.cnblogs.com/bigbrotherer/p/7241845.html
- https://blog.51cto.com/13434336/2178937