先来看下MYSQL异步复制的概念:
异步复制:MySQL本身支持单向的、异步的复制。异步复制意味着在把数据从一台机器拷贝到另一台机器时有一个延时 – 最重要的是这意味着当应用系统的事务提交已经确认时数据并不能在同一时刻拷贝/应用到从机。通常这个延时是由网络带宽、资源可用性和系统负载决定的。然而,使用正确的组件并且调优,复制能做到接近瞬时完成。
当主库有更新的时候,主库会把更新操作的SQL写入二进制日志(Bin log),并维护一个二进制日志文件的索引,以便于日志文件轮回(Rotate)。在从库启动异步复制的时候,从库会开启两个I/O线程,其中一个线程连接主库,要求主库把二进制日志的变化部分传给从库,并把传回的日志写入本地磁盘。另一个线程则负责读取本地写入的二进制日志,并在本地执行,以反映出这种变化。较老的版本在复制的时候只启用一个I/O线程,实现这两部分的功能。
同步复制:同步复制可以定义为数据在同一时刻被提交到一台或多台机器,通常这是通过众所周知的“两阶段提交”做到的。虽然这确实给你在多系统中保持一致性,但也由于增加了额外的消息交换而造成性能下降。
使用MyISAM或者InnoDB存储引擎的MySQL本身并不支持同步复制,然而有些技术,例如分布式复制块设备(简称DRBD),可以在下层的文件系统提供同步复制,允许第二个MySQL服务器在主服务器丢失的情况下接管(使用第二服务器的复本)。要了解更多信息,
MYSQL 5。5开始,支持半自动复制。之前版本的MySQL Replication都是异步(asynchronous)的,主库在执行完一些事务后,是不会管备库的进度的。如果备库不幸落后,而更不幸的是主库此时又出现Crash(例如宕机),这时备库中的数据就是不完整的。简而言之,在主库发生故障的时候,我们无法使用备库来继续提供数据一致的服务了。
Semisynchronous Replication则一定程度上保证提交的事务已经传给了至少一个备库。
Semi synchronous中,仅仅保证事务的已经传递到备库上,但是并不确保已经在备库上执行完成了。
此外,还有一种情况会导致主备数据不一致。在某个session中,主库上提交一个事务后,会等待事务传递给至少一个备库,如果在这个等待过程中主库Crash,那么也可能备库和主库不一致,这是很致命的。(在主库恢复后,可以通过参数Rpl_semi_sync_master_no_tx观察)
如果主备网络故障或者备库挂了,主库在事务提交后等待10秒(rpl_semi_sync_master_timeout的默认值)后,就会继续。这时,主库就会变回原来的异步状态。
MySQL在加载并开启Semi-sync插件后,每一个事务需等待备库接收日志后才返回给客户端。如果做的是小事务,两台主机的延迟又较小,则Semi-sync可以实现在性能很小损失的情况下的零数据丢失。
主机Crash时的处理
备库Crash时,主库会在某次等待超时后,关闭Semi-sync的特性,降级为普通的异步复制,这种情况比较简单。
主库Crash后,那么可能存在一些事务已经在主库Commit,但是还没有传给任何备库,我们姑且称这类事务为"墙头事务"。"墙头事务"都是没有返回给客户端的,所以发起事务的客户端并不知道这个事务是否已经完成。
这时,如果客户端不做切换,只是等Crash的主库恢复后,继续在主库进行操作,客户端会发现前面的"墙头事务"都已经完成,可以继续进行后续的业务处理;另一种情况,如果客户端Failover到备库上,客户端会发现前面的“墙头事务”都没有成功,则需要重新做这些事务,然后继续进行后续的业务处理。
可以做多个备库,任何一个备库接收完成日志后,主库就可以返回给客户端了。
网络传输在并发线程较多时,一次可能传输很多日志,事务的平均延迟会降低。
"墙头事务"在墙头上的时候,是可以被读取的,但是这些事务在上面Failover的场景下,是被认为没有完成的。
默认情况下MySQL的复制是异步的,Master上所有的更新操作写入Binlog之后并不确保所有的更新都被复制到Slave之上。异步操作虽然效率高,但是在Master/Slave出现问题的时候,存在很高数据不同步的风险,甚至可能丢失数据。
MySQL5.5引入半同步复制功能的目的是为了保证在master出问题的时候,至少有一台Slave的数据是完整的。在超时的情况下也可以临时转入异步复制,保障业务的正常使用,直到一台salve追赶上之后,继续切换到半同步模式。
Master:
INSTALL PLUGIN rpl_semi_sync_master SONAME ‘semisync_master.so’;
SET GLOBAL rpl_semi_sync_master_enabled=1;
SET GLOBAL rpl_semi_sync_master_timeout=1000; (1s, default 10s)
Slave:
INSTALL PLUGIN rpl_semi_sync_slave SONAME ‘semisync_slave.so’;
SET GLOBAL rpl_semi_sync_slave_enabled=1;
复制心跳(用户检测复制是否中断)
MySQL5.5提供的新的配置master_heartbeat_period,能够在复制停止工作和出现网络中断的时候帮助我们迅速发现问题。
启用方法:
STOP SLAVE;
CHANGE MASTER TO master_heartbeat_period= milliseconds;
START SLAVE;
Slave自动恢复同步
在MySQL5.5版本之前,MySQL Slave实例在异常终止服务之后,可能导致复制中断,并且relay binlog可能损坏,在MySQL再次启动之后并不能正常恢复复制。在MySQL5.5中这一问题得到了解决,MySQL可以自行丢弃顺坏的而未处理的数据,重新从master上获取源数据,进而回复复制。
跳过指定复制事件
在多Master或环形复制的情况下,处于复制链条中间的服务器异常,可以通过
CHANGE MASTER TO MASTER_HOST=xxx IGNORE_SERVER_IDS=y
跳过出问题的MySQL实例。
自动转换字段类型
MySQL5.1在基于语句的复制下,支持部分的字段转换,但是行级的会报错。MySQL5.5语句和行级复制都已支持。还可以通过 SLAVE_TYPE_CONVERSIONS 控制转换的方向。
赠送:
- 为mysql的安装提供前提环境和初始化安装mysql
- 创建数据库目录
- # mkdir /mydata/data –pv
- 创建mysq用户
- # useradd -r mysql
- 修改权限
- # chown -R mysql.mysql /mydata/data/
- 使用mysql-5.5通用二进制包安装
- 解压mysql软件包
- # tar xf mysql-5.5.28-linux2.6-i686.tar.gz-C /usr/local/
- 创建连接,为了方便查看mysql的版本等信息
- # cd /usr/local/
- #ln –sv mysql-5.5.28-linux2.6-i686.tar.gzmysql
- 修改属主属组
- # cd mysql
- # chown -R root.mysql ./*
- 初始化数据库
- # scripts/mysql_install_db –user=mysql --datadir=/mydata/data/
- 提供配置文件
- # cp support-files/my-large.cnf /etc/my.cnf
- 提供服务脚本
- # cp support-files/mysql.server/etc/rc.d/init.d/mysqld
- 添加至服务列表
- # chkconfig --add mysqld
- # chkconfig --list mysqld
- # chkconfig mysqld on
- 编辑配置文件,提供数据目录
- # vim /etc/my.cnf
- # The MySQL server 修改mysqld服务器端的内容
- log-bin=master-bin 主服务器二进制日志文件前缀名
- log-bin-index=master-bin.index 索引文件
- innodb_file_per_table= 1 开启innodb的一表一个文件的设置
- server-id = 1 必须是唯一的
- datadir =/mydata/data 数据目录路径
- 启动mysql服务
- # servicemysqld start
- 为了便于下面的测试,设置环境变量
- # vim/etc/profile.d/mysql.sh
- export PATH=$PATH:/usr/local/mysql/bin
- 执行环境变量脚本,使其立即生效
- # . /etc/profile.d/mysql.sh
- 建立用户账户
- mysql> grant replication slave on *.* to 'chris'@'172.16.%.%' identified by 'work';
- 刷新数据使其生效
- mysql> flush privileges;
- 创建mysql数据库目录
- # mkdir /mydata/data –pv
- 创建mysql用户
- # useradd -r mysql
- 修改数据目录权限
- # chown -R mysql.mysql /mydata/data/
- 使用mysql-5.5通用二进制包安装mysql
- 解压mysql软件包
- # tar xf mysql-5.5.28-linux2.6-i686.tar.gz-C /usr/local/
- 创建连接,便于查看mysql的版本等信息
- # cd /usr/local/
- # ln –sv mysql-5.5.28-linux2.6-i686.tar.gzmysql
- 修改mysql属主属组
- # cd mysql
- # chown -R root.mysql ./*
- 初始化mysql数据库
- # scripts/mysql_install_db –user=mysql--datadir=/mydata/data/
- 提供mysql配置文件
- # cp support-files/my-large.cnf /etc/my.cnf
- 提供服务脚本
- # cp support-files/mysql.server /etc/init.d/mysqld
- 添加至服务列表
- # chkconfig --add mysqld
- 编辑配置文件
- # vim /etc/my.cnf
- # The MySQL server
- #log-bin=mysql-bin 禁用二进制日志,从服务器不需要二进制日志文件
- datadir = /mydata/data mysql的数据目录
- relay-log = relay-log 设置中继日志
- relay-log-index = relay-log.index 中继日志索引
- innodb_file_per_table = 1
- server-id = 2 id不要和主服务器的一样
- 设置环境变量
- # vim/etc/profile.d/mysql.sh
- export PATH=$PATH:/usr/local/mysql/bin
- 执行此脚本(导出环境变量)
- # . /etc/profile.d/mysql.sh
- 启动服务
- # service mysqld start
- mysql> show master status; #在Master上执行查看二进制文件
- 在从服务器上开启复制功能
- change master to master_host='172.16.7.1',master_user='chris',master_password='work',master_log_file='master-bin.000001',master_log_pos=407;
- 开启复制功能
- mysql>start slave;
- 安装插件:mysql> INSTALL PLUGIN rpl_semi_sync_master SONAME 'semisync_master.so';
- 启动模块:mysql> SET GLOBAL rpl_semi_sync_master_enabled = 1;
- 设置超时时间:mysql> SET GLOBAL rpl_semi_sync_master_timeout = 1000;
1
2
3
4
|
slave:
安装插件:msyql> INSTALL PLUGIN rpl_semi_sync_slave SONAME
'semisync_slave.so'
;
启动模块:mysql> SET GLOBAL rpl_semi_sync_slave_enabled =
1
;
重启进程使其模块生效:mysql> STOP SLAVE IO_THREAD; START SLAVE IO_THREAD;
|
- 在Master和Slave的my.cnf中编辑:
- # On Master
- [mysqld]
- rpl_semi_sync_master_enabled=1
- rpl_semi_sync_master_timeout=1000 #此单位是毫秒
- # On Slave
- [mysqld]
- rpl_semi_sync_slave_enabled=1
- master:
- mysql> CREATE DATABASE asyncdb;
- master> SHOW STATUS LIKE 'Rpl_semi_sync_master_yes_tx';
- slave> SHOW DATABASES;
- 其测试过程如下
- # 主服务器上
- [mysqld]
- server-id = 1
- log-bin = mysql-bin
- relay-log = relay-mysql
- relay-log-index = relay-mysql.index
- auto-increment-increment = 2 #每次跳两个数。
- auto-increment-offset = 1 #从1开始。
- [mysqld]
- server-id = 2
- log-bin = mysql-bin
- relay-log = relay-mysql
- relay-log-index = relay-mysql.index
- auto-increment-increment = 2
- auto-increment-offset = 2
- master:查看日志文件信息
- mysql> show master status;
- +------------------+----------+--------------+------------------+
- | File | Position | Binlog_Do_DB | Binlog_Ignore_DB |
- +------------------+----------+--------------+------------------+
- | mysql-bin.000001 | 107 | | |
- +------------------+----------+--------------+------------------+
- Slave:查看服务器日志文件信息
- mysql> show master status;
- +------------------+----------+--------------+------------------+
- | File | Position | Binlog_Do_DB | Binlog_Ignore_DB |
- +------------------+----------+--------------+------------------+
- | mysql-bin.000001 | 107 | | |
- +------------------+----------+--------------+------------------+
- 1 row in set (0.00 sec)
1
2
3
|
master:
mysql> GRANT REPLICATION SLAVE ON *.* TO
'chrislee'
@
'172.16.%.%'
IDENTIFIED BY
'work'
;
mysql> flush privileges;
|
- slave:
- mysql> GRANT REPLICATION SLAVE ON *.* TO 'chrisli'@'172.16.%.%' IDENTIFIED BY 'work';
- mysql> flush privileges
- server1
- mysql> CHANGE MASTER TO MASTER_HOST='172.16.7.2',MASTER_USER='chrisli',MASTER_PASSWORD='work',MASTER_LOG_FILE='mysql-bin.000001',MASTER_LOG_POS=344;
- server2:
- mysql> CHANGE MASTER TO MASTER_HOST='172.16.7.1',MASTER_USER='chrislee',MASTER_PASSWORD='work',MASTER_LOG_FILE='mysql-bin.000001',MASTER_LOG_POS=345;