icon
Update time
Jul 21, 2022 02:37 PM
Internal status
password
上午客服反应管理后台在售管理和求购管理打开缓慢,我跟踪了下原因 我猜想原因如下:
- 交易相关的,走了交易服务的接口,接口比较缓慢
- 没加limit或者查询条件比较复杂没走索引
查看代码,发现是php代码直接查询数据库的,排除第一个原因,接下来往直接查数据的方向查,可能原因有
交易数据负载比较高,查询响应慢
管理后台使用了外网地址连接数据库
- 查看交易数据的近期慢SQL,是否有管理后台请求的慢SQL,发现端倪
本地开启浏览器显示sql调试信息
发现会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秒多