icon
Update time
Jul 21, 2022 02:37 PM
Internal status
password
上午客服反应管理后台在售管理和求购管理打开缓慢,我跟踪了下原因 我猜想原因如下:
- 交易相关的,走了交易服务的接口,接口比较缓慢
- 没加limit或者查询条件比较复杂没走索引
查看代码,发现是php代码直接查询数据库的,排除第一个原因,接下来往直接查数据的方向查,可能原因有
交易数据负载比较高,查询响应慢
管理后台使用了外网地址连接数据库
- 查看交易数据的近期慢SQL,是否有管理后台请求的慢SQL,发现端倪
![notion image](https://www.notion.so/image/https%3A%2F%2Ffile.notion.so%2Ff%2Ff%2F94c79f86-3a7f-4d2d-ac0e-eaccf7961f02%2F6bbe06a5-8152-4bfe-94b6-0db42222a434%2Fpage1.png%3Fid%3Dcdaff933-057f-4e68-a12a-d0cf2a4b070d%26table%3Dblock%26spaceId%3D94c79f86-3a7f-4d2d-ac0e-eaccf7961f02%26expirationTimestamp%3D1722009600000%26signature%3DrV-sj_yIGd08x9XTJTLQShcCdknQwYpnntZ0E1kGxwc?table=block&id=cdaff933-057f-4e68-a12a-d0cf2a4b070d&cache=v2)
本地开启浏览器显示sql调试信息
![notion image](https://www.notion.so/image/https%3A%2F%2Ffile.notion.so%2Ff%2Ff%2F94c79f86-3a7f-4d2d-ac0e-eaccf7961f02%2F8a9ceeba-aec2-4108-9f52-a0330f75e010%2Fpage2.png%3Fid%3D7f535c85-be70-4653-863f-6ae6c7630ffa%26table%3Dblock%26spaceId%3D94c79f86-3a7f-4d2d-ac0e-eaccf7961f02%26expirationTimestamp%3D1722009600000%26signature%3Dg0YTrj9ewn6FMCqJb3USsHadV3G4AkPwYDrWznW_Sc4?table=block&id=7f535c85-be70-4653-863f-6ae6c7630ffa&cache=v2)
发现会select count(*) from trade_product_sell,这个应该是为了分页,但是线上表数据已经5600万了,这个sql得执行9秒,能不慢吗 管理后台的分页插件都会执行这个count操作
public function getTotalItemCount($refresh=false) { if($this->_totalItemCount===null || $refresh) $this->_totalItemCount=$this->calculateTotalItemCount(); return $this->_totalItemCount; }
找到问题后,怎么处理,现状如下
- 此处代码为yii1.x的分页逻辑,查询总条数,然后计算分页,此代码在管理后台使用的非常多
- 此分页逻辑在针对小表查询的时候一点问题也没有,且使用比较方便
- 原则是不能改框架,且一定要获取到总条数,前端页面才能正常显示分页
解决方案
- 既然总条数的获取不可避免,那就优化count(*)的性能
根治的方法:针对大表进行归档处理,这个是交易系统的表,处理起来需要时间,可能要排到下季度了治标的方法: 赋值一个假的总条数,这个的问题是体验不好,有的表只有几行,却显示一堆分页- 治标的方法: 将查询总数缓存起来,虽然第一次会慢,但是之后都挺快的
- 代码实现
- 不能修改框架代码,防止之后升级框架版本的时候覆盖了
- 可以选择修改基础的模型类,增加一个方法,进行总数缓存,且可以按需使用,针对大表及对总数精确度不敏感的场景
BasicAcitveRecord类中增加方法
/**
* 缓存一下分页组件使用的数据总条数
* 适用场景:
* 对count不要求精确的,表比较大的,select count(*) 比较慢的那种,如果不缓存,每次都得查count(*)比较慢
* 不适用场景:
* 对总数要求精确的
* 实现原理,使用表名+查询条件的md5作为key,缓存此条件下的查询总条数
* @param CActiveDataProvider $dataProvider :分页使用的dataProvider
*/
public function cacheTotalCount(CActiveDataProvider $dataProvider)
{
/**
* @var $redis ARedisCache
*/
$redis = Yii::app()->cache;
$md5 = md5(json_encode($dataProvider->getCountCriteria()) . $dataProvider->model->tableName());
$count = $redis->get(RedisKeyService::getCachedTabelCountKey($md5));
if ($count != null) {
$dataProvider->setTotalItemCount($count);
} else {
$redis->set(RedisKeyService::getCachedTabelCountKey($md5), $dataProvider->getTotalItemCount(), 3600);
}
}
- 使用方式
$model = new TradeTransfer(); $model->setAttributes([ 'id' => $id, 'receiver_steam_id' => $receiverSteamId, 'sender_steam_id' => $senderSteamId, 'status' => $status, ], false); $dataProvider = $model->search($orderAssetId); $dataProvider->pagination->setPageSize(10); //原有的代码增加下面这行 $model->cacheTotalCount($dataProvider); $this->render('index', ['dataProvider' => $dataProvider]);
- 优化成果,接口响应时间从10秒多到1秒多
![notion image](https://www.notion.so/image/https%3A%2F%2Ffile.notion.so%2Ff%2Ff%2F94c79f86-3a7f-4d2d-ac0e-eaccf7961f02%2F11753be2-f6d3-4d37-b094-d64d2dc54cf6%2Fpage3.png%3Fid%3D07abeea6-0bf1-4903-99af-079ba0fb9f4f%26table%3Dblock%26spaceId%3D94c79f86-3a7f-4d2d-ac0e-eaccf7961f02%26expirationTimestamp%3D1722009600000%26signature%3Da7PudfPfv_gMWZl2lXNgMf7IkN7KPpbO3w1vx4ODaME?table=block&id=07abeea6-0bf1-4903-99af-079ba0fb9f4f&cache=v2)
![notion image](https://www.notion.so/image/https%3A%2F%2Ffile.notion.so%2Ff%2Ff%2F94c79f86-3a7f-4d2d-ac0e-eaccf7961f02%2F2123fa65-945f-486d-897e-0af6b5003e43%2Fpage4.png%3Fid%3D6c6dcdb2-d68b-42bf-be0c-3029344505ec%26table%3Dblock%26spaceId%3D94c79f86-3a7f-4d2d-ac0e-eaccf7961f02%26expirationTimestamp%3D1722009600000%26signature%3DkV1j6NnrSeGDHPgrrNdkUt6karA1_O3urtec9nJK4lo?table=block&id=6c6dcdb2-d68b-42bf-be0c-3029344505ec&cache=v2)