MySQL搭建一套主从复制架构,大致来说,就是主库接受增删改操作,把增删改操作binlog写入本地文件,然后从库发送请求来拉取binlog,接着在从库上重新执行一遍binlog的操作,就可以还原出一样的数据了。
那么搭建的时候肯定是需要两台机器的,一台机器放主库,一台机器放从库,至于主库和从库如何安装和启动?随便网上一搜就大把的MySQL安装步骤,这里就讲解搭建主从复制架构要做的一些配置。
首先呢,要确保主库和从库的server-id是不同的,这个是必然的,其次就是主库必须打开binlog功能,必须打开binlog功能主库才会写binlog到本地磁盘,接着就可以按如下步骤在主库上执行一通操作了。
首先在主库上要创建一个用于主从复制的账号:
create user 'backup_user'@'192.168.31.%' identified by 'backup_123';
grant replication slave on *.* to 'backup_user'@'192.168.31.%';
flush privileges;
接着要考虑一个问题,假设主库都跑了一段时间了,现在要挂一个从库,那从库总不能把主库从0开始的所有binlog都拉一遍吧!这是不对的,此时就应该在凌晨的时候,在公司里直接让系统对外不可用,说是维护状态,然后对主库和从库做一个数据备份和导入。
可以使用如下的mysqldump工具把主库在这个时刻的数据做一个全量备份,但是此时一定是不能允许系统操作主库了,主库的数据此时是不能有变动的。
/usr/local/mysql/bin/mysqldump --single-transaction -uroot -proot --master-data=2 -A > backup.sql
注意,mysqldump工具就在MySQL安装目录的bin目录下,然后用上述命令就可以对主库所有的数据都做一个备份,备份会以SQL语句的方式进入指定的backup.sql文件,只要执行这个backup.sql文件,就可以恢复出来跟主库一样的数据。
至于上面命令里的–master-data=2,意思就是说备份SQL文件里,要记录一下此时主库的binlog文件和position号,这是为主从复制做准备的。
接着可以通过scp之类的命令把这个backup.sql文件拷贝到从库服务器上去就行了,这个scp命令怎么用就不用说了,随便网上查一下就知道这个命令是怎么用的了,这个是很简单的。接着操作步骤转移到从库上去执行,在从库上执行如下命令,把backup.sql文件里的语句都执行一遍,这就相当于把主库所有的数据都还原到从库上去了,主库上的所有database、table以及数据,在从库里全部都有了。
接着在从库上执行下面的命令去指定从主库进行复制:
CHANGE MASTER TO MASTER_HOST='192.168.31.229', MASTER_USER='backup_user',MASTER_PASSWORD='backup_123',MASTER_LOG_FILE='mysql-bin.000015',MASTER_LOG_POS=1689;
可能有人会疑惑,上面的master机器的ip地址我们是知道的,master上用于执行复制的用户名和密码是我们自己创建的,也没问题,但是master的binlog文件和position是怎么知道的?这不就是之前我们mysqldump导出的backup.sql里就有,在执行上述命令前,打开那个backup.sql就可以看到如下内容:
MASTER_LOG_FILE='mysql-bin.000015',MASTER_LOG_POS=1689
然后就把上述内容写入到主从复制的命令里去了。接着执行一个开始进行主从复制的命令:start slave,再用show slave status查看一下主从复制的状态,主要看到Slave_IO_Running和Slave_SQL_Running都是Yes就说明一切正常了,主从开始复制了。接着就可以在主库插入一条数据,然后在从库查询这条数据,只要能够在从库查到这条数据,就说明主从复制已经成功了。
这仅仅是最简单的一种主从复制,就是异步复制,就是之前讲过的那种原理,从库是异步拉取binlog来同步的,所以肯定会出现短暂的主从不一致的问题的,比如你在主库刚插入数据,结果在从库立马查询,可能是查不到的。
万一此时要是主库的binlog还没同步到从库,结果主库宕机了,此时数据不就丢失了么?即使做了高可用自动切换,一下子把从库切换为主库,但是里面是没有刚才写入的数据的,所以这种方式是有问题的。
因此一般来说搭建主从复制,都是采取半同步的复制方式的,这个半同步的意思,就是说,主库写入数据,日志进入binlog之后,起码得确保 binlog日志复制到从库了,再告诉客户端说本次写入事务成本了是不是?
这样起码主库突然崩了,之前写入成功的数据的binlog日志都是到从库了,从库切换为主库,数据也不会丢的,这就是所谓的半同步的意思。
这个半同步复制,有两种方式,第一种叫做AFTER_COMMIT方式,它不是默认的,它的意思是说,主库写入日志到binlog,等待binlog复制到从库了,主库就提交自己的本地事务,接着等待从库返回给自己一个成功的响应,然后主库返回提交事务成功的响应给客户端。另外一种是现在MySQL 5.7默认的方式,主库把日志写入binlog,并且复制给从库,然后开始等待从库的响应,从库返回说成功给主库了,主库再提交事务,接着返回提交事务成功的响应给客户端。
总而言之,这种方式可以保证每个事务提交成功之前,binlog日志一定都复制到从库了,所以只要事务提交成功,就可以认为数据在从库也有一份了,那么主库崩溃,已经提交的事务的数据绝对不会丢失的。
搭建半同步复制也很简单,在之前搭建好异步复制的基础之上,安装一下半同步复制插件就可以了,先在主库中安装半同步复制插件,同时还得开启半同步复制功能:
install plugin rpl_semi_sync_master soname 'semisync_master.so';
set global rpl_semi_sync_master_enabled=on;
show plugins;
可以看到你安装了这个插件,那就ok了。接着在从库也是安装这个插件以及开启半同步复制功能:
stop slave io_thread; start slave io_thread;
然后在主库上检查一下半同步复制是否正常运行:
show global status like '%semi%';
如果看到了Rpl_semi_sync_master_status的状态是ON,那么就可以了。到此半同步复制就开启成功了,其实一般来说主从复制都建议做成半同步复制,因为这样配合高可用切换机制,就可以保证数据库有一个在线的从库热备份主库的数据了,而且主要主库宕机,从库立马切换为主库,数据不丢失,数据库还高可用。
一般在生产中都会采用半同步的复制模式,但是其实除了这种传统搭建方式之外,还有一种更加简便一些的搭建方式,就是GTID搭建方式;
首先在主库进行配置:
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=单独设置一个
接着按照之前讲解的步骤在主库创建好用于复制的账号之后,就可以跟之前一样进行操作了,比如在主库dump出来一份数据,在从库里导入这份数据,利用mysqldump备份工具做的导出,备份文件里会有SET @@GLOBAL.GTID_PURGED=***一类的字样,可以照着执行一下就可以了。
接着其余步骤都是跟之前类似的,最后执行一下show master status,可以看到executed_gtid_set,里面记录的是执行过的GTID,接着执行一下SQL:select * from gtid_executed,可以查询到,对比一下,就会发现对应上了。那么此时就说明开始GTID复制了。
无论是GTID复制,还是传统复制,都不难,很简单,往往这就是比较典型的MySQL主从复制的搭建方式了,然后可以自行搜索一下MyCat中间件或者是Sharding-Sphere的官方文档,其实也都不难,照着文档做,整合到Java代码里去,就可以做出来基于主从复制的读写分离的效果了。那些中间件都是支持读写分离模式的,可以仅仅往主库去写,从从库去读,这都没问题的。
如果落地到项目里,那么就完成了一个主从架构以及读写分离的架构了,此时按照之前所说的,如果说你的数据库之前对一个库的读写请求每秒总共是2000,此时读写分离后,也许就对主库每秒写TPS才几百,从库的读QPS是1000多。
那么万一你要是从库的读QPS越来越大,达到了每秒几千,此时你是不是会压力很大?没关系,这个时候你可以给主库做更多的从库,搭建从库,给他挂到主库上去,每次都在凌晨搞,先让系统停机,对外不使用,数据不更新。
接着对主库做个dump,导出数据,到从库导入数据,做一堆配置,然后让从库开始接着某个时间点开始继续从主库复制就可以了,一旦搭建完毕,就等于给主库挂了一个新的从库上去,此时继续放开系统的对外限制,继续使用就可以了,整个过程基本在1小时以内。如果在凌晨比如2点停机1小时,基本对业务是没有影响的。