作者:钱颖(腾讯课堂研发团队)
疫情停倮以来,腾讯课堂助力全国数百万老师和数千万学生在线教学、听课。已有3000多个线下教育机构申请入驻腾讯课堂。这背后,离不开腾讯课堂可支持百万人同时在线上课、网络延时低至百毫秒级、1080P直播高清视频、秒级扩容服务海量用户等优势。
腾讯课堂业务(包括核心业务、非核心业务)大量使用了云数据库,如果DB性能波动,对现网业务会存在很大影响。比如核心业务——用户进入直播间行为,当DB存在波动时,会导致现网大批量用户无法进入直播间学习,影响巨大。
疫情突发,还没等做好准备,就需要迎接上千万的流量考验。腾讯课堂的研发、运维和云数据库团队在业务发展过程中,持续优化DB的性能瓶颈问题。
简要总结下,主要的优化方法有以下九点:
1)扩容RO组
2)拆分主库
3)数据库压测
4)慢查询优化
5)连接数优化
6)主从同步延迟优化
7)高负载优化
8)监控与告警
9)借助腾讯云数据库智能管家进行主动巡检
以下是腾讯课堂对数据库的详细案例和方法总结。
1 扩容主库DB的RO组,紧急应对流量考验
RO组是指只读库,一般可以通过设置主从数据库实现读写分离,主数据库负责写操作,从数据库负责读操作,根据压力情况,从数据库可以部署多个提高“读“的速度,借此来提高系统总体的性能。
课堂主库DB的RO组从1个RO组增加到多个RO组。每个RO组分别提供给不同的业务服务模块读。
服务模块中,为了服务能够快速响应用户的需求,在业务进程启动时,将DB数据全量加载到内存。业务进程服务运行过程中,将DB数据增量更新到内存的服务。
疫情期间服务扩容,读取主库的主进程服务已经部署了数千台机器,这么多机器来拉取DB的数据,对DB来说无疑是压力巨大。因此,增加的RO组有一些是专门用来进行主进程服务数据的加载。另外一些RO组则专门用来处理现网业务读请求。避免主进程服务数据的加载影响到现网的正常请求响应。
1.1 增加RO组遇到的问题
一个DB的主数据库,并不是可以无限挂载多个RO组的。因为RO组增加过多,会增加主库的负载。多增加一个RO实例,主库需要多发一份BINLOG到备机上,等同于多了一个发送BINLOG的线程,对主库的CPU/内存/网卡/IO等都会有影响。
虽然从上面贴出的架构图中看,我们的RO组只增加至多个RO组,但是每个RO组下面会挂2-3个实例,实际上主数据库挂载了众多从机数据库。如果再增加新的RO组,对于主数据库来说负载可能会比较大,需要综合考虑主库的各种负载因素。
1、案例1
【问题发现】
部分RO组出现了主从同步延迟告警。
【处理过程】
(1)查看RO库负载飙高,网卡出现等待,存在全表扫描的SQL。
(2)该RO组是业务用来全量加载DB数据到服务缓存中,已被独立出来,不影响在线业务。
(3)去掉该RO组的主从同步告警。
1.2 拆分主库DB,分离DB压力
课堂的主库DB是历史包袱之一,主库DB里面包含了很多不同功能的DB表(资料侧、数据搜索等),导致各种业务请求都会读写主库DB。不仅不同功能业务之间会相互影响,同时DB负载也会很大。
在这次疫情期间,课堂的不同功能业务分批次从主库拆分出来,避免业务之间相互影响,同时保证主库的负载正常,核心业务功能稳定提供服务。
2 DB在业务高峰期遭遇的各种问题
经过上面的主库DB紧急扩容RO组,课堂DB抗住了日益增长的流量突增考,包括主库DB慢查询猛增,连接数猛增,主从同步延迟等。
2.1 慢查询突增,怎么定位和防治?
慢查询是DB运营过程中遇到最多的问题,同时慢查询也是最难定位原因的问题。
因为慢查询出现时候的表现都是DB突然慢查询增多,查询当时的慢查询日志,会有很多SQL都变成慢查询,然而这些SQL可能是被某个慢查询带起来的(正常情况下,普通执行SQL是不会变成慢查询的),也有可能是因为DB负载变高,出现很多慢查询,需要具体问题具体分析。
2.1.1 课堂每日压测中发现的DB慢查询问题
【问题发现】
课堂每天在业务低峰斯都会对业务进行全方位的压测,在压测时可以压出DB的性能问题,譬如慢查询飙高。
【处理过程】
(1)找出TOP的慢查询SQL。
(2)业务分析慢查询,进行优化。
【优化方法】
(1)【拆分DB】相对独立的业务DB从主DB中拆分出来,独立部署。
(2)【切换RO组】非核心流程Select语句,对主从同步延迟容忍较高,切换RO组,不影响主机负载。注意Update/Insert/Delete等修改语句不能切换到RO组,会引起主备不一致,如果RO组开了只读,则修改SQL在RO组操作会失败。
(3)【添加索引】可以进行添加索引优化的SQL,添加索引优化,注意添加索引不要在高峰期进行,避免对主机DB负载和主从同步有影响,需要在低峰期进行。
SQL优化的案例可详见各种优化方法。
课堂在疫情期间,专门跟进主库DB的慢查询SQL,推进开发处理解决。
2.1.2 一些课堂DB运营过程遇到的慢查询案例
1、案例1
【问题发现】
主机慢查询飙高
【处理过程】
(1)查找慢查询SQL,发现全是UPDATE/INSERT。
(2)KILL慢查询,但是业务还没有停掉,导致慢SQL一被KILL,又全部重新连上。
(3)业务停掉了UPDATE/INSERT大并发执行的服务后,DB慢查询和负载逐渐恢复。
【原因分析】
(1)找出慢查询TOP SQL, 以及发生问题时间段(包括出现告警之前)的慢查询日志。发现有一批更新在线课堂和和统计在线课堂的SQL请求。
(2)业务层面是大批量心跳DB操作失败,导致老师同一个时间点全部退房,然后老师狂刷上课请求,上课请求的DB操作很耗时,结果引发慢SQL雪崩。
核心问题:心跳SQL为什么在DB正常的时候,出现失败?
(3)分析慢查询日志发现在慢查询告警之前有其他慢SQL,执行10s ,扫描几百万行。当时的上课业务请求有出现超时。
(4)SQL的事务间有锁冲突。
【解决方案】
(1) 先暂时停掉非核心慢SQL。
(2) 分析一天内超过10s的慢查询,和具体业务模块负责人确认是否可以强Kill,确认OK后,设置自动Kill。使用select sleep(12); 验证是否可以自动Kill。
(3) 准备按正则Kill SQL的工具,类似上面的3个雪崩SQL,可以输入SQL正则停止。
2、案例2
【问题发现】
出现慢查询告警
【处理过程】
(1)查询慢查询日志,表现是:Select查询突增
(2)观察DB日志,发现根本原因是某条Update语句有死锁。统计发现Table_locks_immediate达到1K多。互相死锁的两个SQL都来自业务服务。
分析原因为:
· 功能:业务服务有定时任务,通过心跳刷新上下课时间和更新时间,来更新直播状态。
· 问题点:该模块下有几十台机器会同时执行定时器逻辑,会产生排它锁。
· 影响点:如果产生X锁,会导致Select也卡死,进而影响整个表和DB(影响比较严重,如果该表锁住,直播状态无法更新)
解决方案:
· 【服务拆分】将该服务拆分,有定时器的版本只处理对账逻辑,无定时器的版本处理现网功能
· 【分离部署】对定时器的版本和无定时器版本的服务,分别单独部署服务器。
3、案例3
【问题发现】
出现慢查询告警
【处理过程】
(1)查找慢SQL。
代码语言:javascript复制SELECT * FROM t_xx_info WHERE f_update_time>'2020-03-01 08:30:16' and f_trans_state=12 AND f_o_lock = 0 AND f_retry_nums < 5 ORDER BY f_update_time limit 100 for update
上一条语句的Select For Update 会加锁,容易造成锁等待。
【解决方案】
业务进行优化SQL处理。
· 【优先恢复】停掉服务。慢查询被Kill掉,但是服务请求存在,慢查询就会一直死灰复燃。
· 【SQL优化】优化for update语句,不进行加锁。慢SQL添加索引优化。SQL逻辑优化。f_update_time 范围目前为15day,可以缩短至3day。
· 【读写分离/拆分DB】业务能够容忍偶尔主从同步延迟的话,纯粹的Select语句改读备机,不影响主机负载。业务不能容忍主从同步延迟的话,可以单独从主库中切出,不影响其他业务。
2.1.3 DB慢查询处理和防治总结
【处理】
(1)【优先恢复】先快速KILL掉慢查询,如果是服务请求引起的慢查询,需要停掉服务,避免慢查询死灰复燃。
(2)【原因查找】定位引发慢查询的深层原因,需要具体问题具体分析。找出问题后,解决问题,避免下次出现同样事件。
【防治】
(1)【监控告警】DB慢查询实时监控告警。
(2)【自动Kill SQL】主库DB配置超10s的select查询SQL自动Kill, 因为DB出现严重慢查询的时候会出现雪崩现象,该策略主要是防止出现慢查询时,DB彻底被压垮。
(3)【拆分DB】相对独立的业务DB从主DB中拆分出来,独立部署。
(4)【切换RO组】非核心流程Select语句,对主从同步延迟容忍较高,切换RO组,不影响主机负载。注意Update/Insert/Delete等修改语句不能切换到RO组,会引起主备不一致,如果RO组开了只读,则修改SQL在RO组操作会失败。
(5)【添加索引】可以进行添加索引优化的SQL,添加索引优化,注意添加索引不要在高峰期进行,避免对主机DB负载和主从同步有影响,需要在低峰期进行。
(6)【业务优化】业务根据自身的情况,进行优化SQL
2.2 连接数猛增,怎么处理和防治?
DB连接数猛增的原因也是多种多样,例如服务在扩容时,一次性扩容很多机器,同时连接DB,就会导致连接数瞬间爆掉,又例如业务服务存在bug,DB连接泄露,也会导致DB连接数猛增。
幸运的是,连接数的问题要比慢查询容易处理很多。
2.2.1 一些课堂DB运营过程遇到的连接数案例
1、案例1
【问题发现】
主机连接数告警 & 大部分RO组主从同步异常
【处理过程】
(1)重启主库,恢复连接数和RO组的主从同步。
(2)原因:业务扩容了几百个服务svr,同时启动导致DB连接数爆掉。每个服务svr占用30个连接,主库DB的连接数最大值是8000,超过阈值。
(3)业务分批启动,主库连接数仍会爆掉。业务切RO组解决问题。
2、案例2
【问题发现】
主机DB连接数增多
【处理过程】
(1)SHOW PROCESSLIST,根据来源IP来查看DB的连接数。
代码语言:javascript复制select client_ip, count(client_ip) as client_num from (select substring_index(host,':' ,1) as client_ip from processlist ) asconnect_info group by client_ip order by client_num desc limit 5
(2)登录TOP来源的机器上,netstat查看是哪个服务的连接数较多。使用root账户执行以下命令,可以看到是哪个服务连接DB较多。
代码语言:javascript复制netstat -nalp | grep 120:3319
(3)业务评估该服务可重启,先手动重启服务恢复DB连接数。重启之后,DB连接数下降。但过了一会儿,DB连接数又上涨了,发现是服务代码存在DB连接泄露的问题。
(4)解决方法:
· 临时方案:安装服务的定时重启脚本,每隔20min重启一次,当连接池泄露的时候重启,重启后DB连接恢复。
· 根本方案:解决服务代码的连接泄露问题。
2.2.2 DB连接数处理和防治总结
【处理】
(1)【SHOW PROCESSLIST】SHOW PROCESSLIST查看连接数最多的业务来源,停掉服务或者其他处理,避免连接数增加过多
(2)【DBA处理】当DB连接数爆掉的时候,需要运维介入,重启恢复。 【防治】
(1)【监控告警】配置DB连接数的告警,有异常及时发现并处理。
(2)【扩容注意】业务扩容非线上紧急,低峰期进行。线上紧急扩容,需要全面考虑存储层、服务调用上下游链路、机器性能等方面的影响。
(3)【连接池】服务代码需要考虑DB连接的数量,尽量使用DB连接池,不要使用短连接。使用短连接的话,服务请求量突增,会导致DB连接数猛增。同时,使用连接池也需要根据DB连接数的配置大小,合理设置服务连接池的大小。
(4)【切换RO组】如果主机的连接数已经无法满足业务需求,可以考虑把Select等只读的请求切换到RO组。
连接上DB之后,可以使用下面的命令查询DB连接数的配置大小。
代码语言:javascript复制show variables like 'max_connections';
2.3 主从同步延迟,怎么处理和防治?
主从同步延迟疫情期间,并没有成为DB运营中最头疼的问题,出现的主从同步问题基本对线上业务无影响。
2.3.1 一些课堂DB运营过程遇到的主从同步延迟案例
1、案例1
【问题发现】
主从同步告警
【处理过程】
(1)DBA帮忙查找SQL
代码语言:javascript复制DELETE FROM t_xxx_day WHERE ETL_STAMP='2020726736_20200312';
DELETE FROM t_xxx_day WHERE ETL_STAMP='2020726736_20200312';
(2)业务有定时TDW任务,每天早晨执行,因为不影响线上业务同时业务需要TDW任务,未进一步处理。
2、案例2
【问题发现】
DB的备机主从同步延迟
【原因定位】
(1)备机上日常的备份会产生全局读锁。备份快结束的时候会获取一次锁。这个锁正常会很快结束的,但如果这个DB上有很多慢查询,就会导致锁一直等待。所以延迟就会越来越大。
(2)数据库里面有很多MyISAM表,每次备份都需要全实例锁定备份,备份期间从库不能更新,导致主从会有延迟,这也是个比较大的风险点。
(3)备份资源是有限的,实例比较多,实例发起备份之后需要排队,而且每次分配的备份机器性能有差异,性能差的备份机器可能会出现主从延迟。
【备机主从同步预防方案】
(1)【使用RO组】将数据实时性要求较高的查询放在RO组,而非DB的备机。 大查询放在冷备机不太合适。会一直阻塞备份获取锁。每天的备份失败风险也比较高。
(2)【MyISAM引擎->INNODB引擎】数据库表的存储引擎考虑INNODB。备机上MyISAM表比较多,备份时持有锁的时间长,也会导致主从同步延迟一直较高。INNODB表的查询效率没有MyISAM高,但差别不会很大。
业务从MyISAM->INNODB时需要注意,大表操作需要在低峰期进行。
1)修改期间主库会锁表。
2)修改期间主从会有延迟,包括RO库。
2.3.2 主从延迟处理和防治总结
【处理】
(1)【DBA处理】需要DBA介入,帮忙恢复主从同步延迟。
(2)【业务处理】主从同步延迟来源自业务的SQL操作,停掉服务或者其他处理,避免主从同步延迟产生影响。
【防治】
(1)【监控告警】RO组主从同步监控告警
(2)【SQL操作谨慎】主库避免在业务高峰期进行大量数据的Delete/Update/Insert/创建索引/添加字段等操作。课堂主库使用的MySQL版本数据库,修改操作在主库是并发执行,然而在RO组是串行执行,这会使得RO组追上主库花费的时间更多。
(2)【连接数】主库的连接数控制在合理范围。如果主库连接数爆了,RO组和主库通信的BINLOG进程也断开连接,将会出现主从延迟问题。
(3)【使用RO组】对于数据延迟非常敏感的业务,建议放在主库进行读取。对于数据延迟不是很敏感的业务,可以放在RO组进行读取。不建议使用DB的备机来读取。
2.4 DB CPU居高不下,怎么办?
2.4.1 现网案例
【问题发现】 DB CPU飙高
【处理过程】
(1)慢查询日志查找TOP SQL和来源IP。
代码语言:javascript复制SELECT t_x FROM t_xxx WHERE (t_x = '' OR t_x IS NULL) ORDER BY t_updatetime DESC LIMIT 10
这个SQL较多,且未命中索引。 手动执行速度不慢。
(2)猜测: DB存在缓存: 业务将这些Limit 10的数据从物理盘load进到内存了,所以我们手动执行其实读的是内存,所以快。 是否因为表太大,导致Select变慢,需要分库或者分表?
但是DB表的数据量每天增长正常,
实际原因为有一条手动执行的SQL,一直执行了9w秒左右,导致cpu一直很高。
2.4.2 DB CPU处理和防治总结
【处理】
(1)【SHOW PROCESSLIST】如果DB CPU一直居高不下,大概率影响的SQL还在,可以SHOW PROCESSLIST查找到,并Kill
(2)【慢查询日志】如果CPU飙高了一下就恢复,可以通过慢查询日志看CPU飙高的时候有没有大量慢查询
【防治】
(1)【监控告警】DB CPU告警监控,及时发现问题
(2)【自动Kill SQL】Select SQL超过10s,自动Kill
3 DB运营总结
DB运营中业务可进行的处理有以下几种:
下面的思维导图总结了上面提到的DB慢查询、连接数、主从同步延迟、CPU问题处理和防治方法。
4 DB监控告警
4.1 DB监控指标
课堂在疫情期间主要监控了DB的连接数、慢查询数、主从同步时间、CPU负载,如果上述指标存在异常,则会告警提示。选择监控上述指标的原因,是DB出现问题时,这些指标能够及时感知。
4.2 DB监控方式
DB监控方式主要来自下面几种:
1. Grafana监控
2. 云数据库监控
3. 服务器监控
4. 定时脚本拨测
4.2.1 Grafana监控
将DB的连接数、慢查询数、主从同步时间等指标,通过Grafana展示。
4.2.2 腾讯云数据库监控
使用腾讯云上的数据库监控页面和视图进行展示。
4.2.3 服务器监控
业务逻辑的服务器CPU等指标监控,通过云监控获取,并推送到企业微信群。
4.2.4 定时脚本拨测
用于连接数、慢查询数、RO组的主从同步的拨测监控。此外,还帮助完成了自动Kill超时SQL的存储过程监控。
4.3 告警方式
4.3.1 企业微信机器人
Grafana监控以及数据库监控和定时脚本拨测等,都采用了企业微信机器人的告警方式。
4.3.2 微信
5 腾讯云数据库智能管家-DBbrain
业务通过智能管家对所有数据库进行巡检,监控和问题诊断。
业务可以在大盘里巡检得分较差的数据库实例,下钻到该实例,了解评分差的因素,譬如CPU使用率高,或者慢查询因素。可以继续分析慢查询的详细日志,提取出问题SQL,对其进行SQL优化。
上图,智能管家提示的慢查询事件,及详细说明,包括实例、发生时间、时长、与CPU负载对照、SQL语句等。
上图,智能管家给出优化建议