有一个业务需要分 1000 个库,每一个库中都有 80 个表,总共就是 80000 * 2 个文件。文件使用率还挺高,大概是 60000 * 2。
这个业务采用的高可用架构是 MMM,由于集群机器在硬件检查时发现有问题,必须要换掉。于是想了一个比较简单、影响面较小的方法去解决,就是找了另外两台机器迁移过去。同时,要求这四台机器属于同一个网段,VIP(虚拟 IP 地址)在机器之间可以漂移,这样业务就不需要修改 IP 地址即可迁移,相当于两次主从切换过程。
切换方案如图 16.1 所示。
从图 16.1 中可以看到,切换过程很简单,如下三步。
先将原来的写节点(db1)与一个新的节点(db3)切换成一套集群,也就是把在 db2 上面的 VIP(读流量)切换到 db3 上面,此时 db1 与 db3 组成一套新集群。
接着将 db1 和 db3 的角色互换,让 db3 成为写节点,db1 成为读节点。
最后,再将 db1 读节点上面的 VIP(读流量)切换到 db4 上面,此时新的集群就是 db3 和 db4,db3 为写节点,已经切换完成。这样的变更是晚上做的。做好之后,观察了一段时间,发现没有什么问题(因为压力小),所以觉得事情完成了,睡吧。 第二天上班之后,业务反映说迁移之后,数据库比原来慢了 10 倍(10 倍啊!感觉不可思议)。询问了一番,说没有任何变更,只是做了迁移之后就成这样了。同时经过观察,只有写库的读操作变慢了,而读库的读是不慢的。最后,业务已经受不了了,要求切换回去。还好,db1 还正在从 db3 复制,做了一个回退操作,把写挂在 db1 上面,把读挂在 db3 上面。神奇的是,问题解决了!好吧,那就先这样,走出去的路不能回头,总是要迁出去的,所以先在新旧两台机器上面挂着,查明原因后再切换回去(这样少做一步)。
以上是背景。
问题分析
环境对比
db1 写入时,db1 写不慢,读不慢,db3 也不慢。
db3 是新的硬件,db1 是老的、有问题的硬件。
db3 切换成写之后,在慢查询文件中明显看到很多慢查询(使用相同的语句查询,原来是 50ms,现在是 500ms),和监控是一致的。
db1 和 db3 配置文件有差别,如图 16.2 所示(左边是 db3 的,右边是 db1 的)。
其他方面,环境完全相同,业务方面没有任何更改,重现慢的现象,只是需要切换而已。
图 16.3 是切换过程中的监控图,高起来的就是把流量切换到 db3 的情况,处于低谷的就是切换到 db1 的情况,效果非常明显,慢得立竿见影,好神奇!
原因分析
从对比中可以得知,db1 是正常的(以前长时间在这个机器上跑,没有问题),而 db3 是不正常的。这个业务目前是读多写少,现在的现象是读慢。因为写少,没有发现慢,就不考虑了。
接着就是硬件的区别,二者都是 PCI-e 卡,老的、坏的概率比较大,从经验上来看新的会比较好,这是一个值得怀疑的点。但实际上,针对这个问题,找到了新卡的技术人员进行分析,将写切换到 db3 上之后观察,发现 IO 非常小,能看到的监控指数都非常正常。(他们也很纳闷。)
除此之外,唯一的区别就是二者的配置了,但从图 16.3 中可以看到,没有一个参数可以影响到让数据库的响应时间是原来的 10 倍。
但上面这些都只是分析,硬件测试之后,没有发现问题(也不能说就不是硬件的问题,一直吊在那里)。那只剩下配置了,所以接下来从这里入手吧,希望能成功!
那么,再找一个夜深人静的夜晚……
案例解决
首先要做的事情是,把 db3 的读流量切换到 db1,然后把配置完全换成 db1 的配置,将数据库重启,然后上线。此时,db1 是写节点,db3 是读节点,最神奇的时刻即将到来。
切换之后,经过观察,竟然没有问题了。问题已经解决,那么说明还是上面列出来的配置差别引起的问题。
那么解决之后,下面的工作就是重复一开始的工作,把 db1 下线,让 db4 上线。此刻,之前的迁移工作已经完成,线上服务没有问题。
但……开发同学,能给我半个小时,让我看看是哪个参数引起的么? 得到的回答是:“迅速点,就这一次,给你 20 分钟。”
把最有可能的参数找出来,比如字符集(实际上,上面列出的每一个,我们认为都不会有多大影响),考虑到字符集是不可动态修改的参数,所以先把这个改了。重启,然后一个一个地动态修改、业务重启重连等,都没有发现。修改的这些参数包括:sql_mode、join_buffer_size、max_heap_size、sort_buffer_size,这些都没有影响。
结果已经说好的只有这一次,那就这样吧,任务成功完成,问题解决失败。
然而,这个问题“才下手头,却上心头”,总有一件事放心不下,约吧。 和开发商量了一下,我们想解决这个问题,知道其所以然,防止在其他业务上出现同样的问题。好吧,再给你们一次机会(来之不易啊)。
那么,再找一个夜深人静的夜晚……这次的月亮好像比上次更圆一些,是好日子的征兆么?
操作之前,还简单规划了一下,下面是当时的一个计划步骤。
^* 黑体的表示已经专门测试过,没有影响
步骤如下。
考虑到先重现问题,首先应该全部使用新配置测试一次,确定问题是否还存在。
因为重点考虑问题是因为 sql_mode 引起的,所以第二次只将这个参数改为老配置,这样就可以测试出其他配置组合时有问题,或者是没有问题,从而得出结论是 sqlmode 的问题。
如果上面还没有找到问题原因,那么就是除了 sql_mode 之外的其他参数组合出了问题(如果没有,则见鬼了)。此时,通过二分法测试,先测试 innodb_flush_log_at_trx_commit、innodb_open_files、sort_buffer_size 三个参数。
如果发现上一步有问题,则再进行二分;如果没有发现问题,则对 sync_binlog、join_buffer_size、tmp_table_size 进行二分。
如果能走到这里,那也是醉了。
再说吧。
按照步骤,一步步地开始做。
首先使用有问题的配置,测试一遍,发现是老样子,还是有问题的(真是幸运,问题还存在)。
把除了 sql_mode 之外的所有参数改成新的,其他都用老配置,测试发现没有问题。
做完了,也是没有问题。
做完了,还是没有问题。
我醉了。
此时当事人已经搞不清楚了,难道是某两个的组合会导致出现这样的问题?如果是这样的话,那情况就太多了,天已经亮了,很累,放弃吧!