暂无图片
暂无图片
1
暂无图片
暂无图片
暂无图片

mysql8.0 主从原理

原创 抖草 2023-11-02
355

文章目录
1. 主从架构的作用
1.1 binlog主从数据同步原理
1.2 GITD主从数据同步
2. 主从集群架构搭建
2.1 配置master主服务器
2.2 配置slave从服务器
2.3 主从架构的数据延迟问题
3. 全库同步与部分同步
4. 读写分离
5. 其他集群配置方式
5.1 一主多从
5.2 互主集群
6. 半同步复制
6.1 什么是半同步复制
6.2 搭建半同步复制集群


1. 主从架构的作用
通过搭建MySQL主从集群,可以缓解MySQL的数据存储以及访问的压力,具体如下:

数据备份。给主服务增加一个数据备份,保障数据安全。基于这个目的,可以搭建主从架构,或者也可以基于主从架构搭建互主的架构。
故障转移-高可用。当MySQL主服务宕机后,可以由一台从服务切换成为主服务,继续提供数据读写功能。对于高可用架构,主从数据的同步也只是实现故障转移的一个前提条件,要实现MySQL主从切换,还需要依靠一些其他的中间件来实现。比如MMM、MHA、MGR。
读写分离。对于大部分的JAVA业务系统来说,都是读多写少的,读请求远远高于写请求。这时,当主服务的访问压力过大时,可以将数据读请求转为由从服务来分担,主服务只负责数据写入的请求,这样大大缓解数据库的访问压力。要理解,MySQL的主从架构只是实现读写分离的一个基础。实现读写分离还是需要一些中间件来支持,比如ShardingSphere。
在一般项目中,如果数据库的访问压力没有那么大,那读写分离不一定是必须要
做的,但是,主从架构做数据备份和高可用架构则是必须要搭建的。


1.1 binlog主从数据同步原理

MySQL服务的主从架构一般都是通过binlog日志文件来进行的。通常对MySQL做的读写分离配置就必须基于主从架构来搭建。

在主服务上打开binlog记录每一步的数据库操作
然后从服务上会有一个IO线程,负责跟主服务建立一个TCP连接,请求主服务将binlog传输过来。
这时主库上会有一个IO dump线程,负责通过这个TCP连接把Binlog日志传输给从库的IO线程。
接着从服务的IO线程会把读取到的binlog日志数据写入自己的relay日志文件中。
然后从服务上另外一个SQL线程会读取relay日志里的内容,进行操作重演,达到还原数据的目的。
注意:
MySQL的binlog不光可以用于主从同步,还可以用于缓存数据同步等场景。 例如Canal,可以模拟一个slave节点,向MySQL发起binlog同步,然后将数据落地到Redis、Kafka等其他组件,实现数据实时流转。


1.2 GITD主从数据同步
Binlog日志是最为传统的MySQL集群搭建方式,然而在MySQL5.6版本引入了GTID集群搭建方式。GTID即全局事务ID,全局唯一并且趋势递增,他可以保证为每一个在主节点上提交的事务在复制集群中可以生成一个唯一的ID。GTID的本质也是基于Binlog来实现主从同步,只是他会基于一个全局的事务ID来标识同步进度。

在基于GTID的复制中,首先从服务器会告诉主服务器已经在从服务器执行完了哪些事务的GTID值,然后主库会有把所有没有在从库上执行的事务,发送到从库上进行执行,并且使用GTID的复制可以保证同一个事务只在指定的从库上执行一次,这样可以避免由于偏移量的问题造成数据不一致。

GITD搭建方式和主从架构整体搭建方式差不多只是需要在my.cnf中修改一些配置。

主节点配置

gtid_mode=on
enforce_gtid_consistency=on
log_bin=on
server_id=单独设置一个
binlog_format=row

从节点

gtid_mode=on
enforce_gtid_consistency=on
log_slave_updates=1
server_id=单独设置一个

然后分别重启主服务和从服务,就可以开启GTID同步复制方式。



2. 主从集群架构搭建
点此查看linux单机mysql8.0以上版本安装教程

注意:在连接mysql的过程中,如果用户名和密码不正确,需要开启安全模式修改密码后方可连入!
mysql8.0修改密码方法
克隆多台虚拟机模拟服务器集群方案

