笔者负责的项目最初是由外包团队开发完成,验收阶段没有做过性能方面测试,项目中较复杂的业务性能表现不太理想。
我们技术团队对线上的接口加了监控,监测各个关键接口的健康状况,主要包含接口响应时长。从中发现短视频模块中一个视频排行榜的接口性能非常糟糕,前端经常出现超时(前端设置的超时时间是10s)。针对此问题,笔者梳理业务逻辑,着手解决此接口的性能问题。
- 定位问题
本项目是部署在阿里云服务器,从mysql实例的慢查询日志中发现多条查询耗时在10s左右的sql语句,对应项目中的sql片段如下
SELECT
ID,
act_works.MAG_ID,
act_works.user_name,
act_works. DESCRIBE,
act_works.user_icon,
`LIKES`,
<
IF test = "commentMode == 'true'" > (
SELECT
count(1)
FROM
act_comment
WHERE
WORKS_ID = act_works.ID
AND act_comment. STATUS = 1
) COMMENTS,
</
IF > <
IF test = "commentMode == 'false'" > (
SELECT
count(1)
FROM
act_comment
WHERE
WORKS_ID = act_works.ID
AND act_comment. STATUS != 2
AND act_comment. STATUS != 3
) COMMENTS,
</
IF > RESOURCE_ID,
(
SELECT
ifnull(sum(act_works_like. LIKE), 0)
FROM
act_works_like
WHERE
act_works_like.WORKS_ID = act_works.ID
AND act_works_like. ENABLE = 1 <
IF test = "starTime != null and endTime != null" >
AND act_works_like.CREATE_TIME >= #{starTime,jdbcType=VARCHAR}
AND #{endTime,jdbcType=VARCHAR} >= act_works_like.CREATE_TIME
</
IF >
) likesCount,
RESOURCE_URL
FROM
act_works
WHERE
act_works. STATUS = 1
AND act_works.ACT_ID = #{actId,jdbcType=VARCHAR}
<
IF test = "starTime != null and endTime != null" >
AND act_works.CHECK_TIME >= #{starTime,jdbcType=VARCHAR}
AND #{endTime,jdbcType=VARCHAR} >= act_works.CHECK_TIME
</
IF >
ORDER BY
likesCount DESC,
act_works.CHECK_TIME DESC
- 分析问题
分析此sql片段,此sql为查询语句。初步分析此sql存在如下问题:
1、涉及三张表act_works、act_works_like、act_comment的关联查询,三张表的数据量都比较大;
2、每次调用接口都查询数据库,增加了数据库的压力。 - 制定方案
1、把act_works、act_works_like、act_comment三张表的关联查询拆分开来,把数据库中的计算工作转移到服务器内存中计算;
2、使用缓存来分担数据库查询压力;
3、视频排行榜相当于全局数据,可以放在缓存中,并通过定时任务来定期更新。 - 具体实施
加载最近100个挑战活动获得挑战活动列表(加载热点数据);
循环遍历挑战活动列表,根据时间条件查询此活动的周榜、月榜和总榜的排行榜信息,并把查询结果存放到redis缓存;
每查一个活动sleep 500毫秒,避免数据库资源长时间占用。