折腾论坛运维

杀一个运维不用枪

接手了学校最大学生论坛的运维(听起来好屌然并卵),结果没想到是个天坑。

然而,说到底,还是我图样。

频繁502,数据库报错

论坛服务器是阿里云一台2核2G内存的ECS,安装LNMP集成环境,运行了十余年,一度是X大学子网络信息交流的首选平台,数据量可观。然而最近数年呈现颓势,访问量下滑,相对压力,应该也不是很大吧!?

从韩*处刚接手时,我试着ssh到服务器。

结果。

TMD。

连。

不。

上。

你大爷。

enter image description here

吓得我去登录了阿里云控制台,发现CPU爆炸。试着访问论坛,许久无响应,最终502。

(话说阿里云账号还绑定着几届以前的运维负责人,每次要验证手机号的时候都要发条短信过去问验证码orz)

试着从阿里云控制台的管理终端连接,休眠屏幕上显示OUT OF MEMORY,并且KILL了几个进程。为了尽快恢复访问便直接重启了整个lnmp。

~/lnmp restart

由于经验不足,一时也难以排查,看到论坛正常了便撒手不管了。

结果,论坛在接下来一段时间的运行中大错小错不断,不断有502和数据库错误的情况发生,几乎每天都要修复。

于是找到了甩锅的上一任韩师傅的这篇文章,了解了大概原因后,为了避免数据库总是出错把php的进程限制很保守的改到了30+(真尼玛保守),如愿以偿的告别了一天两次的database error。

然而,我毕竟还是图样。

论坛开始了地狱般的502。

enter image description here

你大爷。

enter image description here

最后经过谭师傅和周师傅多名师傅的指点,调节到了80+,报502的频率明显降低。

然而。后来报502的时候,问题就都比较大了。

mysqld占用奇高,数据库操作响应间歇奇慢

在之后的运行中发现,一天中的某些时段论坛经常响应时间非常长(并且最后也基本上是502)。top发现服务器CPU占用奇高,有一、两个php-fpm进程达到了20+占用,mysqld进程则经常破50%,使用top命令监控服务器,负载值高破天际——双核服务器三个时间段内负荷居然经常达到30+。

enter image description here

(杭州某机房发生爆炸事件,湖南某高校学生组织宣称对此事负责)

服务器上似乎并没有安装性能监控(有我也不知道在哪),为了更好的分析问题便安装了OneAPM 进行性能分析。

发现最长的WEB事务执行时间达到了126.6662s。

enter image description here

这尼玛说没问题我特么都不信了!

查看数据库分析,发现对于 forum_thread 表的select 操作普遍时间非常长

enter image description here

288.653秒!报警了好么!

enter image description here

在OneAPM中查看对应的SQL语句,发现是根据 fid displayorder 字段进行查询并且根据 displayorder 进行排序

SELECT * FROM forum_thread WHERE `fid`=? AND `displayorder` IN(?,?) ORDER BY displayorder DESC, lastpost DESC LIMIT ?, ?

对于一个几十w的表来说select 执行200s肯定不正常。那么首先分析下索引的建立。结合在Google的鼎力支持下找到的这篇文章 ,连接终端进入mysql命令行,查看表结构

发现displayorder 果然没有建立索引。

之后干脆打开了mysql的慢sql日志,结合日志对一个操作频繁的字段进行了索引的建立。

终于过上了几天清净日子。

挑了个良辰吉日(502的时候),在MySQL命令行中输入

show full processlist;

刷刷刷出现一大堆Query(共计127个),lock一堆,全是UPDATE……. 翻到最上果然有很多条Sorting Data的SELECT,正是这样的语句

SELECT * FROM forum_thread WHERE `fid` IN('334','268','26') AND `displayorder` IN('0','1') ORDER BY displayorder DESC, lastpost DESC LIMIT 40

作为SELECT,它们勇敢的站在最前方阻塞了100+条Query,值得敬佩。

EXPLAIN一下

+----+-------------+--------------+-------+------------------------------------+--------------+---------+------+-------+-----------------------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+--------------+-------+------------------------------------+--------------+---------+------+-------+-----------------------------+
| 1 | SIMPLE | forum_thread | range | displayorder,typeid,displayorder_2 | displayorder | 4 | NULL | 64709 | Using where; Using filesort |
+----+-------------+--------------+-------+------------------------------------+--------------+---------+------+-------+-----------------------------+

发现是 Using filesort 。这条SQL语句可以说是Discuz最常用SQL之一,难道那些攻城狮们都没给这几个字段建立索引?

在等待队列中的SQL执行完毕后,打开profile进行分析

set profiling=1;
SELECT * FROM forum_thread WHERE `fid` IN('334','268','26') AND `displayorder` IN('0','1') ORDER BY displayorder DESC, lastpost DESC LIMIT 40;
show profile for query 1;

发现确实是sorting占了大头

+--------------------+----------+
| Status | Duration |
+--------------------+----------+
| starting | 0.000035 |
| Opening tables | 0.000006 |
| System lock | 0.000002 |
| Table lock | 0.000003 |
| init | 0.000029 |
| optimizing | 0.000005 |
| statistics | 0.000077 |
| preparing | 0.000060 |
| executing | 0.000002 |
| Sorting result | 0.257200 |
| Sending data | 0.000413 |
| end | 0.000004 |
| query end | 0.000002 |
| freeing items | 0.000266 |
| logging slow query | 0.000002 |
| cleaning up | 0.000003 |
+--------------------+----------+

查看一下 forum_thread 表的索引

+--------------+------------+----------------+--------------+--------------+-----------+-------------+----------+--------+------+------------+---------+
| Table | Non_unique | Key_name | Seq_in_index | Column_name | Collation | Cardinality | Sub_part | Packed | Null | Index_type | Comment |
+--------------+------------+----------------+--------------+--------------+-----------+-------------+----------+--------+------+------------+---------+
| forum_thread | 1 | displayorder | 1 | fid | A | NULL | NULL | NULL | | BTREE | |
| forum_thread | 1 | displayorder | 2 | displayorder | A | NULL | NULL | NULL | | BTREE | |
| forum_thread | 1 | displayorder | 3 | lastpost | A | NULL | NULL | NULL | | BTREE

然而确实是对这几个字段建立了索引的。那么问题有可能是索引的顺序。

<尚未解决 不立flag>