2.1 配置master主服务器
首先,配置主节点的mysql配置文件: /etc/my.cnf 这一步需要对master进行配置,主要是需要打开binlog日志,以及指定severId。我们打开MySQL主服务的my.cnf文件,在文件中一行server-id以及一个关闭域名解析的配置。然后重启服务。

[mysqld]
server-id=47
#开启binlog
log_bin=master-bin
log_bin-index=master-bin.index
skip-name-resolve
# 设置连接端口
port=3306
# 设置mysql的安装目录
basedir=/usr/local/mysql
# 设置mysql数据库的数据的存放目录
datadir=/usr/local/mysql/mysql-files
# 允许最大连接数
max_connections=200
# 允许连接失败的次数。
max_connect_errors=10
# 服务端使用的字符集默认为UTF8
character-set-server=utf8
# 创建新表时将使用的默认存储引擎
default-storage-engine=INNODB
# 默认使用“mysql_native_password”插件认证
#mysql_native_password
default_authentication_plugin=mysql_native_password

更多配置查看上边链接中的配置,可根据需求增加

配置说明:主要需要修改的是以下几个属性:

server-id:服务节点的唯一标识。需要给集群中的每个服务分配一个单独的ID。
log_bin:打开Binlog日志记录,并指定文件名。
log_bin-index:Binlog日志文件
然后重启MySQL服务, service mysqld restart
然后给root用户分配一个replication slave(从节点复制)的权限。

# 登录主数据库
mysql -u root -p
# 授权
GRANT REPLICATION SLAVE ON *.* TO 'root'@'%';
# 刷新
flush privileges;
# 查看主节点同步状态:
show master status;

查看结果如下:

开启binlog后,数据库中的所有操作都会被记录到datadir当中,以一组轮询文件的方式循环记录。而指令查到的File和Position就是当前日志的文件和位置。而在后面配置从服务时,就需要通过这个File和Position通知从服务从哪个地方开始记录binLog。


2.2 配置slave从服务器
打开mysqls的配置文件my.cnf,修改配置文件:

[mysqld]
#主库和从库需要不一致
server-id=48
#打开MySQL中继日志
relay-log-index=slave-relay-bin.index
relay-log=slave-relay-bin
#打开从服务二进制日志
log-bin=mysql-bin
#使得更新的数据写进二进制日志中
log-slave-updates=1
# 设置3306端口
port=3306
# 设置mysql的安装目录
basedir=/usr/local/mysql
# 设置mysql数据库的数据的存放目录
datadir=/usr/local/mysql/mysql-files
# 允许最大连接数
max_connections=200
# 允许连接失败的次数。
max_connect_errors=10
# 服务端使用的字符集默认为UTF8
character-set-server=utf8
# 创建新表时将使用的默认存储引擎
default-storage-engine=INNODB
# 默认使用“mysql_native_password”插件认证
#mysql_native_password
default_authentication_plugin=mysql_native_password

更多配置查看上边链接中的配置,可根据需求增加

配置说明:主要需要关注的几个属性:

server-id:服务节点的唯一标识
relay-log:打开从服务的relay-log日志。
log-bin:打开从服务的bin-log日志记录。
然后我们启动mysqls的服务,并设置他的主节点同步状态。

#登录从服务
mysql -u root -p;

#设置同步主节点:
CHANGE MASTER TO
MASTER_HOST='192.168.232.128',
MASTER_PORT=3306,
MASTER_USER='root',
MASTER_PASSWORD='root',
MASTER_LOG_FILE='master-bin.000004',
MASTER_LOG_POS=156
GET_MASTER_PUBLIC_KEY=1;

#开启slave
start slave;

#查看主从同步状态
show slave status;
或者用 show slave status \G; 这样查看比较简洁


注意,CHANGE MASTER指令中需要指定的MASTER_LOG_FILE和MASTER_LOG_POS必须与主服务中查到的保持一致。

并且后续如果要检查主从架构是否成功,也可以通过检查主服务与从服务之间的File和Position这两个属性是否一致来确定。

可以看到从节点状态中相关信息与主节点保持一致,就表示这个主从同步搭建是成功的。


