刚刚结束的2015年双11,天猫以912亿的成交量再次打破去年的记录成为一个奇迹,大家可能不知道,这些天猫的订单最后的处理都是放在阿里云聚石塔的机房完成,从2012年开始,淘宝的ISV,商家就开始把他们的订单,CRM后台系统逐渐迁移到云上,最核心的数据库就是存放在RDS中。
双11之前用户都会进行大批量的弹性升级,期间有较多用户反馈,在弹性升级后性能出现了大幅度的下降,其中一个用户有两个RDS,一个RDS进行了弹性升级,另外一个RDS没有弹性升级,结果弹性升级后的RDS反而出现了性能下降,这让我们反思不得其解。RDS的弹性升级包括了两部分,一部分是磁盘容量的升级,另一部分是内存容量的升级(内存升级会同时升级数据库的连接数,CPU,IOPS),那么是什么原因导致了性能下降?
1.是不是弹性升级后,后端的DB性能提升,前端的流量增加,导致后端DB响应缓慢?
通过后端的监控查看,数据库的QPS并没有显著的增加,但是RT却是增加了许多,所以此种情况排除。
2.是不是SQL的执行计划发生改变,导致数据库的性能降低?
通过监控发现即使是普通的insert 语句也会出现执行缓慢的情况,慢日志出现了较多非常简单的SQL,所以此种情况排除。
3.看到有非常简单的SQL也执行缓慢,排查主机是否存在资源瓶颈?
通过监控显示,实例所在的物理主机的压力非常的低,不是主机的资源争抢导致的性能瓶颈,所以此种情况排除。
4.在排除了以上可能的情况后,在数据库连接出现较多连接堆积的时候,进行一次pstack查看数据库中连接到底在等待些什么:
#0 0x00000000008d3fd9 in buf_LRU_invalidate_tablespace ()
#1 0x0000000000904ef6 in fil_delete_tablespace ()
#2 0x00000000008627cf in row_drop_table_for_mysql ()
#3 0x000000000084d64e in ha_innobase::delete_table(char const*) ()
#4 0x0000000000554368 in rm_temporary_table(handlerton*, char*) ()
#5 0x0000000000556ea2 in close_temporary(TABLE*, bool, bool) ()
#6 0x000000000055a878 in drop_temporary_table(THD*, TABLE_LIST*, bool*)
#7 0x0000000000600939 in mysql_rm_table_no_locks(THD*, TABLE_LIST*)
#8 0x00000000006016dd in mysql_rm_table(THD*, TABLE_LIST*, char, char) ()
#9 0x0000000000599b35 in mysql_execute_command(THD*) ()
#10 0x0000000000788629 in sp_instr_stmt::exec_core(THD*, unsigned int*) ()
#11 0x000000000078d267 in sp_lex_keeper::reset_lex_and_exec_core(THD*, unsigned int*, bool, sp_instr*) ()
#12 0x000000000078d724 in sp_instr_stmt::execute(THD*, unsigned int*) ()
#13 0x000000000078b1b3 in sp_head::execute(THD*, bool) ()
#14 0x000000000078c587 in sp_head::execute_procedure(THD*, List<Item>*)
#15 0x0000000000597f84 in mysql_execute_command(THD*) ()
#16 0x000000000059bed4 in mysql_parse(THD*, char*, Parser_state*) ()
#17 0x000000000059deac in dispatch_command(enum_server_command, )
#18 0x0000000000641b8d in do_handle_one_connection(THD*) ()
#19 0x0000000000641cdc in handle_one_connection ()
#20 0x0000003bd6807851 in start_thread () from /lib64/libpthread.so.0
#21 0x0000003bd64e767d in clone () from /lib64/libc.so.6
看到了buf_LRU_invalidate_tablespace 这个函数后,其实就豁然开朗了,用户业务中频繁的drop table,在5.5版本DROP TABLE操作会对innodb的整个buffer pool的LRU链进行两次扫描(DROP期间的扫描操作会持有buf_pool::mutex,导致整个数据库hang主),如果内存很大,则会导致阻塞时间加长(5.6版本改进只扫描flush list,则会大大降低影响),相关的bug列表可以参考:
http://bugs.mysql.com/bug.php?id=64284
http://bugs.mysql.com/bug.php?id=51325
该如何解决此问题?其实有三种办法,第一就是在将用户的实例内存降级,减小DROP期间的影响;第二就是将实例的版本升级到5.6版本;第三就是调整应用中的业务,优化Drop table的业务。最终采取了最简单的办法,就是把实例的内存降低回原来的规格后,应用恢复正常。
这是今年双11比较普遍的一个问题,用户升级完规格后性能反而出现下降,所以如果你的应用中如果有大量的drop table,同时数据库的版本是MySQL 5.5,则建议升级到5.6版本(注意5.6版本开启了GTID,应用程序中不要有create temporary table的操作)。