业务零影响!如何在Online环境中巧用MySQL传统复制技术
这篇文章我并不会介绍如何部署一个MySQL复制环境或keepalived+双主环境,因为此类安装搭建的文章已经很多,大家也很熟悉。在这篇文章里,我主要是介绍MySQL复制技术在Online【在线业务系统】环境里如何进行架构上的调整,同时这些调整对在线业务系统的影响又是尽可能的小甚至是零影响。希望大家能有所收获。
1MySQL复制中的监控管理
大家都知道,MySQL复制(不论是经典传统复制还是5.6新引入的GTID复制)都是以io_thread和sql_thread这两个核心线程为基础来实现的。(关于复制的原理在这篇文章我就不过多阐述,有兴趣可以关注我以后的文章)
那我们在实际工作中需要关心以下几个问题:
显然,在Slave里的show slave status\G给我们提供了足够的信息来确认以上的问题。(双主环境,两个复制节点均有show slave status\G信息)
show slave status\G中首先要看的信息是:
Slave_IO_Running: Yes
Slave_SQL_Running: Yes
Slave_IO_Running代表io_thread是否正常工作,Slave_SQL_Running代表sql_thread是否正常工作。如果有出现No,那你的复制环境应该就需要报警了。
但仅仅只关注这两个指标还远远不够,show slave status\G输出信息里我们还需要关注的值是:Seconds_Behind_Master【单位是秒】。
Seconds_behind_master = 0 表示没有延迟, >0表示有延迟,当为的时候,表示复制可能出错了。
在实际工作中,我发现很多DBA朋友通常都只根据Seconds_Behind_Master的值判断Slave是否存在延迟,这在部分情况里尚可接受,但并不够准确。特别是业务高并发的系统里,我更不建议你使用Seconds_Behind_Master来作为判定复制延迟的标准。
也就是说,Seconds_Behind_Master并不能非常准确地体现复制延迟,我们还需要考虑更多的因素。
真正来准确判断Slave是否存在延迟,我们应该从复制的binlog文件名和binlog位置是否一致来入手。
我们知道,show slave status\G的输出结果中:
大多数时候,可以通过io_thread 和sql_thread执行的位置进行对比,来确认是不是存在延迟。(因为在大多数时候,io_thread很少会存在延迟,一般都是sql_thread存在延迟,这种比较方法的前提是我们可以把io_thread的执行位置看作跟主库show master status的位置一致的)
当show slave status\G输出的这几个指标信息满足上面的条件,我们则可以认为复制不存在延迟,是实时同步完成的。
如果你对io_thread的延迟也存在担忧【比如复制环境的网络不好等因素】,更严谨的思路是下面这样:
具体实现思路是:
在第三方监控节点上,对MASTER和SLAVE同时发起SHOW BINARY LOGS【或者SHOW MASTER STATUS】和SHOW SLAVE STATUS\G的请求,然后按照上面的公式去判断是否存在差异。
当然,这么做起来的话,脚本实现相对麻烦,而且对第三方监控节点的网络要求也非常高,同时,由于io_thread一般不会是瓶颈,所以在做脚本监控的时候,不会这么干。大多数时候按照我之前说的做法来监控复制延迟就已经足够了:
还有一种监控MySQL复制的方式是:通过维护一个监控表来判定复制延迟。
可以在MASTER上维护一个监控表,一般只有两个字段(id和time),存储这最新最新时间戳(高版本MySQL可以采用event_scheduler来更新,低版本可以用Linux cron结合自动循环脚本来更新),然后在SLAVE上读取该字段的时间,只要MASTER和SLAVE的系统时间【NTP是必须的】一致,即可快速知道SLAVE和MASTER延迟差了多少。
举例:
这种监控方式称为update心跳。
另外注意,在高并发的系统下,这个时间戳需要细化到毫秒,否则哪怕时间一致,也是有可能会延迟数个binlog event的。
以上是MySQL传统复制中在监控这一块需要我们注意的一些地方,只要我们把思路理清,编写一个MySQL复制的监控脚本应该是顺理成章的事情。(用python、perl、shell或其他语言写一个都应该问题不大)
那接下来,进入我们的正题。
2MySQL复制中Online(在线)架构的调整
首先我要强调下在线架构,因为在一个已经离线(停机)的环境里做架构调整并没有太多技术含量。
在线架构,就是已经投入在使用的架构,已经面向业务用户开放的系统架构。
在线架构,意味着数据每份每秒都在不停得写入,show master status的log_file和log_pos,时刻都在进行变化。数据是不会静止的,这就是在线架构。
这和公司里的开发环境或测试环境不同,是在线线上的环境。
那我们下面说的复制架构调整,就是必须在Online这个背景下来做调整的。
另外,下面的介绍基于MySQL传统复制,如果是使用MySQL5.6以后的GTID复制,你可以不用太关心下面的内容。 GTID复制在运维上比传统复制方便了很多,有关GTID复制的内容会在以后的文章里讲解。
第一个实战:如何在一个在线架构的复制环境里引入VIP
假设我们已经在线的一个简单的主从环境如下:
应用的连接全部都是连在192.168.1.10的master上进行访问的。
我们这里想通过实现一次Online(在线)切换,来实现MySQL的内存扩容,因为Slave的内存是32G。
按照目前的架构,在做切换完成后,我们得需要修改应用服务器的IP地址为192.168.1.11,那这个操作肯定会影响业务系统使用,无法满足在线架构系统的要求。
更好的解决方案是给目前的复制环境引入一个虚拟IP(VIP),通过VIP来实现切换,这样在线切换起来的影响会小很多。
假设我们申请的VIP是:192.168.1.20
那我们把这个VIP先绑到master上:
master的主机节点上会有两个IP:
192.168.1.10【Real_IP】
192.168.1.20【VIP】 这个是我们手工绑上的。
那我们现在要考虑的是在线把应用服务器的数据库地址连接改192.168.1.20:3306 ,这个影响有多大?
实际的情况取决与你前端应用服务器的架构了,如果你的前端应用服务器只有一台,那肯定还是有影响的,但大多数时候,前端的应用服务器也是集群架构,都是能实现无状态的。那其实只要前端应用服务器的结构稍微合理,这个引入VIP的操作是0影响的。
引入VIP后【实际上就是在node1上绑定一个VIP】,应用服务器就都通过VIP连接到数据库了,架构如下:
这里的切换,我们不引入任何第三方的组件,什么keepalived,MHA都不考虑,我们就是自己写脚本来做切换。
我们来看下,手动脚本切换的思路,前提还是我们说的在线架构:
1. 前置准备
master【node1】 vip unbind【VIP解绑】,同时检查原来的master是否还有连接及连接的处理,如果还存在连接,要考虑把这些连接都干掉:
然后source /tmp/1.sql 就行了。
master上干掉残余连接后,前置准备还要注意下slave的read_only问题,如果slave设置了read_only,记得取消。
master干掉连接后,可以短暂的实现数据静止。
2. 接管VIP之前需要做复制一致性检查
这个复制一致性检查很简单,我们之前讲的老办法:
确认复制pos都对上了,node2才能接管VIP,这里在脚本里面最好把判断一致性通过的log_file和log_pos记录下来。
3. node2上绑定VIP
slave vip bind
这样就完成了整个的切换流程。
总结来说,非常简单:
- 复制环境引入VIP,修改应用服务器连接数据库地址为VIP
- 解绑原master上的VIP
- 干掉原master上的所有应用连接
- 检查同步一致性
- slave上绑定vip
以上这些动作,如果脚本准备充分,执行脚本一般3-5秒就可以搞定了。
虽然是在线架构,3-5秒左右的数据库短暂影响,在业务低峰期应用基本是无感知的【切换我们还是需要错峰来执行】,绝对能满足在线架构系统的要求。
另外的一个注意点就是要确保检查同步都完成了,同步没完成的话,是还要花一点时间等待直到完成为止。
那成功切换后,现在VIP绑定在了node2上【新的master】,那node1重新要从node2上同步数据,node1变为slave,这里分情况讨论:
- 如果node1和node2之前做的是双master架构,那最简单,不用多管【实际架构中双master也是最多用的,就是切换后维护方便】
- 如果node1和node2只做了主从架构,那还得在之前的脚本里【检查同步是否完成这1步中】记录下log_file和log_pos,在node1【新的slave】上重新做一次change master to master_log_file=XXX,master_log_pos=XXX,然后在node2【新的master】上reset slave all,清空原来的show slave status\G信息
第二个实战:在线系统环境中调整为MySQL级联复制架构
假设,现有的架构如下:
目前的架构是:
A->B(单向复制)
A->C(单向复制)
如何把上面的架构调整为:A->B->C 的级联复制架构呢?
同样,我们需要在线实现,不能对业务有太大影响【假设业务都是在master上执行,没有做读写分离拆分】
这里的思路是:
- 不去动master,因为业务都在master上【这样可以保证对master的0影响,满足在线业务不间断的需求】。而是想办法让slave1和slave2停在一个相同的pos位置,只要slave1和slave2静止下来,我们就可以很方便地进行架构调整了:
- 实现目的:让slave1(B)和slave2(C)停在相同的pos位置上,数据静止下来。
操作步骤:
1.(master)A上执行 : create table biaobiao(id int);
2.(slave)B和C上执行: drop table biaobiao;
3.(master)A上执行 : drop table biaobiao;
上面的操作会导致B、C上同步报错,都报错后【sql thread报错】,我们就可以开始比对sql_thread执行的位置了(实际目的就是人为地去让B和C停在同一个位置上)
4.slave1(B)和slave2(C)上show slave status\G 发现报错,同时检查各个复制点的位置是否相等(此时Slave上的sql_thread应该都停止工作了):
检查复制点位置的公式如下:
检查后公式都相等,就代表B和C已经停到同一个复制位置了。B和C的数据此时应该是完全一致。
此时B和C上的relay_master_log_file和exec_master_log_pos 都不会变化了。
一旦让B和C停在了同一个位置,那接下来的事情就好办了。
此时,C就变成了B的Slave
完成了架构调整 A->B->C
如果脚本来做,一定要在B上多做几次show master status,确保输出不会变化,静止下来,同时B和C的pos要相等:
B节点: show master status;
sleep(10);
B节点: show master status;
因为此时sql thread已经报错卡住了【sql_thread:no 、io_thread:yes】,因此前后的show master status在B上肯定是一致的。
B是静止的,C也是静止的,然后B和C的exec_master_log_pos相等。
接下来还没完,还需要恢复A->B的复制,A到B的复制sql thread人为搞断了,还得恢复:
在B上跳过复制错误,在B上执行
因为,这个复制错误是我们人工模拟出来的,是有把握的,所以才能跳过。任何没有把握的复制错误,都是不能轻易跳过的。
8.最后全部恢复后,进行一把测试,测试下新的架构是否通的。有条件再效验下数据。
这里,完成了我们需求的复制在线架构调整。
第三个实战:双master复制环境中的运维管理
实际上,业务规模稍微大一点的公司,在做MySQL的时候都会搭建双master【主主】架构。相较于单向的主从复制,双master架构在运维管理上相对更方便,省事。因为单向的主从结构,在发生主从切换后,还得在原来旧的master上再搞一次change master to ,同时在新的master上执行reset slave all;
这就比双master复制架构稍显繁琐了。双master架构在发生切换后,撒事不用做,仅漂移VIP就行了【双master和keeaplived这类HA组件结合起来也能非常方便得实现自动切换】。
那如果仅仅就是两个节点的双master环境,大家需要注意的就是在生产环境保持其中一个节点来进行业务数据写入就好了。
那为什么明明双master是双向复制,照理两个节点都可以双向同时写入数据,我这里却只建议大家单边写入呢?单边写是不是有点浪费资源呢?
也许有朋友会答到MySQL里面有两个不错的参数:
我只要在双master的两个节点上分别把这两个参数配置正确,就可以很好的解决双写冲突的问题,同时也最大化的利用了服务器资源,减缓了单台的写入压力。
的确,利用auto_increment_increment和auto_increment_offset能够解决部分的写入冲突问题,但是仅仅只能解决insert的冲突,但遇到是针对同一行数据的update和delete操作,配置这两个参数还是搞不定的。
试想一下,在双master的两个节点上同时发起下面这条SQL语句【假设在业务高峰期并发度高的时候】:
update tb set col1=xxx where id = 1;
显然会引起写入冲突和锁竞争,就无疑就给我们挖坑了。
因此,auto_increment_increment和auto_increment_offset参数其实只能解决insert的写入冲突,而对同一行的update或delete冲突却无法解决。
也许你还会说,我们公司的业务拆分做得不错,在node1我们只会写A表,在node2上只会写B表,不可能在两个节点上发生update同一行数据的问题。这听起来确实是一个不错的最终解决写入冲突的方案,但你依然可能遇到另外一个问题:
如果启用双节点双边写,其中一个节点不幸挂掉的话,切换另外一个节点能顺利接管吗?你会面临性能瓶颈的问题,在大多数时候,你用了两个节点的资源来承担读写压力,当突然变成一个节点来承担所有的读写压力时候,这个节点的性能能抗得住吗?
这跟Oracle RAC的架构设计有点类似,两个节点同时提供服务的话,你还得考虑很多性能上的问题,每个节点都要做性能的预留,不然其中一个节点挂了,另外一个节点就算你能故障切换过来,性能扛不住的话也是一点意义没有。
因此,在双master复制环境中我建议大家用单节点抗业务就好了,另外一个节点就完全预留做为故障切换使用,没有必要用两个节点来同时承担业务压力,因为这可能会给我们在运维管理上挖坑。
这是双master架构里只有两个节点的运维管理注意点,但很多时候,我们还会为双master环境引入一个或多个slave来分担读(read)的压力,或者用来做一些经分统计和统计报表用,就像下面这样:
上面这种架构才是我们实际运维管理工作中最常见的架构。
在上图里面:
- M1和M2互为双master复制环境,而S1是一个单独的slave。
- R_IP代表每个服务器的real_ip(真实IP),W_VIP代表虚拟的VIP,应用程序的所有写入操作应该都是通过VIP来连接到数据库,在上图的架构里W_VIP挂在了M1上,代表M1这个节点是主要用来抗应用业务的节点。而M2是完全为了作为故障切换(failover)的节点,S1是我们做统计报表的(分担部分读压力)的节点。
在这类架构中,我们要理清的第一个问题是:
S1是挂在M1上,还是挂在M2上,或者说是挂在W_VIP上呢?
- 很明显,传统复制模型里挂在W_VIP上是不行的,原因是: m1和m2 binlog filename 、binlog position 完全有可能不一样。(M1和M2的 show master status出来的结果是不同的),如果是GTID复制的话,原理上可以挂在W_VIP上,但即使是GTID,一般也不会这么做。
另外,如果把S1挂在M1这个抗业务的节点上,那当M1节点不幸挂掉,在传统复制原理下,S1和M2就很难联系起来了。、
因此,正确答案是:S1必须挂在不是抗业务的那个双master节点上,即挂在图里的M2这个节点,这样做的好处是当M1不幸挂掉,M2和S1的复制不会受到任何影响。
这是我们设计架构时的注意点。
另外一个重要的注意点是,当M1挂掉后,W_VIP会漂移到M2节点上,保证应用程序继续能正常连接数据库,像下图这样:
那当M1挂掉后,我们把M1修复好后,M1还会继续加入到复制集群架构里,像下面这样:
此时的架构变为了:
M2->S
M2<->M1
但如果是这样的架构,就和我们刚才提到的设计依据:
- “S1必须挂在不是抗业务的那个双master节点上”相违背了,这里明显S1是挂在了抗业务的M2架构上,是不正确的,我们还需要调整S1在复制架构中的位置,把S1挂到M1下才行。
我们还需要想办法从上图左边的架构调整为上图右边的架构,同样,这个调整我们肯定也需要在Online在线业务的背景下去完成。
那这个调整其实跟我们在第二个实战里讲的案例非常类似了。
原来的架构是:
M2<->M1(双向复制)
M2->S1(单向复制)
现在要把架构调成为:M2<->M1->S1(级联复制)
要实现这个调整的办法,我们就可以完全照着第二个实战里说的方法来做【这里请大家回到前面的内容,再仔细阅读一遍】。
基本的思路依然是让保持对M2的0影响,而通过人为的模拟复制错误让M1和S1停在相同的复制点位上。
这里稍微不同的就是因为M2和M1互为主主,M1上的人为模拟操作也会同步到M2上,所以在M1上做人为模拟操作的时候,要记得临时禁止写binlog,免得把M1上做的人为模拟操作也同步到M2上了。
后面的步骤和我们在第二个实战里面讲的步骤完全一致,照搬就行了。我就不赘述了。
完成以上的调整,才算一个双master+slave复制架构的切换完成。
第四个实战: 在线系统环境把级联复制架构再调整回一主多从的架构
原来的架构是:A->B->C【级联复制】
现在要把架构调成为:
A->B
A->C 【一主两从】
同样,A节点是抗应用业务的节点。
其实经过我们之前的几个案例,这个案例是很简单的了,这个我就简单提下:
对上面的这类需求,在B节点上stop slave就行了,stop后等待会,B和C的数据应该就能一致了:
检查公式:
满足以上公式,代表B节点和C节点的同步是一致的了。
接下来在C上进行change master的修改,change master到A上,在C节点的MySQL上操作:
注意,这里要取B节点上show slave status\G的relay_master_log_file , exec_master_log_pos
start slave;
最后回到B节点的MySQL上操作:
start slave;
整个架构调整完成。
总结
以上是一些工作中的总结,重点在于给大家提供一种思路,希望大家能在这个基础上继续扩展思维。
有了清晰的思路,编写一些MySQL复制运维中的管理脚本也并不是难事。
如果大家在编写复制管理的脚本中遇到任何问题,也可以随时找我交流。
另外,每个公司的业务对在线【Online】架构的要求可能是不同的,有的公司业务在Online环境里也能容忍在特定时间的3-5秒甚至更长时间的业务暂停写入,而有的公司业务却是0容忍,我们应该紧密结合业务上的需求,以需求为导向,才能更好的完成MySQL复制管理工作。
当然,随着MySQL版本的不断发展,比如:MySQL5.6引入的GTID复制,MySQL5.7引入的group commit和并行复制,这些新的技术也逐渐让MySQL的复制运维管理工作变得更加得便捷,高效。
但是,不论技术如何发展和变化,掌握MySQL经典传统复制的运维管理技巧仍然能帮我们搞定很多在线环境的管理需求。
转自
业务零影响!如何在Online环境中巧用MySQL传统复制技术_Mysql_数据库-ITnose
http://www.itnose.net/detail/6525929.html