数据库同步测试:
主从架构搭建完成后,需要进行测试。首先使用命令查看两个服务器的数据库:


可以看到,主从服务器原始都有四个数据库,然后我们在主服务器上新创建一个数据库syncdemo,看看效果如何!

在使用show databases 命令查看从服务器是否进行了主从复制

至此,主从架构搭建完成!

另外,这个主从架构是有可能失败的,如果在slave从服务上查看slave状态,发现Slave_SQL_Running=no,就表示主从同步失败了。这有可能是因为在从数据库上进行了写操作,与同步过来的SQL操作冲突了,也有可能是slave从服务重启后有事务回滚了。

如果是因为slave从服务事务回滚的原因,可以按照以下方式重启主从同步:

mysql> stop slave ;
mysql> set GLOBAL SQL_SLAVE_SKIP_COUNTER=1;
mysql> start slave ;

而另一种解决方式就是重新记录主节点的binlog文件消息(与上文的类似)

mysql> stop slave ;
mysql> change master to .....
mysql> start slave ;

但是这种方式要注意binlog的文件和位置,如果修改后和之前的同步接不上,那就会丢失部分数据。所以不太常用。


2.3 主从架构的数据延迟问题
这个主从集群中,有一个比较隐藏的问题,就是这样的主从复制之间会有延迟。这在做了读写分离后,会更容易体现出来。即数据往主服务写,而读数据在从服务读。这时候这个主从复制延迟就有可能造成刚插入了数据但是查不到。在一主一从这个集群中是很难出现的,但是在大型集群中会很容易出现。

出现这个问题的根本在于:面向业务的主服务数据都是多线程并发写入的,而从服务是单个线程慢慢拉取binlog,这中间就会有个效率差。所以解决这个问题的关键是要让从服务也用多线程并行复制binlog数据。

# 查看从服务器上拉取binlog的线程数命令
show global variables like 'slave_parallel_%‘;


MySQL自5.7版本后才支持并行复制。可以在从服务上设置slave_parallel_workers为一个大于0的数,然后把slave_parallel_type参数设置为LOGICAL_CLOCK,这就可以了。

set GLOBAL slave_parallel_type = LOGICAL_CLOCK;
# 设置线程数
set GLOBAL slave_parallel_workers = 2;
# 查看线程数
show global variables like 'slave_parallel_%';
结果如下:从节点拉取binlog已更改为5个线程去执行!


3. 全库同步与部分同步
之前提到,我们目前配置的主从同步是针对全库配置的,而实际环境中,一般并不需要针对全库做备份,而只需要对一些特别重要的库或者表来进行同步。那如何针对库和表做同步配置呢?

首先在Master端:在my.cnf中,可以通过以下这些属性指定需要针对哪些库或者哪些表记录binlog

# 需要同步的二进制数据库名
binlog-do-db=masterdemo

# 只保留7天的二进制日志,以防磁盘被日志占满(可选)
expire-logs-days = 7

# 不备份的数据库
binlog-ignore-db=information_schema
binlog-ignore-db=performation_schema
binlog-ignore-db=sys

然后在Slave端:在my.cnf中,需要配置备份库与主服务的库的对应关系。

# 如果salve库名称与master库名相同,使用本配置
replicate-do-db = masterdemo

# 如果master库名[mastdemo]与salve库名[mastdemo01]不同,使用以下配置[需要做映射]
replicate-rewrite-db = masterdemo -> masterdemo01

# 如果不是要全部同步[默认全部同步],则指定需要同步的表
replicate-wild-do-table=masterdemo01.t_dict
replicate-wild-do-table=masterdemo01.t_num

配置完成了之后,在show master status指令中,就可以看到Binlog_Do_DB和Binlog_Ignore_DB两个参数的作用了。


4. 读写分离
目前我们的这个MySQL主从集群是单向的,也就是只能从主服务同步到从服务,而从服务的数据表更是无法同步到主服务的。

所以,在这种架构下,为了保证数据一致,通常会需要保证数据只在主服务上写,而从服务只进行数据读取。这个功能,就是大名鼎鼎的读写分离。但是这里要注意下,mysql主从本身是无法提供读写分离的服务的,需要由业务自己来实现。可以使用ShardingSphere来实现,可以关注我的其他博文。

