MySQL在生产环境中,必须要搭建一套 主从复制的架构,同时可以基于一些工具实现高可用架构 ; 另外如果有需求,还需要基于一些中间件实现读写分离架构,最后就是如果数据量很大,还必须可以实 现分库分表的架构。
那么今天我们就先来给大家讲讲MySQL的主从复制架构,这个主从复制架构,顾名思义,就是部署两台 服务器,每台服务器上都得有一个MySQL,其中一个MySQL是master(主节点),另外一个MySQL是 slave(从节点)。

然后我们的系统平时连接到master节点写入数据,当然也可以从里面查询了,就跟你用一个单机版的 MySQL是一样的,但是master节点会把写入的数据自动复制到slave节点去,让slave节点可以跟 master节点有一模一样的数据,如下图所示。
image.png

高可用架构:
一个先决条件就是主从复制架构 , 必须得让主节点可以复制数据到从 节点,保证主从数据是一致的,接着万一你的主节点宕机了,此时可以让你的Java业务系统连接到从节 点上去执行SQL语句,写入数据和查询数据,因为主从数据是一致的,所以这是没问题的。
image.png
比如主从进行数据复制的时候,其实从节点通常都会落后一些,所以数据不完全一致。另外,主节点宕 机后,要能自动切换从节点对外提供服务,这个也需要一些中间件的支持,也没那么容易,这些问题, 后续我们都会讲到的。

读写分离架构, 也是依赖于MySQL的主从复制架构的。
读写分离架构的意思就是,你的Java业务系统可以往主节点写入数据,但是从从节点去查询数据,把读 写操作做一个分离,分离到两台MySQL服务器上去,一台服务器专门让你写入数据,然后复制数据到从 节点,另外一台服务器专门让你查询数据,如下图所示。
image.png
假设我们的MySQL单机服务器配置是8核16GB,然后每秒最多能抗4000读写请求, 现在假设你真实的业务负载已经达到了,每秒有2500写请求+2500读请求,也就是每秒5000读写请求 了,那么你觉得如果都放一台MySQL服务器,能抗的住吗?

必然不行啊!所以此时如果你可以利用主从复制架构,搭建起来读写分离架构,就可以让每秒2500写请 求落到主节点那台服务器,2500读请求落到从节点那台服务器,用2台服务器来抗下你每秒5000的读写 请求,如下图所示。
image.png
接着现在问题来了,大家都知道,其他大部分Java业务系统都是读多写少,读请求远远多于写请求,那 么接着发现随着系统日益发展,读请求越来越多,每秒可能有6000读请求了,此时一个从节点服务器也 抗不下来啊,那怎么办呢?

简单!因为MySQL的主从复制架构,是支持一主多从的,所以此时你可以再在一台服务器上部署一个从 节点,去从主节点复制数据过来,此时你就有2个从节点了,然后你每秒6000读请求不就可以落到2个 从节点上去了,每台服务器主要接受每秒3000的读请求,如下图。
image.png
如上图,Java业务系统每秒以2500的TPS写入主库,然后主库会复制数据到两个从库,接着你每秒6000 QPS的读请求分散在两个从库上,一切似乎很完美,这就是主从复制架构的另外一个经典的应用场景, 就是读写分离,通过读写分离,可以让你抗下很高的读请求。

而且在上述架构之下,还可以融合高可用架构进去,因为你有多个从库,所以当你主库宕机的时候,可 以通过中间件把一个从库切换为主库,此时你的Java业务系统可以继续运行,在实现读写分离的场景 下,还可以同时实现高可用。

不过其实一般在项目中,高可用架构是必须做的,但是读写分离架构并不是必须的,因为对于大多数公 司来说,读请求QPS并没那么高,远远达不到每秒几千那么夸张,但是高可用你是必须得做的,因为你 必须保证主库宕机后,有另外一个从库可以接管提供服务,避免Java业务系统中断运行。

除此之外,这个从库其实还有很多其他的应用场景,比如你可以挂一个从库,专门用来跑一些报表SQL 语句,那种SQL语句往往是上百行之多,运行要好几秒,所以可以专门给他一个从库来跑。也可以是专 门部署一个从库,让你去进行数据同步之类的操作。

MySQL实现主从复制的一个基本的工作原理

首先呢,大家通过之前的学习,应该都知道,MySQL自己在执行增删改的时候会记录binlog日志,这个binlog日志里就记录了所有数据增删改的操 作,如下图
image.png
从库有一个IO线程,这个IO 线程会负责跟主库建立一个TCP连接,接着请求主库传输binlog日志给从库,这个时候主库有一个IO dump 线程,会负责通过TCP连接把binlog日志传输给从库的IO线程。
image.png
从库把主库获取到binlog 日志数据,写入到自己本地的relay日志文件中去,然后从库上另外有一个SQL线程会读取relay日志里的内容,进行日志重做,把所有在主库执行过的增删改操作,在从库上做一遍,达到同步数据的效果。
image.png

