• 微信
    咨询
    微信在线咨询 服务时间:9:00-18:00
    纵横数据官方微信 使用微信扫一扫
    马上在线沟通
  • 业务
    咨询

    QQ在线咨询 服务时间:9:00-18:00

    选择下列产品马上在线沟通

    纵横售前-老古
    QQ:519082853 售前电话:18950029581
    纵横售前-江夏
    QQ:576791973 售前电话:19906048602
    纵横售前-小李
    QQ:3494196421 售前电话:19906048601
    纵横售前-小智
    QQ:2732502176 售前电话:17750597339
    纵横售前-燕子
    QQ:609863413 售前电话:17750597993
    纵横值班售后
    QQ:407474592 售后电话:18950029502
    纵横财务
    QQ:568149701 售后电话:18965139141

    售前咨询热线:

    400-188-6560

    业务姚经理:18950029581

  • 关注

    关于纵横数据 更多优惠活动等您来拿!
    纵横数据官方微信 扫一扫关注官方微信
  • 关闭
  • 顶部
  • 您所在的位置 : 首页 > 新闻公告 > 云主机读写分离异常如何修复?

    云主机读写分离异常如何修复?

    说起来你可能不信,我第一次遇到读写分离异常的时候,竟然是在一个周六的凌晨,刚刚看完一部电影准备睡觉。手机突然疯狂震动,监控系统显示,用户端出现了大量的“查询超时”和“数据不一致”的报错。我揉了揉眼睛,打开电脑,屏幕上的数据让我瞬间清醒过来。主库的压力曲线是正常的,但好几个从库的延迟时间已经飙升到了几千秒,有些从库干脆就停在那儿不干活了。

    那天的排查和修复过程持续了整整一个晚上。今天就把这次经历以及后来积累的经验好好梳理一下,希望能帮到正在或者将要面对类似困境的朋友。

    先搞清楚读写分离到底出了什么状况

    读写分离这个架构,说起来其实不复杂。主库负责写入,一个或者多个从库通过复制主库的变更来提供读取服务。应用这边,写请求走主库,读请求走从库,各司其职。听起来很完美,但一旦这个链条上的某个环节出了问题,事情就变得棘手了。

    常见的问题大概有这么几类。主从复制延迟,这个是最普遍的,从库追不上主库的更新速度,导致用户刚写完数据立刻去查,查到的却是旧数据。从库复制中断,复制线程因为各种原因停掉了,从库的数据停留在过去的某个时间点,再也不往前走了。数据一致性问题,主库和从库的数据因为某些原因出现了差异,同样的查询在不同从库上可能得到不同的结果。连接池或者负载均衡层面的问题,读请求本该去从库的,结果跑到了主库上,把主库压垮了。还有一类是代理中间件的问题,比如读写分离的代理服务本身出了故障或者配置错了。

    那天晚上的故障,属于典型的复制延迟爆炸。主库在业务高峰期执行了一批大批量的数据更新操作,产生了巨大的binlog日志量,而从库的SQL线程只有一个,单线程回放的速度根本赶不上主库生成日志的速度。延迟就像滚雪球一样越滚越大,从几分钟变成几十分钟,最后变成了几千秒。

    第一步:先摸清现状,别急着动手

    发现读写分离异常之后,很多人第一反应是赶紧去调整配置或者重启服务。这个做法其实挺危险的,因为在你没有搞清楚具体原因之前,任何操作都可能让情况变得更糟。

    正确的做法是先做一次全面的“体检”。登录到云主机上,连接到数据库,执行一条命令看看复制的基本状态。对于MySQL来说,在从库上执行“show slave status”这条命令,能看到几乎所有你需要的信息。Seconds_Behind_Master这一项告诉你了从库落后主库多少秒。Slave_IO_Running和Slave_SQL_Running这两个状态分别代表了IO线程和SQL线程是否在正常运行。如果这两个状态中有一个不是Yes,那说明复制已经彻底中断了,需要更深入的分析。

    那天的故障排查中,我首先看了Seconds_Behind_Master,数值是三千多秒,说明延迟确实很严重。但两个Running状态都是Yes,这说明复制线程还在工作,只是跑得太慢。为了确认问题到底有多严重,我还在主库上执行了“show master status”,记下了当前binlog的文件名和位置,然后在从库上用“show slave status”看了一下从库已经执行到的binlog位置。两个位置一对比,差距大概有两三个binlog文件,每个文件的大小是一GB,这就意味着从库要追赶的数据量有好几个GB。

    搞清楚现状之后,我心里就有底了,这不是什么神秘的故障,就是一个典型的复制延迟问题,而且复制的链条是通的,只是速度跟不上而已。

    第二步:找出导致异常的罪魁祸首

    知道了问题的类型,接下来就要找到具体的原因。读写分离异常的原因可以粗略地分为三类,主库产生了太多变更,网络传输慢,或者从库回放太慢。

    从库回放太慢是最常见的原因,尤其是在MySQL这种默认采用单线程回放的关系型数据库上。你可以用“show processlist”命令查看从库上正在执行的SQL语句,看看是不是某一条特别耗时的语句卡住了回放线程。那次排查中,我发现从库的SQL线程正在执行一条大型的alter table操作,涉及一张几千万行数据的表,这个操作在主库上只用了不到一分钟,但在从库上因为硬件配置不同,执行了快半个小时还没有完成。在这个大操作完成之前,后面所有的变更都得排队等着,延迟自然就越来越大了。

    除了大表变更,还有一种情况是主库上存在大量的行级修改操作。比如说主库上跑了一个批量更新的业务脚本,一次性修改了几百万行数据。这些修改在binlog里会生成几百万条记录,从库需要一条一条地去执行,如果从库的IO能力跟不上,延迟就产生了。还有一种不太常见但确实存在的情况,就是主从之间的网络带宽不足,binlog传输不过去,IO线程就成了瓶颈,从库接收新日志的速度跟不上主库生成日志的速度。

    那天的故障中,我通过分析发现根本原因是那条大表变更操作。同时我还注意到一个细节,从库的磁盘类型是普通的云盘,而主库用的是更高性能的云盘,IO能力的差异放大了延迟的问题。找到了根本原因,后续的处理就有了明确的方向。

    第三步:根据异常类型选择修复策略

    修复读写分离异常的方法不是唯一的,要根据具体的原因和业务对实时性的要求来选择不同的策略。我来逐一说说常见的几种修复手段。

    针对复制延迟,如果你是先看到延迟在不断增长,但复制线程没有停,那么最直接的办法就是想方设法加速从库的回放速度。一种方法是临时把从库的只读模式关掉,增加并行复制的线程数。从MySQL 5.7开始支持基于逻辑时钟的并行复制,到8.0版本更是大大增强了并行复制的能力。如果你的数据库版本支持,可以动态调整slave_parallel_workers这个参数,让从库用多个线程来回放日志,能显著提升回放速度。

    但增加并行度也不是万能药。如果延迟的原因是一条大事务在阻塞回放,那么增加再多的线程也没用,因为大事务本身必须由单个线程来执行。这时候的临时处理办法是等待这个大事务执行完成,或者评估一下是否可以在从库上跳过这个操作,等业务低峰期再专门处理。跳过操作需要谨慎,涉及到设置sql_slave_skip_counter或者使用pt-slave-restart这类工具,搞不好会引发主从数据不一致。

    另外一种修复方式是通过重新搭建从库来彻底解决严重的不一致或者无法修复的延迟问题。这个方法虽然耗时比较长,但在某些场景下反而是最可靠的。比如说主从数据已经严重不一致了,简单的跳过或者追赶已经无法让数据回归一致状态,这时候就需要用主库的最新备份重新搭建从库。操作步骤通常是这样的,先在主库上执行flush tables with read lock,然后记录binlog位置,接着用mysqldump或者xtrabackup创建一份物理备份,把备份传输到从库所在的云主机上,导入数据,最后用change master to命令重新指定主库的日志文件和位置,启动复制。

    这个过程比较耗时,数据量大的时候可能需要几个小时甚至更久。所以如果业务允许,建议在业务低峰期操作。而且云主机的网络带宽和磁盘IO能力会直接影响这个过程的速度,可以临时给从库所在的云主机升级一下配置来加速备份的导入和复制追赶的过程。

    还有一种情况是读写分离的配置本身出了问题。我就遇到过这样一个案例,客户的应用程序配置文件中,读写分离的配置写错了,所有的查询请求都发往了主库。一开始业务量小的时候还没什么感觉,随着业务增长,主库的读压力越来越大,响应越来越慢,最后主库的CPU被打满了,所有的请求都卡住了。

    这个问题的修复其实很简单,就是修正配置文件中的读写分离规则,把读请求的地址改成从库的地址,重启应用就好了。但教训是深刻的,读写分离的配置绝对不能粗心大意,上线之前一定要在测试环境验证规则是否正确。

    代理中间件的问题也值得专门说一下。很多云上用户会用数据库代理来做读写分离,代理自动识别SQL类型,把写请求发往主库,读请求分发到各个从库。如果代理服务本身出了故障,比如进程崩溃了或者网络不通了,那所有的数据库请求可能都无法正常处理了。还有一种情况是代理的负载均衡策略配置不当,比如把所有的读请求都分配给了同一个从库,导致这个从库压力过大而其他从库却闲着。

    遇到代理的问题,修复手段包括重启代理服务,检查代理的配置文件,调整负载均衡策略,或者临时绕过代理让应用直连数据库。如果代理服务的问题一时半会儿解决不了,切换到直连模式是恢复业务最快的方式,当然代价是失去了自动的读写分离能力,需要应用层自己做好区分。

    第四步:处理最头疼的主从数据不一致

    复制延迟虽然麻烦,但通常不会造成数据丢失,只要从库最终能追上主库,数据就是一致的。但主从数据不一致这个问题就严重多了,它意味着从库上的某些数据跟主库不一样,这种差异可能是部分缺失的更新,也可能是完全错误的数据。不一致一旦发生,读写分离查询出来的结果就可能出现混乱,同一个请求在不同的从库上得到不同的结果,对业务来说是致命的。

    导致数据不一致的原因通常有几个。一种是从库在执行更新时碰到了主键冲突或者记录不存在的情况。比如说主库上删除了某条记录,但从库上因为某些原因没有执行这条删除操作,那从库上就多了一条主库不存在的记录。另一种是复制过程中binlog的内容本身有问题,比如主库使用了不确定的语句,像用了uuid函数或者limit不加order by的情况,同样的语句在主库和从库上执行的结果可能不一样。

    发现不一致之后,常用的修复工具是pt-table-checksum和pt-table-sync这对组合。pt-table-checksum在主库上运行,它会逐表逐行地计算校验和,然后和从库上的校验和进行比对,找出哪些表哪些行存在差异。pt-table-sync则根据校验的结果生成修复的SQL语句,在从库上执行这些语句,把数据修正成和主库一致的状态。

    用这个工具的时候有几个要点需要注意。第一,不要在业务高峰期运行,因为校验和修复过程会对数据库产生额外的负载。第二,修复之前最好先在从库上做一次备份,防止操作失误导致更严重的问题。第三,如果差异的行数非常多,可能需要分批次修复,每次只处理一部分数据。第四,修复完成之后要分析一下不一致的根源,从根本上避免同样的问题再次出现。

    第五步:建立主动防御机制

    修复完异常之后,最重要的工作是总结经验,建立主动的预防机制,不让同样的问题再次发生。根据我这些年的经验,有以下几个方面的措施非常有效。

    监控必须到位。对于每一个从库,都需要监控复制延迟的秒数,设置合理的告警阈值。比如说超过六十秒触发警告,超过三百秒触发严重告警,需要立即处理。同时还要监控复制线程的运行状态,如果Slave_IO_Running或者Slave_SQL_Running不是Yes,立刻告警。监控主库的binlog生成速率和从库的日志应用速率,如果生成的速率持续高于应用的速率,延迟就会不可避免地上涨,需要提前介入。

    对于关键的从库,可以配置多线程复制。如果你的数据库版本支持,建议开启并行复制,并设置合适的并行工作线程数量。线程太多会增加管理的开销,线程太少又起不到加速的作用,需要根据服务器的CPU核心数和磁盘IO能力来测试确定一个合理的数值。

    对大表变更操作一定要有严格的流程管控。在生产环境的主库上执行大表变更之前,必须评估这个操作对主从复制的影响。如果预计会产生大量的binlog,最好安排在业务低峰期执行,或者使用pt-online-schema-change这类工具来减少对主库和复制的冲击。同时要确保从库的硬件配置不低于主库,否则从库在处理相同的操作时会比主库慢,延迟就会持续扩大。

    读写分离的架构设计上也应该考虑一些容错机制。比如在应用层面实现读请求的降级策略,当从库的延迟超过某个阈值时,自动把读请求切换到主库,虽然这会增加主库的压力,但至少能保证用户不会读到严重过期的数据。还有一种做法是在代理层面实现读请求的负载均衡和健康检查,当某个从库的延迟过高或者复制中断时,自动把它从负载均衡池中摘除,等恢复健康后再加回来。

    复盘:那天晚上的故事是怎么收尾的

    回到那天凌晨的故障处理,经过一系列的排查,我确定了根本原因是大表变更加上从库IO能力不足导致的严重延迟。当时的选择是这样的,先通过调整从库的配置参数,临时把同步模式做一些调整,让从库能够更快地追日志。然后在业务层面做了一个临时的调整,把一部分实时性要求不高的查询切换到了另一个相对空闲的从库上。

    等到那条大表变更终于执行完毕之后,我观察到延迟开始逐步下降,从三千秒降到两千秒,再到一千秒。我在监控前面守了两个多小时,看着延迟一点点回落,那种感觉就像是看着一个生病的机器在慢慢康复。等到延迟降到十秒以内之后,我把临时的调整都恢复了,让所有读请求重新回到正常的读写分离架构上。

    第二天,我组织团队做了一次复盘,制定了几个改进措施。一是所有的大表变更操作必须提前评审,评估对复制的影响,并安排在业务低峰期执行。二是把从云的云主机配置升级到和主库持平的水平,消除硬件差异带来的性能瓶颈。三是在监控系统中增加了复制延迟的趋势预测功能,当延迟增长速度超过某个阈值时提前预警。四是开启了并行复制,并经过测试确定了合适的工作线程数量。

    最后

    读写分离异常这件事,说大不大说小不小。赶上业务高峰期,稍微延迟几分钟就可能引发用户投诉。但说到底,大多数读写分离异常都是有规律可循的,不是那种完全无法预测的幽灵故障。只要你对主从复制的原理有清晰的理解,手上有扎实的排查工具,脑子里有一套成熟的应对流程,处理起来就不会手忙脚乱。

    预防永远比修复更重要。花一点时间把监控做完善,把流程定规范,把架构设计得更健壮一些,这些投入都会在你未来遇到故障的时候成倍地回报给你。



    最新推荐


    微信公众帐号
    关注我们的微信