在MySQL主从架构中,是需要严格限制从服务的数据写入的,一旦从服务有数据写入,就会造成数据不一致。并且从服务在执行事务期间还很容易造成数据同步失败。如果需要限制用户写数据,我们可以在从服务中将read_only参数的值设为1( set global read_only=1; )。这样就可以限制用户写入数据。但是这个属性有两个需要注意的地方:

read_only=1设置的只读模式,不会影响slave同步复制的功能。 所以在MySQL
slave库中设定了read_only=1后,通过 “show slave status\G” 命令查看salve状态,可以看到salve仍然会读取master上的日志,并且在slave库中应用日志,保证主从数据库同步一致;
read_only=1设置的只读模式, 限定的是普通用户进行数据修改的操作,但不会限定具有super权限的用户的数据修改操作。 在MySQL中设置read_only=1后,普通的应用用户进行insert、update、delete等会产生数据变化的DML操作时,都会报出数据库处于只读模式不能发生数据变化的错误,但具有super权限的用户,例如在本地或远程通过root用户登录到数据库,还是可以进行数据变化的DML操作;
如果需要限定super权限的用户写数据,可以设置super_read_only=0。
如果要想连super权限用户的写操作也禁止,就使用"flush tables with read lock;",这样设置也会阻止主从同步复制!


5. 其他集群配置方式
我们到这里搭建出了一个一主一从的MySQL主从同步集群,具有了数据同步的基础功能。而在生产环境中,通常会以此为基础,根据业务情况以及负载情况,搭建更大更复杂的集群。

5.1 一主多从
为了进一步提高整个集群的读能力,可以扩展出一主多从。而为了减轻主节点进行数据同步的压力,可以继续扩展出多级从的主从集群。要扩展到一主多从的集群架构,只需要增加一台机器,配置方式参考上文从服务器配置即可

但是如果我们的集群是已经运行过一段时间,这时候如果要扩展新的从节点就有一个问题,之前的数据没办法从binlog来恢复了。这时候在扩展新的slave节点时,就需要增加一个数据复制的操作。MySQL的数据备份恢复操作相对比较简单,可以通过SQL语句直接来完成。具体操作可以使用mysql的bin目录下的mysqldump工具。

mysqldump -u root -p --all-databases > backup.sql
#输入密码

通过这个指令,就可以将整个数据库的所有数据导出成backup.sql,然后把这个backup.sql分发到新的MySQL服务器上,并执行下面的指令将数据全部导入到新的MySQL服务中。

mysql -u root -p < backup.sql
#输入密码

这样新的MySQL服务就已经有了所有的历史数据,然后就可以再按照上面的步骤,配置Slave从服务的数据同步了。


5.2 互主集群
为了实现MySQL多活部署,也可以采用互为主从的互主集群甚至是环形的主从集群。 搭建互主集群只需要按照上面的方式,在主服务上打开一个slave进程,并且指向slave节点的binlog当前文件地址和位置。


6. 半同步复制
6.1 什么是半同步复制
上文已经搭建了MySQL的主从集群,互主集群,但是我们这个集群有一个隐患,就是有可能会丢数据。这是为什么呢?这要从MySQL主从数据复制分析起。MySQL主从集群默认采用的是一种异步复制的机制。主服务在执行用户提交的事务后,写入binlog日志,然后就给客户端返回一个成功的响应了。而binlog会由一个dump线程异步发送给Slave从服务。如下图

由于这个发送binlog的过程是异步的。主服务在向客户端反馈执行结果时,是不知道binlog是否同步成功了的。这时候如果主服务宕机了,而从服务还没有备份到新执行的binlog,那就有可能会丢数据。那怎么解决这个问题呢,这就要靠MySQL的半同步复制机制来保证数据安全。

半同步复制机制是一种介于异步复制和全同步复制之前的机制。主库在执行完客户端提交的事务后,并不是立即返回客户端响应,而是等待至少一个从库接收并写到relay log中,才会返回给客户端。MySQL在等待确认时,默认会等10秒,如果超过10秒没有收到ack,就会降级成为异步复制。