简单来说,你只要给主节点挂 上一个从节点,从节点的IO线程就会跟主节点建立网络连接,然后请求主节点传输binlog日志,主节点 的IO dump线程就负责传输binlog日志给从节点,从节点收到日志后就可以回放增删改操作恢复数据。 在这个基础之上,就可以实现MySQL主从节点的数据复制以及基本一致,进而可以实现高可用架构以及 读写分离架构

搭建主从

  1. 确保主库和从库的server-id是不同
  2. 其次就是主库必须打开binlog功能 , 主库才会写binlog到本地磁盘
  3. 接着你要考虑一个问题,假设你主库都跑了一段时间了,现在要挂一个从库,那从库总不能把你主库从 0开始的所有binlog都拉一遍吧!这是不对的,此时你就应该在凌晨的时候,在公司里直接让系统对外 不可用,说是维护状态,然后对主库和从库做一个数据备份和导入。
  4. 可以使用如下的mysqldump工具把主库在这个时刻的数据做一个全量备份,但是此时一定是不能允许 系统操作主库了,主库的数据此时是不能有变动的。

    /usr/local/mysql/bin/mysqldump —single-transaction -uroot -proot —master-data=2 -A > backup.sq
    mysqldump工具就在你的MySQL安装目录的bin目录下,然后用上述命令就可以对你主库所有的 数据都做一个备份,备份会以SQL语句的方式进入指定的backup.sql文件,只要执行这个backup.sql文 件,就可以恢复出来跟主库一样的数据 ; 至于上面命令里的—master-data=2,意思就是说备份SQL文件里,要记录一下此时主库的binlog文件和 position号,这是为主从复制做准备的。

  5. 接着你可以通过scp之类的命令把这个backup.sql文件拷贝到你的从库服务器上

  6. 在从库上执行如下命令,把backup.sql文件里的语句都执行一遍, 这就相当于把主库所有的数据都还原到从库上去了,主库上的所有database、table以及数据,在从库 里全部都有了。

    接着在从库上执行下面的命令去指定从主库进行复制。 CHANGE MASTER TO MASTER_HOST=’192.168.31.229’, MASTER_USER=’backup_user’,MASTER_PASSWORD=’backup_123’,MASTER_LOG_FILE=’mysqlbin.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
    然后你就把上述内容写入到主从复制的命令里去了。

  7. 接着执行一个开始进行主从复制的命令:start slave,再用show slave status查看一下主从复制的状 态,主要看到Slave_IO_Running和Slave_SQL_Running都是Yes就说明一切正常了,主从开始复制了。

  8. 接着就可以在主库插入一条数据,然后在从库查询这条数据,只要能够在从库查到这条数据,就说明主 从复制已经成功了。 【主从复制已经搭建好了】
  9. 这仅仅是最简单的一种主从复制,就是异步复制,就是之前讲过的那种原理,从库是异步拉取binlog来 同步的,所以肯定会出现短暂的主从不一致的问题的,比如你在主库刚插入数据,结果在从库立马查 询,可能是查不到的。

半同步的复制方式

比如可以用mycat或者sharding-sphere之类的中间件,就可以实现你的系统写入主库,从从库去读取 了。
但是现在搭建出来的主从复制架构有一个问题,那就是之前那种搭建方式他默认是一种异步的复制方 式,也就是说,主库把日志写入binlog文件,接着自己就提交事务返回了,他也不管从库到底收到日志 没有。

那万一此时要是主库的binlog还没同步到从库,结果主库宕机了,此时数据不就丢失了么?即使你做了 高可用自动切换,一下子把从库切换为主库,但是里面是没有刚才写入的数据的,所以这种方式是有问 题的。

因此一般来说搭建主从复制,都是采取半同步的复制方式的,这个半同步的意思,就是说,你主库写入 数据,日志进入binlog之后,起码得确保 binlog日志复制到从库了,你再告诉客户端说本次写入事务成功了是不是?

这样起码你主库突然崩了,他之前写入成功的数据的binlog日志都是到从库了,从库切换为主库,数据 也不会丢的,这就是所谓的半同步的意思。

这个半同步复制,有两种方式
第一种叫做AFTER_COMMIT方式,他不是默认的,他的意思是说,主 库写入日志到binlog,等待binlog复制到从库了,主库就提交自己的本地事务,接着等待从库返回给自 己一个成功的响应,然后主库返回提交事务成功的响应给客户端。

另外一种是现在MySQL 5.7默认的方式,主库把日志写入binlog,并且复制给从库,然后开始等待从库 的响应,从库返回说成功给主库了,主库再提交事务,接着返回提交事务成功的响应给客户端。

总而言之,这种半同步方式可以保证你每个事务提交成功之前,binlog日志一定都复制到从库了,所以只要事 务提交成功,就可以认为数据在从库也有一份了,那么主库崩溃,已经提交的事务的数据绝对不会丢失 的。

