社区所有版块导航
Python
python开源   Django   Python   DjangoApp   pycharm  
DATA
docker   Elasticsearch  
aigc
aigc   chatgpt  
WEB开发
linux   MongoDB   Redis   DATABASE   NGINX   其他Web框架   web工具   zookeeper   tornado   NoSql   Bootstrap   js   peewee   Git   bottle   IE   MQ   Jquery  
机器学习
机器学习算法  
Python88.com
反馈   公告   社区推广  
产品
短视频  
印度
印度  
Py学习  »  DATABASE

MySQL上云后引发的雪崩

DBAplus社群 • 8 年前 • 879 次点击  


作者介绍

甘兵高级运维工程师,6年运维工作经验。曾就职于国家互联网应急中心、天音控股等企业。拥有丰富的系统运维经验、大型网络架构设计经验。热衷于开源技术的研究,关注Docker、DevOps等技术方向

注:本文系原创投稿,未经 DBAplus社群及作者 允许,不得擅自转载。


一、事件起源


整个事件的起源还要从笔者最近入职了一家区块链金融公司说起,公司业务发展比较迅猛,突破百万用户也是近在眼前。整个×××都在阿里云上运行,每天都能看到用户的不断增长,既兴奋又担忧,为什么这么说呢?


由于笔者入职的时候这里的业务就已经上线了,×××接过来之后,快速了解了所有的应用服务都是在Docker Swarm跑起来的,也包括MySQL数据库,以至于笔者有了迁库的想法:按照这种用户量发展下去,MySQL在容器中运行用不了多久肯定会撑不住。


笔者隐隐担忧起来,毕竟不想每天提心吊胆地做运维,所以立马重新规划了新的方案和大家一起探讨。最终,总监和相关技术负责人都敲定用RDS作为数据库新的方案,正所谓“天下武功,唯快不破”,于是我们立马动身干起来。


二、迁移计划


1
原架构图


分析架构图


1、从入口层(CDN)→到安全层(WAF)→ 最后到达应用层 (ECS集群);
2、Docker Swarm打通了ECS集群中的每台服务器,在每台ECS宿主机安装Docker Engine并部署了公司需要的应用服务和数据库(Nginx、PHP、Redis、MySQL等);
3、MySQL容器通过宿主机的本地(目录)挂载到容器中实现数据持久化;
4、业务项目以PHP为主,PHP也是运行在容器中,通过PHP指定的配置文件连接到MySQL容器中。


其中一个库的docker-compose yaml文件,笔者就随便展示一下其中一个库的YAML文件,比较简单:


version: "3"services:

  ussbao:

    # replace username/repo:tag with your name and image details

    image: 隐藏此镜像信息

    deploy:

      replicas: 1

      restart_policy:

        condition: on-failure

    environment:

      MYSQL_ROOT_PASSWORD: 隐藏此信息

    volumes:

      - "/data//mysql/db1/:/var/lib/mysql/"

      - "/etc/localtime:/etc/localtime"

      - "/etc/timezone:/etc/timezone"networks:

  default:

    external:

      name: 隐藏此信息


从上面的信息可以看出来,每个库只运行了一个MySQL容器,并没有主从或读写分离的方案,而且也没有对数据库进行任何优化,数据库这样跑下去让笔者很担忧,正常来说,都会把数据库独立部署运行。


2
调整后的架构图



从上图可以看出,笔者只是把MySQL独立出来了,开通RDS实例来跑数据库,当然还开通了其它的一些服务(比如OSS、云Redis等),这些不是本文的重点,就没有画出来。Nginx和PHP服务还是在Docker Swarm中运行。本文只是对迁移后出了问题的库进行分享。


下面来看看迁移的方案:


3
迁移流程方案


开通RDS实例→备份SQL→导入到RDS→修改数据库配置文件→测试验证


1、根据业务量规划开通RDS实例,创建数据库和用户;
2、提前做好RDS白名单,添加允许访问RDS的IP地址;
3、MySQL Dump备份Docker中的MySQL
4、把备份好的.sql文件导入到RDS中;
5、修改PHP项目的数据库配置文件;
6、清空PHP项目的缓存文件或目录;
7、测试验证;
8、RDS定时备份。


