Mysql 主从复制相关原理简述
Mysql 主从同步基本原理
复制的基本过程如下:
Slave 上面的 IO 进程连接上 Master,并请求从指定日志文件的指定位置(或者从最开始的日志)之后的日志内容;
Master 接收到来自 Slave 的 IO 进程的请求后,通过负责复制的 IO 进程,根据请求信息,读取指定日志指定位置之后的日志信息,返回给 Slave 的 IO 进程。返回信息中除了日志所包含的信息之外,还包括本次返回的信息已经到 Master 端的 bin-log 文件的名称以及 bin-log 的位置;
Slave 的 IO 进程接收到信息后,将接收到的日志内容依次添加到 Slave 端的 relay-log 文件的最末端,并将读取到的 Master 端的 bin-log 的文件名和位置记录到 master-info 文件中,以便在下一次读取的时候能够清楚的告诉 Master “我需要从某个 bin-log 的哪个位置开始往后的日志内容,请发给我”;
Slave 的 Sql 进程检测到 relay-log 中新增加了内容后,会马上解析 relay-log 的内容,获得在 Master 端真实执行的那些可执行的内容,并在自身执行。
双主情况下,禁止同时写入,建议还是按照主从的方式工作,防止数据冲突。双主场景下,主要是切换主备方便。
Mysql 复制方式
异步复制(Asynchronous replication)
MySQL 默认的复制即是异步的,主库在执行完客户端提交的事务后会立即将结果返给给客户端,并不关心从库是否已经接收并处理,这样就会有一个问题,主如果 crash 掉了,此时主上已经提交的事务可能并没有传到从上,如果此时,强行将从提升为主,可能导致新主上的数据不完整。
全同步复制(Fully synchronous replication)
指当主库执行完一个事务,所有的从库都执行了该事务才返回给客户端。因为需要等待所有从库执行完该事务才能返回,所以全同步复制的性能必然会收到严重的影响。
半同步复制(Semisynchronous replication)
介于异步复制和全同步复制之间,主库在执行完客户端提交的事务后不是立刻返回给客户端,而是等待至少一个从库接收到并写到 relay log 中才返回给客户端。相对于异步复制,半同步复制提高了数据的安全性,同时它也造成了一定程度的延迟,这个延迟最少是一个 TCP/IP 往返的时间。所以,半同步复制最好在低延时的网络中使用。半同步复制失败(配置超时时间),自动转为异步复制
半同步复制配置步骤
- 加载使用的插件
主库执行以下命令
INSTALL PLUGIN rpl_semi_sync_master SONAME 'semisync_master.so'; |
从库执行以下命令
INSTALL PLUGIN rpl_semi_sync_slave SONAME 'semisync_slave.so'; |
通过 show plugins; 可查看已加载的插件
- 启动半同步复制
主库执行以下命令
SET GLOBAL rpl_semi_sync_master_enabled = 1; |
从库执行以下命令
SET GLOBAL rpl_semi_sync_slave_enabled = 1; |
执行以下命令重启从库上的 IO 线程
STOP SLAVE IO_THREAD; |
- 检查半同步复制插件是否在运行
主库执行以下命令
show status like 'Rpl_semi_sync_master_status'; |
从库执行以下命令
show status like 'Rpl_semi_sync_slave_status'; |
Mysql 复制级别说明
不同复制级别的设置会影响到 Master 端的 bin-log 记录成不同的形式。
配置方式:
binlog_format='row' |
基于 sql 语句(Statement level)
每一条会修改数据的 sql 都会记录到 master 的 bin-log 中。slave 在复制的时候,sql 进程会解析成和原来 master 端执行过的相同的 sql 来再次执行。
优点 :statement level 下的优点首先就是解决了row level下的缺点,不需要记录每一行数据的变化,减少 bin-log 日志量,节约 IO,提高性能。因为他只需要记录在 Master 上所执行的语句的细节,以及执行语句时候的上下文的信息。
缺点 :由于他是记录的执行语句,所以,为了让这些语句在 slave 端也能正确执行,那么他还必须记录每条语句在执行的时候的一些相关信息,也就是上下文信息,以保证所有语句在 slave 端被执行的时候能够得到和在 master 端执行时候相同的结果。
另外就是,由于 Mysql 现在发展比较快,很多的新功能不断的加入,使 mysql 的复制遇到了不小的挑战,复制的时候涉及到越复杂的内容,bug 也就越容易出现。在 statement level 下,目前已经发现的就有不少情况会造成 mysql 的复制出现问题,主要是修改数据的时候使用了某些特定的函数或者功能的时候会出现,比如:sleep()函数在有些版本中就不能真确复制,在存储过程中使用了 last_insert_id()函数,可能会使 slave 和 master 上得到不一致的 id 等等。
由于 row level 是基于每一行来记录的变化,所以不会出现类似的问题。
基于一条记录(Row level)
日志中会记录成每一行数据被修改的形式,然后在 slave 端再对相同的数据进行修改
优点 : 在 row level 模式下,bin-log 中可以不记录执行的 sql 语句的上下文相关的信息,仅仅只需要记录那一条记录被修改了,修改成什么样了。所以 row level 的日志内容会非常清楚的记录下每一行数据修改的细节,非常容易理解。而且不会出现某些特定情况下的存储过程,或 function,以及 trigger 的调用和触发无法被正确复制的问题。
任何情况都可以被复制,这对复制来说是最安全可靠的;和其他大多数数据库系统的复制技术一样;多数情况下,从服务器上的表如果有主键的话,复制就会快了很多,更少的锁
缺点 : row level 下,所有的执行的语句当记录到日志中的时候,都将以每行记录的修改来记录,这样可能会产生大量的日志内容,比如有这样一条 update 语句:update product set owner_member_id = ‘b’ where owner_member_id = ‘a’,执行之后,日志中记录的不是这条 update 语句所对应的事件(mysql 以事件的形式来记录 bin-log 日志),而是这条语句所更新的每一条记录的变化情况,这样就记录成很多条记录被更新的很多个事件。自然,bin-log 日志的量就会很大。尤其是当执行 alter table 之类的语句的时候,产生的日志量是惊人的。因为 Mysql 对于 alter table 之类的表结构变更语句的处理方式是整个表的每一条记录都需要变动,实际上就是重建了整个表。那么该表的每一条记录都会被记录到日志中。
Mixed
在 Mixed 模式下,Mysql 会根据执行的每一条具体的 sql 语句,来区分对待记录的日志形式,也就是在 Statement 和 Row 之间选择一种。新版本中的 Statment level 还是和以前一样,仅仅记录执行的语句。而新版本的 Mysql 中对 row level 模式也被做了优化,并不是所有的修改都会以 row level 来记录,像遇到表结构变更的时候就会以 statement 模式来记录,如果 sql 语句确实就是 update 或者 delete 等修改数据的语句,那么还是会记录所有行的变更。
GTID 模式
需要基于 row 模式,mysql-5.6.2 支持,mysql5.6.10 后完善
log_bin=on |
限制:
- 不支持非事务引擎(从库报错, stop slave; start slave ; 忽略)
- 不支持 create table … select 语句(主库直接报错)
- 不支持一个 sql 同时更新一个事务引擎和非事务引擎的表
- 在一个复制组中,必须要求统一开启 gtid 或是关闭 gtid
- 开启 gtid 需要重启
- 开启 gtid 后,就不在使用原来传统的复制方式
- 对于 create temporary table 和 drop temporary table 语句不支持
- 不支持 sql_slave_skip_counter
MySQL(主从)配置相关参数
master 相关配置
server-id = 1 |
slave 相关配置
server-id = 2 |