Yii 1.x的管理后台分页导致的性能问题

Jimmy Lee

技术分享|Sep 10, 2021|Last edited: 2022-7-21|
icon
Update time
Jul 21, 2022 02:37 PM
Internal status
password
 
上午客服反应管理后台在售管理和求购管理打开缓慢,我跟踪了下原因 我猜想原因如下:
  • 交易相关的,走了交易服务的接口,接口比较缓慢
  • 没加limit或者查询条件比较复杂没走索引
查看代码,发现是php代码直接查询数据库的,排除第一个原因,接下来往直接查数据的方向查,可能原因有
  1. 交易数据负载比较高,查询响应慢
  1. 管理后台使用了外网地址连接数据库
  1. 查看交易数据的近期慢SQL,是否有管理后台请求的慢SQL,发现端倪
      notion image
本地开启浏览器显示sql调试信息
notion image
发现会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;   }
找到问题后,怎么处理,现状如下
  1. 此处代码为yii1.x的分页逻辑,查询总条数,然后计算分页,此代码在管理后台使用的非常多
  1. 此分页逻辑在针对小表查询的时候一点问题也没有,且使用比较方便
  1. 原则是不能改框架,且一定要获取到总条数,前端页面才能正常显示分页
解决方案
  1. 既然总条数的获取不可避免,那就优化count(*)的性能
      • 根治的方法:针对大表进行归档处理,这个是交易系统的表,处理起来需要时间,可能要排到下季度了
      • 治标的方法: 赋值一个假的总条数,这个的问题是体验不好,有的表只有几行,却显示一堆分页
      • 治标的方法: 将查询总数缓存起来,虽然第一次会慢,但是之后都挺快的
  1. 代码实现
    1. 不能修改框架代码,防止之后升级框架版本的时候覆盖了
    2. 可以选择修改基础的模型类,增加一个方法,进行总数缓存,且可以按需使用,针对大表及对总数精确度不敏感的场景
    3. 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); } }
    4. 使用方式
      1.        $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]);
  1. 优化成果,接口响应时间从10秒多到1秒多
    1. notion image
      notion image

开始订阅我的关于终生学习, 生产力以及知识管理的文章. 订阅后, 您将收到我的精选文章.

©2014-2024 Jimmy Lee. All rights reserved. 公众号: 技术管理方法论
Powered By My Lovely Children