具体迁移细节就不展示了,笔者是在夜深人静的时候进行迁移操作的,确定大半夜没人访问我们的APP和网站了才开始干的。


我们的业务情况还有点像股市,是晚上12点不许操作和交易,第二天早上9点开盘,9点钟是并发的高峰期,就像朝阳大悦城上午开门一样,大批的顾客同时并发过来。


所以那天晚上在12点15分准时开干,按计划和提前准备的配置、命令、脚本进行操作。非常顺利地把Docker中运行的MySQL迁移到RDS上,好几个库的迁移不到半个小时就结束了,并且把网站和APP的流程都跑了一遍,也都是妥妥的,最终把提前准备好的备份脚本放在crontab中定时执行,可以来看下脚本内容:


#!/bin/bash#数据库IP

dbserver='*******'#数据库用户名

dbuser='ganbing'#数据库密码

dbpasswd='************'#备份数据库,多个库用空格隔开

dbname='db1 db2 db3'#备份时间

backtime=`date +%Y%m%d%H%M`

out_time=`date +%Y%m%d%H%M%S`#备份输出路径

backpath='/data/backup/mysql/'

logpath=''/data/backup/logs/'

echo "################## ${backtime} #############################" 

echo "开始备份" #日志记录头部

echo "" >> ${logpath}/${dbname}_back.log

echo "-------------------------------------------------" >> ${logpath}/${dbname}_back.log

echo "备份时间为${backtime},备份数据库 ${dbname} 开始" >> ${logpath}/${dbname}_back.log

#正式备份数据库

for DB in $dbname; do

  source=`/usr/bin/mysqldump  -h ${dbserver} -u ${dbuser} -p${dbpasswd} ${DB} > ${backpath}/${DB}-${out_time}.sql` 2>> ${backpath}/mysqlback.log;

  #备份成功以下操作

  if [ "$?" == 0 ];then

    cd $backpath

    #为节约硬盘空间,将数据库压缩

    tar zcf ${DB}-${backtime}.tar.gz ${DB}-${backtime}.sql > /dev/null

    #删除原始文件,只留压缩后文件

    rm -f ${DB}-${backtime}.sql

    #删除15天前备份,也就是只保存15天内的备份

    find $backpath -name "*.tar.gz" -type f -mtime +15 -exec rm -rf {} \; > /dev/null 2>&1

    echo "数据库 ${dbname} 备份成功!!" >> ${logpath}/${dbname}_back.log

  else

  #备份失败则进行以下操作

  echo "数据库 ${dbname} 备份失败!!" >> ${logpath}/${dbname}_back.log

  fi

done

echo "完成备份"

echo "################## ${backtime} #############################"


到了1点钟,确定没问题后发通知到群里,发微信给领导表示已迁移完成,进行很顺利,然后笔者打车回家,睡觉。


三、雪崩来临


其实这一夜笔者睡得也不踏实,到了早上8点半就醒了,因为我们9点开盘,会有大量的客户涌进,每天开始产生新的交易(买入和卖出),给大家看下截图:



果不其然,9点过后,笔者打开APP,一切正常,但点击切换几个界面后,就发现其中一个功能的请求超时了,一直在转,然后紧接着其它功能也超时了。


完了,出问题了。我赶紧开笔记本查问题,过了一会儿群里就开始沸腾了(反映好多客户打开APP都显示请求超时了),我的电话也立马响了,技术总监打来的,问我怎么回事,我说正在开笔记本排查。


这个时候,我要说明一下:运维人员此时需要冷静并且安静地处理问题,公司领导千万别催得太急,以免打乱处理人的思路。我们总监临场处理能力做得真是非常到位,马上跟我说不用担心上面压力,有他扛着,叫我只管排查和解决问题。


四、紧急处理


1
排查问题


笔记本打开后,首先想到的就是RDS数据库出了问题,登录阿里云,进入RDS中的DMS数据管理控制台,一进去就傻眼了——CPU爆了!这么多连接数,如下图:



进入会话去看看,发现会话“炸锅了”,几百页的select都挤在ub_user_calculate这个表中,这个表是数据量相对大一些,目前这张表有200多万条数据:



自然反应就是去查看此表的结构,发现此表没有索引,被惊讶到了,竟然没有索引?!这...... 


然后笔者返回源数据库查看这张表,也发现没有索引,由此可以确定我导过来的这张表就是没有创建索引,如下图:



分析:当数据库中出现访问表的SQL没创建索引导致全表扫描,如果表的数据量很大,扫描大量的数据,执行效率过慢,占用数据库连接,连接数堆积很快达到数据库的最大连接数设置,新的应用请求将会被拒绝,导致故障发生。


2
解决问题


赶紧把此事反应给开发负责人,表明问题根源找到了,会话锁死了,由其中的一张表没有索引而导致的,并询问需要给哪几个字段加索引,接着操作增加索引:



点击保存后,发现创建索引的SQL一直卡死着,如下图所示:



突然想起来还有一堆会话在那里,先Kill掉所有会话吧,不然索引肯定创建不了。然后又发现会话根本杀不完,如下图:



怎么办呢?会话杀不完……


没办法,先把访问入口切断吧,反正现在用户访问也超时,就果断先把域名停了,访问入口给切断了,然后再增加索引。


入口断了,索引也加上了,但发现CPU还下不去,如下图:



为了快速让CPU降下去,重启这个实例吧:



实例重启完后,CPU下去了,会话也下去了:



开启入口层的域名访问吧,再次观察现在的会话和CPU等状况,如下图:




这就对了,会话也正常了,随后通知领导业务恢复。


这是笔者后期补的一张图:再来看一下服务器CPU情况(迁移MySQL后的情况),明显逐渐好转。



五、索引使用策略及优化


1
创建索引


  • 在经常查询而不经常增删改操作的字段加索引。

  • order by与group by后应直接使用字段,而且字段应该是索引字段。

  • 一个表上的索引不应该超过6个。

  • 索引字段的长度固定,且长度较短。

  • 索引字段重复不能过多,如果某个字段为主键,那么这个字段不用设为索引。

  • 在过滤性高的字段上加索引。


2
使用索引注意事项


  • 使用like关键字时,前置%会导致索引失效。

  • 使用null值会被自动从索引中排除,索引一般不会建立在有空值的列上。

  • 使用or关键字时,or左右字段如果存在一个没有索引,有索引字段也会失效。

  • 使用!=操作符时,将放弃使用索引。因为范围不确定,使用索引效率不高,会被引擎自动改为全表扫描。

  • 不要在索引字段进行运算。

  • 在使用复合索引时,最左前缀原则,查询时必须使用索引的第一个字段,否则索引失效;并且应尽量让字段顺序与索引顺序一致。

  • 避免隐式转换,定义的数据类型与传入的数据类型保持一致。


参考:

https://help.aliyun.com/document_detail/52274.html?spm=a2c4g.11174283.6.812.ZGPyBQ


六、总结


1、此次故障虽然是表没有索引造成的,但笔者是有责任的,没有挨个表检查一下表的结构;
2、通过此次故障也可以看出开发在设计表的时候真的要非常重视、注意细节;
3、之前在容器中运行的MySQL也时不时出现CPU瓶颈(比如CPU使用率偶尔会达到80%以上),笔者应该提前发现这些问题,彻底排查,找出问题根源所在,再进行迁库操作。



近期热文

方法论与技术栈双管齐下的运维可用性能力建设

中小型企业大数据体系建设的核心技术选型

从NoSQL到NewSQL,谈交易型分布式数据库建设要点

从架构特点到功能缺陷,重新认识分析型分布式数据库

新手也能看懂的监控报警系统架构设计



今天看啥 - 高品质阅读平台
本文地址:http://www.jintiankansha.me/t/zhD3TWkbBr
Python社区是高质量的Python/Django开发社区
本文地址:http://www.python88.com/topic/11682