MySQL-主从复制一图全搞定

187 阅读8分钟

如图: 就是MySQL的主从复制原理图,好的讲完了。(😄那是不可能的)

image-20241018141916769.png

一句话解释:主从复制

ctrl+c ctrl+v。在主服务器上的操作,通过复制(线程)的方式,在从服务器上也做一次。(😛)

简单来说, 把大象装进冰箱只需要三步:

  1. 主库把数据更改记录到二进制日志(BinaryLog)中
  2. 从库将主库上的日志复制到自己的中继日志(Relay Log)中
  3. 从库读取中继日志中的事件,将其写入到到从库数据库中

详细的说明如下:

  • 主库会将用户的变更操作(DDL/DML,不包括select/show等)写入到 binary log(需要提前开启)日志中。big log 只会记录成功的事件,并按照事务提交的顺序来记录。
  • 从库 会启动一个I/O线程与 主库 连接,并告诉 主库 将要读取的文件及位置
  • 主库 会启动一个特殊的二进制转储线程(binlog dump),它会读取 主库 上的 bin log 中的事件。 如果 从库 追赶上了 主库,则会进入sleep 状态,直到 主库 发送信号量有新的事件产生才会被唤醒
  • 从库接收到数据后,写入到relay log中继日志中
  • 从库开启一个sql线程读取 relay log 中的数据,写入到库中。如果从库把数据写入了自己的 bin log 中,那么在后续的时候,就可以做为其它从库的主库来用,如下图:(需要设置:log_slave_updates)

image-20241021104935992.png

  • 通过异步的方式,进行复制。目前有两种方式:日志点/GTID 两种方式

两种二进制日志格式:基于语句和基于行的复制(binlog_format=STATEMENT/ROW/MIXED)

基于语句的复制(也称为逻辑/段复制):

​ 主库会记录造成数据更改的sql语句,当从库读取到 bin log中的数据时,就会在自己的库上再执行一次这些语句。 ​ 优点:日志记录量相对较小 节约磁盘空间及复制时的网络I/O 例如:这个基于语句的复制就很方便

update table set col1 = 0;

​ 缺点:需要记录上下文信息,这样才能保证在从库执行时结果与主库相同 ​ 如果使用存储过程、触发器、某些特定函数,如UUID(), user()等,可能无法正确的复制,导致主从的不一致 ​ 在执行上需要更多的行锁

基于行的复制:(5.7后,默认)

​ 主库会把实际影响到的数据行记录到 bin log 中, 可以正确的复制每一行数据。 ​ 由于不需要查询库,使用基于行的复制模式能够更高效的复制数据。例如:

insert into table1(col1, col2, sum(colr))
select col1, col2, sum(col3)
from table2
group by col1, col2;

如果查询结果中,实际的数据较少(例如只有三行),使用行复制,在从库上开销就会很小。

​ 优点:使MySQL主从复制更加安全,尽管使用了诸如 UUID(), user(),函数等等,只需要记录受影响的实际数据即可。 ​ 占用小,开销小,降低延迟时间 ​ 某些情况下,可以通过记录,反向处理数据不一致的问题 ​ 可以减少锁的使用

​ 缺点:由于记录了每一行数据,所以在遇到大批量数据时,相比语句的复制,记录量会较大,磁盘空间及IO同样会变大。(注:查询结果可以通过设置 binlog_row_image=FULL/MINIMAL/NOBLOB 来优化记录内容) ​ 由于没有记录执行的语句,所以无法判断执行了哪些SQL

基于混合的复制:

  • MySQL会根据SQL语句由系统来决定使用基于语句/行的日志格式,大部分会使用基于语句的格式,如果遇到特定函数等,则会使用基于行的日志格式来记录

如何选择:

没有哪种是模式完美的 建议: binlog_format=mixed 或者 binlog_format=row binlog_row_image=minimal

基于日志点/GTID的复制方式:

日志点:

需要指定从哪个二进制日志的偏移量进行增量同步
优点: 
	是MySQL最早支持的复制技术,Bug相对较少
	对SQL查询没有任何限制
	故障处理比较容易
缺点: 
	故障转移时重新获取新主的日志点信息比较困难

GITD(5.6之后):

GTID即全局事务ID,其保证为每一个在主上提交的事务在复制集群中可以生成一个唯一的ID 
GTID=server_uuid:transaction_id
优点:
	可以很方便的进行故障转移,因为我们可以根据 GTID 来确定哪些事务还未在从库执行
  从库不会丢失主库上的修改