这种半同步复制相比异步复制,能够有效的提高数据的安全性。但是这种安全性也不是绝对的,隐患如下:

半同步复制只保证事务提交后的binlog至少传输到了一个从库,并且并不保证从库应用这个binlog日志是成功的(写成功)
半同步复制机制也会造成一定程度的延迟,这个延迟时间最少是一个TCP/IP请求往返的时间。整个服务的性能是会有所下降的。而当从服务出现问题时,主服务需要等待的时间就会更长,要等到从服务的服务恢复或者请求超时才能给用户响应。
在生产环境中,基本都会选择开启半同步复制!



6.2 搭建半同步复制集群
半同步复制需要基于特定的扩展模块来实现。而mysql从5.5版本开始,往上的版本都默认自带了这个模块。这个模块包含在mysql安装目录下的lib/plugin目录下的semisync_master.so和semisync_slave.so两个文件中。

步骤如下:
①:登陆主服务,安装semisync_master模块:

# 在主服务上安装semisync_master模块#
mysql> install plugin rpl_semi_sync_master soname 'semisync_master.so';
Query OK, 0 rows affected (0.01 sec)

# 半同步复制默认配置
mysql> show global variables like 'rpl_semi%';
+-------------------------------------------+------------+
| Variable_name | Value |
+-------------------------------------------+------------+
| rpl_semi_sync_master_enabled | OFF | 默认关闭
| rpl_semi_sync_master_timeout | 10000 | 10秒超时时间
| 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 | 半同步复制的方式
+-------------------------------------------+------------+
6 rows in set, 1 warning (0.02 sec)

# 打开半同步复制
mysql> set global rpl_semi_sync_master_enabled=ON;
Query OK, 0 rows affected (0.00 sec)


在第二行查看系统参数时,最后的一个参数rpl_semi_sync_master_wait_point其实表示一种半同步复制的方式。半同步复制有两种方式:

一种是我们现在看到的这种默认的AFTER_SYNC方式。这种方式下,主库把日志写入binlog,并且复制给从库,然后开始等待从库的响应。从库返回成功后,主库再提交事务,接着给客户端返回一个成功响应。
而另一种方式是叫做AFTER_COMMIT方式。他不是默认的。这种方式,在主库写入binlog后,等待binlog复制到从库,主库就提交自己的本地事务,再等待从库返回给自己一个成功响应,然后主库再给客户端返回响应。
②:登录从服务器,安装semisync_slave模块。

mysql> install plugin rpl_semi_sync_slave soname 'semisync_slave.so';
Query OK, 0 rows affected (0.01 sec)

mysql> show global variables like 'rpl_semi%';
+---------------------------------+-------+
| Variable_name | Value |
+---------------------------------+-------+
| rpl_semi_sync_slave_enabled | OFF |
| rpl_semi_sync_slave_trace_level | 32 |
+---------------------------------+-------+
2 rows in set, 1 warning (0.01 sec)

mysql> set global rpl_semi_sync_slave_enabled = on;
Query OK, 0 rows affected (0.00 sec)

mysql> show global variables like 'rpl_semi%';
+---------------------------------+-------+
| Variable_name | Value |
+---------------------------------+-------+
| rpl_semi_sync_slave_enabled | ON |
| rpl_semi_sync_slave_trace_level | 32 |
+---------------------------------+-------+
2 rows in set, 1 warning (0.00 sec)

mysql> stop slave;
Query OK, 0 rows affected (0.01 sec)

mysql> start slave;
Query OK, 0 rows affected (0.01 sec)


slave端的安装过程基本差不多,不过要注意下安装完slave端的半同步插件后,需要重启下slave服务。

「喜欢这篇文章,您的关注和赞赏是给作者最好的鼓励」
关注作者
【版权声明】本文为墨天轮用户原创内容,转载时必须标注文章的来源(墨天轮),文章链接,文章作者等基本信息,否则作者和墨天轮有权追究责任。如果您发现墨天轮中有涉嫌抄袭或者侵权的内容,欢迎发送邮件至:contact@modb.pro进行举报,并提供相关证据,一经查实,墨天轮将立刻删除相关内容。

评论