请选择 进入手机版 | 继续访问电脑版
查看: 196|回复: 0

php性能优化之不要在for循环中操纵DB

[复制链接]

2066

主题

2066

帖子

6594

积分

管理员

Rank: 9Rank: 9Rank: 9

积分
6594
发表于 2022-10-9 01:01:31 | 显示全部楼层 |阅读模式
前言

如何提高步调运行速度,减轻服务器压力是服务端开发必须面对的一个问题。
简单且朴素的原则:不要在for循环中操纵DB,包罗关系型数据库和NoSql。
我们应该根据自己的业务场景,在for循环之前批量拿到数据,用尽量少的sql查询批量查到结果。 在for循环中进行数据的匹配组装。

场景说明


  • 业务在多个情景下需要获得用户的详细信息,有点可以通过查询用户表直接获取到,有的需要查询关联关系表获取到,有的只生存了关联的id,并没有单独创建关联关系表,需要单独写获取函数取值。
  • 既然多个场景下需要调用,那么封装成一个公共方法,让多个场景统一调用公共方法是基本的优化思路。
  • 上面提到了复杂的存取值关系,我们需要分析一下,哪些操纵是耗时的,耗时的操纵如何优化,能否减少sql查询的次数。

举例说明


  • 下面的代码示例,我们封装了 CommonRender 的类,所有可以统一输出的方法都在这里
  • 下面代码标注了优化之前优化之后
  • 优化之前:在每次查询都需要根据生存的id,去数据库查询;如果列表页每次返回30条数据,那这部分就需要30次sql查询。
  • 优化之后:接纳的是提前批量取值,又写了一个函数 _renderHobby ,只需要1次sql。
  • 这样就极大的减少了sql查询,提高了步调响应的速度。
  1. <?php
  2. namespace App\Render;
  3. .
  4. .
  5. .
  6. class CommonRender extends BaseRender
  7. {
  8.     public static function renderUserinfo($data, $hobbyInfo = [])
  9.     {
  10.         if (!is_array($data)) {
  11.             return [];
  12.         }
  13.         $ret = [
  14.             'uid' => !isset($data['id']) ? 0 : $data['id'],
  15.             'userid' => !isset($data['userid']) ? '' : $data['userid'],
  16.             'username' => !isset($data['username']) ? '' : $data['username'],
  17.             'usericon' => !isset($data['usericon']) ? [] : $data['usericon'],
  18.             .
  19.             .
  20.             .
  21. //优化之前
  22. //          'hobby' => !isset($data['hobby']) ? [] : HobbyInfo::getByIds($data['hobby']),
  23. //优化之后
  24.             'hobby' => !isset($data['hobby']) ? [] : self::_renderHobby($data['hobby'], $hobbyInfo),
  25.             .
  26.             .
  27.             .
  28.         if (!empty($ret['birth'])) {
  29.             $ret['zodiacSign'] = Utility::getZodiacSign($ret['birth']);
  30.         } else {
  31.             $ret['zodiacSign'] = '';
  32.         }
  33.         return $ret;
  34.     }
  35.     protected static function _renderHobby($userHobby, $hobbyInfo)
  36.     {
  37.         $ret = [];
  38.         if ($userHobby) {
  39.             $userHobbyIds = explode(',', $userHobby);
  40.             foreach ($userHobbyIds as $key => $userHobbyId) {
  41.                 $ret[$key] = $hobbyInfo[$userHobbyId];
  42.             }
  43.         }
  44.         return $ret;
  45.     }
  46.     //用户列表卡片常用字段
  47.     public static function renderListCardUserinfo($data)
  48.     {
  49.         .
  50.         .
  51.         .
  52.     }
  53. }
复制代码
进一步优化

上面的代码已经优化了性能,但是还不敷优雅。
获取单用户信息场景比力多,好比编辑,登录,检察单人信息等,这种情况下我还每次都提前批量查询吗?这样的话需要改造的地方太多了。
下面做进一步优化:
在render方法内部封装了一层,如果外部没有传入或传入空数组,自己再查询db获得一次需要的数据源。
  1. <?php
  2. namespace App\Render;
  3. .
  4. .
  5. .
  6. class CommonRender extends BaseRender
  7. {
  8.     public static function renderUserinfo($data, $hobbyInfo = [])
  9.     {
  10.         //区别在这里:批量查询外部传入,减少sql查询次数; 单次查询在render内查一次
  11.         $hobbyInfo = !empty($hobbyInfo) ? $hobbyInfo : HobbyInfo::getAllInfo();
  12.         if (!is_array($data)) {
  13.             return [];
  14.         }
  15.         $ret = [
  16.             'uid' => !isset($data['id']) ? 0 : $data['id'],
  17.             'userid' => !isset($data['userid']) ? '' : $data['userid'],
  18.             'username' => !isset($data['username']) ? '' : $data['username'],
  19.             'usericon' => !isset($data['usericon']) ? [] : $data['usericon'],
  20.             .
  21.             .
  22.             .
  23. //优化之前
  24. //          'hobby' => !isset($data['hobby']) ? [] : HobbyInfo::getByIds($data['hobby']),
  25. //优化之后
  26.             'hobby' => !isset($data['hobby']) ? [] : self::_renderHobby($data['hobby'], $hobbyInfo),
  27.             .
  28.             .
  29.             .
  30.         if (!empty($ret['birth'])) {
  31.             $ret['zodiacSign'] = Utility::getZodiacSign($ret['birth']);
  32.         } else {
  33.             $ret['zodiacSign'] = '';
  34.         }
  35.         return $ret;
  36.     }
  37.     protected static function _renderHobby($userHobby, $hobbyInfo)
  38.     {
  39.         $ret = [];
  40.         if ($userHobby) {
  41.             $userHobbyIds = explode(',', $userHobby);
  42.             foreach ($userHobbyIds as $key => $userHobbyId) {
  43.                 $ret[$key] = $hobbyInfo[$userHobbyId];
  44.             }
  45.         }
  46.         return $ret;
  47.     }
  48.     //用户列表卡片常用字段
  49.     public static function renderListCardUserinfo($data)
  50.     {
  51.         .
  52.         .
  53.         .
  54.     }
  55. }
复制代码
这样,那些获得单个用户资料的方法就不需要修改了。
  1.     //编辑用户资料
  2.     public function editUserInfo(Request $request)
  3.     {
  4.         $userInfo = UserInfo::editUserById($this->_userid, $request);
  5.         return [
  6.             'user' =>
  7.                 CommonRender::renderUserinfo($userInfo)
  8.                 + UserInfo::formatCoverAndPickedFootprint($userInfo)
  9.         ];
  10.     }
复制代码
性能对比

批量获得用户信息对比:性能提升立竿见影。

  • 好比每次取30个用户数据,之前获得爱好,职业,期望部分要查询30次db。
  • 优化之后只需要查询3次db。
  1.     public static function getBatchUserIntro($userid, $userList)
  2.     {
  3.         $retData = [];
  4.         if (empty($userList)) {
  5.             return $retData;
  6.         }
  7.         .
  8.         .
  9.         .
  10.         //批量获得爱好、职业、期望遇到 在foreach中计算取值,不重复请求DB取值
  11.         $hobbyInfo = HobbyInfo::getAllInfo();
  12.         $professionInfo = ProfessionInfo::getAllInfo();
  13.         $expectInfo = ExpectInfo::getAllInfo();
  14.         foreach ($batchUserInfo as $item) {
  15.             $retData[$item['userid']] = array_merge(
  16.                     ['wxnumber' => Utility::maskWxnumber($item['wxnumber'], $batchExchangeStatus[$item['userid']] == UserUserWeixinExchange::TYPE_TRUE)]
  17.                     + CommonRender::renderUserinfo($item, $hobbyInfo, $professionInfo, $expectInfo);
  18.         }
  19.         .
  20.         .
  21.         .
  22.         return $retData;
  23.     }
复制代码
注意,为了行文紧凑,代码段中省略了和文章无关的代码,用竖着的三个.省略。
以上就是php性能优化之不要在for循环中操纵DB的详细内容,更多关于php性能优化for循环DB操纵的资料请关注趣UU其它相关文章!

免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!
打赏作者
  • 0
  • 0
  • 0
  • 0
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

快速回复 返回顶部 返回列表