缺点:
	故障处理比较复杂
	对执行的SQL有一定的限制 

主从复制延迟问题

image-20241023143600396.png

从图上可以知道,延迟主要发生的地方:

  1. 主库写入二进制日志的时间或者说执行事务的时间过长,会导致在之后一传输上,从库数据同步上消耗时间,从而导致延迟 解决:控制主库事务的大小,分割大事务

  2. 二进制日志传输时间 解决:使用 MIXED 日志格式

  3. 默认情况下,从库只有一个SLQ线程。主库上并发的修改在从库上就会变成了串行。(大事务/突发的促销。。。)

    **解决:**使用多线程复制(MySQL5.6之后) 在MySQL5.7中可以按照逻辑时钟的方式来分配SQL线程

  参考配置:
  	stop slave
  	set global slave_parallel_type = 'logical_clock'
  	set global slave_parallel_workers = 4
  	start slave

常见问题处理:(造成主从复制链路中断的情况)

  1. 由于数据损坏或丢失引起的主从复制错误
    • 主库或从库意外宕机引起的错误,导致部分日志没有记录上或同步上,当重启之后再次同步时,出错 解决: 使用跳过二进制日志事件 注入空事务的方式先恢复中断的复制链路 再使用其它方法来对比主从服务器上的数据
    • 主库上的二进制日志损坏 解决: 从库通过 chang master 命令来重新指定 再使用其它方法来对比主从服务器上的数据
    • 从库上的中继日志损坏 解决: 从库通过 chang master 命令来重新从指定的位置读取主库日志
  2. 在从库上进行数据修改造成主从复制链路中断
  • 从库要设置为 read_only (只读),尽量不要修改从库数据
  1. 不唯一的server_id或server_uuid
    • 主库如果存在相同 server_id,在启动复制时会报错
    • 多个从库使用相同的 server_uuid (注:server_uuid是记录在数据目录中的 auto.cnf 文件中,如果文件存在,重启时是不会重新生成的。)
    • max_allow_packet 设置引起的主从复制错误

异步复制和半同步复制

异步复制,所以会带来问题,从还没有复制完成,主宕机了,导致数据不同步。

image-20241023145806620.png

半同步复制(ACK/超时时间) 主服务器发送消息,等从服务器发送ACK,收到后,commit提交; 如果超时,则主服务器不会提交数据;

image-20241015180034806.png

扩展内容:

把从库提升为主库

理论步骤:

  1. 停止向老的主库写入
  2. 让从库赶上主库
  3. 将一台从库配置为主库
  4. 将从库和写操作指向新的主库,然后开启主库的写入

更深入一点,下面是大多数配置需要的步骤:

1.停止当前主库上的所有写操作。如果可以,最好能将所有的客户端程序关闭(除了
复制连接)。为客户端程序建立一个“donotrun”这样的类似标记可能会有所帮助。如果正在使用虚拟IP地址,也可以简单地关闭虚拟IP,然后断开所有的客户端连接以关闭其打开的事务。
2. 通过 FLUSH TABLES WITH READ LOCK在主库上停止所有活跃的写入,这一步是可选
的。也可以在主库上设置read_only选项。从这一刻开始,应该禁止向即将被替换的主库做任何写入。因为一旦它不是主库,写入就意味着数据丢失。注意,即使设置read_only也不会阻止当前已存在的事务继续提交。为了更好地保证这一点,可以“kill”所有打开的事务,这将会真正地结束所有写入。
3.选择一个备库作为新的主库,并确保它已经完全跟上主库(例如,让它执行完所有
从主库获得的中继日志)。
4.确保新主库和旧主库的数据是一致的。可选。
5.在新主库上执行STOP SLAVE。
6.在新主库上执行CHANGE MASTER TO MASTER_HOST='',然后再执行RESET SLAVE,使
其断开与老主库的连接,并丢弃master.info里记录的信息(如果连接信息记录在 my.cnf里,会无法正确工作,这也是我们建议不要把复制连接信息写到配置文件里的原因之一)。
7.执行 SHOW MASTER STATUS记录新主库的二进制日志坐标。
8.确保其他备库已经追赶上。
9.关闭旧主库。
10.在MySQL5.1及以上版本中,如果需要,激活新主库上事件。11.将客户端连接到新主库。
摘自《高性能MySQL第三版》

注:

  • 图片来源于网络,侵删。

  • 参考书籍《高性能mysql第三版》