搭建半同步复制也很简单,在之前搭建好异步复制的基础之上,安装一下半同步复制插件就可以了,先 在主库中安装半同步复制插件,同时还得开启半同步复制功能

  1. install plugin rpl_semi_sync_master soname 'semisync_master.so';
  2. set global rpl_semi_sync_master_enabled=on;
  3. show plugins

接着在从库也是安装这个插件以及开启半同步复制功能:

install plugin rpl_semi_sync_slave soname 'semisync_slave.so';
set global rpl_semi_sync_slave_enabled=on;
show plugins;

接着要重启从库的IO线程: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小时以内。

主从延迟

主从复 制可能会有较大的延迟。这个延迟是什么意思呢?就是说主库可能你都写入了100条数据了,结果从库 才复制过去了50条数据,那么从库就比主库落后了50条数据。

可是为什么会产生这个主从延迟的问题呢?也很简单,其实你主库是多线程并发写入的,这个大家都知 道的,所以主库写入数据的速度可能是很快的,但是从库是单个线程缓慢拉取数据的,所以才会导致从 库复制数据的速度是比较慢的。

这个主从之间到底延迟了多少时间呢?这个可以用一个工具来进行监控,比较推荐的是perconatoolkit工具集里的pt-heartbeat工具,他会在主库里创建一个heartbeat表,然后会有一个线程定时更 新这个表里的时间戳字段,从库上就有一个monitor线程会负责检查从库同步过来的heartbeat表里的 时间戳。 把时间戳跟当前时间戳比较一下,其实就知道主从之间同步落后了多长时间了。

主从同步延迟会有什么不良情况呢?

其实大家可以思考一下,如果你做了读写分离架构,写都往主库写,读都从从库读,那么会不会你的系 统刚写入一条数据到主库,接着代码里立即就在从库里读取,可能此时从库复制有延迟,你会读不到刚 写入进去的数据! 另外就是有可能你的从库同步数据太慢 了,导致你从库读取的数据都是落后和过期的,也可能会导致你的系统产生一定的业务上的bug。

所以针对这个问题,首先你应该做的,是尽可能缩小主从同步的延迟时间,那么怎么做呢?其实就是让 从库也用多线程并行复制数据就可以了,这样从库复制数据的速度快了,延迟就会很低

MySQL 5.7就已经支持并行复制了,可以在从库里设置slave_parallel_workers>0,然后把 slave_parallel_type设置为LOGICAL_CLOCK,就ok了。

另外,如果你觉得还是要求刚写入的数据你立马强制必须一定可以读到,那么此时你可以使用一个办 法,就是在类似MyCat或者Sharding-Sphere之类的中间件里设置强制读写都从主库走,这样你写入主 库的数据,强制从主库里读取,一定立即可以读到的。

选型

如 果说你对数据丢失并不是强要求不能丢失的话,可以用异步模式来复制,再配合一下从库的并行复制机 制。
如果说你要对MySQL做高可用保证数据绝对不丢失的话,建议还是用半同步机制比较好一些,同理最好 是配合从库的并行复制机制。

数据库高可用:基于主从复制实现故障转移

所谓的高可用就是说,如果数据库突然宕机了一台机器,比如说主库或者从库宕机了,那么数据库还能正常使用吗?

一般生产环境里用于进行数据库高可用架构管理的工具是MHA,也就是Master High Availability Manager and Tools for MySQL,是日本人写的,用perl脚本写的一个工具,这个工具就是专门用于监 控主库的状态,如果感觉不对劲,可以把从库切换为主库。

这个MHA自己也是需要单独部署的,分为两种节点,一个是Manager节点,一个是Node节点, Manager节点一般是单独部署一台机器的,Node节点一般是部署在每台MySQL机器上的,因为Node 节点得通过解析各个MySQL的日志来进行一些操作。

Manager节点会通过探测集群里的Node节点去判断各个Node所在机器上的MySQL运行是否正常,如 果发现某个Master故障了,就直接把他的一个Slave提升为Master,然后让其他Slave都挂到新的 Master上去,完全透明。

首先,大家最好是准备4台机器,其中一台机器装一个mysql作为master,另外两台机器都装mysql作 为slave,然后在每个机器上都得部署一个MHA的node节点,然后用单独的最后一台机器装MHA的 master节点,整体就这么一个结构

大家得确保4台机器之间都是免密码互相通信的,这个大家可以自行搜索一下,大量的方法可以 做到,就是4台机器之间要不依靠密码可以直接ssh登录上去,因为这是MHA的perl脚本要用的。

接着大家就应该部署一个MySQL master和两个MySQL slave,搭建的过程就按照之前讲解的就行了, 先装好MySQL,接着进行主从复制的搭建,全部按照之前的步骤走就行了,可以选择异步复制,当然也 可以是半同步复制的。

安装MHA 看到126

今天我们正式来讲解MHA数据库高可用架构的搭建,先来讲解一下在三个数据库所在机器上安装MHA node节点的步骤,首先那必须要先安装Perl语言环境了,这就跟我们平时用Java开发,那你必须得先装 个JDK吧!