先说结论:
如果在线MHA切换,为了减少对系统的影响,应该先让实例只读,等待大查询结束之后,才开始切换,而更好的做法的,自动kill掉大查询,确保切换影响时间最少。
(1)正常的MHA切换程序中,mha会调用FLUSH NO_WRITE_TO_BINLOG TABLES这个语句;
(2)这个时候,如果有大的查询在执行,mha就会需要等待。同事前天做了一次mha在线切换,切换的时候,当时有如下的语句正在执行:
select gpl.ID, gpl.GAME, gpl.LOGIN_ACCOUNT, gpl.PRODUCT_ID, gpl.PURCHASE_TOKEN, gpl.ORDER_ID, gpl.CO_ORDER_ID, gpl.REFUND_STATUS, p.GPID, p.TWD_MONEY, p.ORDER_TIME, gpl.DATETIME,gpl.CHARGE_TIME, p.PACKAGE_NAME, p.PAY_IP from google_play_logs gpl left join payment p on p.LOGIN_ACCOUNT = gpl.LOGIN_ACCOUNT and p.ORDER_ID = gpl.ORDER_ID where gpl.DATETIME >= ‘2017-09-19 00:30:02‘ and gpl.REFUND_STATUS = 1 order by gpl.ID desc
(3)这个语句正常的时候,执行只需要4秒钟不到,就算没有缓存,也只需要1分多钟,而这里执行了480秒,是因为系统资源被占用,备份导致了IO忙,IO忙导致SQL语句执行效率低,从而放大了flush table的影响,导致问题的出现。
(4)测试中能发现,flush table,只需要等待比它早开始的查询结束,flush table就能完成,并不需要等到全部查询完成。
如下图,id 9394执行了flush table,它和time是121秒,说明它已经执行了121秒,但id 9394被阻塞了,它被id 21751线程所阻塞,进程21751在flush table之前已经开始执行,它已经执行了137秒。
进程6333也是一个查询语句,它的状态也是waiting for table flush,但它的开始时间晚,time才87秒。
下面这个图,只有6333存在,说明21751执行完成之后,9394随之执行完成,9394不会被6333所阻塞。
(5)再解释一下,为什么同事在ctrl+c终止了mha的切换之后,故障并没有马上恢复,那是因为ctrl+c虽然终止mha程序,但flush table其实并不能被终止,测试如下。
在这里id6333执行insert into t1(b) values (‘ccc‘);它被阻塞了,但我们执行kill 21751,kill掉flush table,之后再通过show processlist,能看到insert into t1(b) values (‘ccc‘)的state并没有变化,它仍然处于:Waiting for table flush 状态,
需要等到select *,sleep(30) from t1 limit 5 执行完毕,insert into t1(b) values (‘ccc‘);才会成功执行。