php性能优化进阶不要在for循环中DB操作

目录
  • 前言
  • 场景说明
  • 解题思路
  • 代码示例:
  • 性能对比
  • 反思总结

前言

如何提高程序运行速度,减轻服务器压力是服务端开发必须面对的一个问题。

简单且朴素的原则:不要在for循环中操作DB,包括关系型数据库和NoSql。

我们应该根据自己的业务场景,在for循环之前批量拿到数据,用尽量少的sql查询批量查到结果。 在for循环中进行数据的匹配组装。

上一篇文章 性能优化反思:不要在for循环中操作DB ,被推荐到首页也收到了大家的互动评论,再接再厉,进阶一版。

说明:继续上一篇文档的demo整理,不赘述重复代码了,建议大家先读上一篇内容。

场景说明

  • 我们允许用户选择职业,系统预制了一批职业标签;又开放了自定义职业标签的功能,不限制自定义标签的次数。允许用户编辑资料时选择2个职业标签。
  • 发现用户自定义的职业真的五花八门,随着业务增长,数量级越来越大;比如目前职业标签是2千个,以后可能有2万个,甚至20万个。
  • 这种情况下,我们上一篇提到的在for循环之前批量查询全量数据,在for循环中用自定义函数匹配,避免在for循环中操作DB的方式命中率太低了,造成了极大的浪费。
  • 比如:每个列表返回30个用户信息,每个用户选择了2个职业标签,最大标签数量是60;而我全量查到的职业标签数量是2千,命中率只有3%;如果职业标签达到2万个,命中率就只有0.3%了。

解题思路

首先,在for循环中不操作DB,这个大原则不变

上述问题的核心是命中率太低,就是全量查了很多用不到的数据

解决思路就是只批量查询命中的标签数据:

  • 取到30个用户在user表中保存的职业id
  • 30个用户的id去重后重组
  • 在职业表通过whereIn查询匹配的职业标签
  • 其他逻辑不变,替换的只是数据源:之前的数据源是全量数据,优化后的数据源是精准命中的数据。

思路清晰之后,开始coding

代码示例:

为了行文紧凑,代码段中省略了和文章无关的代码,用竖着的三个.省略。

核心代码:抽取 renderUserInfo ,统一输出用户信息,这个函数在for循环中调用,获得数据源在for循环之前。

<?php
namespace App\Render;
.
.
.
class CommonRender extends BaseRender
{
    public static function renderUserinfo($data, $hobbyInfo = [],$professionInfo = [])
    {
        $hobbyInfo = !empty($hobbyInfo) ? $hobbyInfo : HobbyInfo::getAllInfo();
        //特殊处理,因为职业用户可以自定义 数字一直增长 不全量查数据;$professionInfo为空时不是批量查询,只查单条记录
        $professionInfo = !empty($professionInfo) ? $professionInfo : (isset($data['profession']) ? ProfessionInfo::getByIds($data['profession']) : []);
        if (!is_array($data)) {
            return [];
        }
        $ret = [
            .
            .
            .
//优化之前
//          'hobby' => !isset($data['hobby']) ? [] : HobbyInfo::getByIds($data['hobby']),
//          'profession' => !isset($data['profession']) ? [] : ProfessionInfo::getByIds($data['profession']),
//优化之后
            'hobby' => !isset($data['hobby']) ? [] : self::_renderHobby($data['hobby'], $hobbyInfo),
            'profession' => !isset($data['profession']) ? [] : self::_renderProfession($data['profession'], $professionInfo),
            .
            .
            .
        return $ret;
    }
}

isset() 判断,避免传入的数据不存在,提示数组越界。

我还整理了一篇 如何避免数组下标越界 ,有兴趣可以阅读一下。

protected static function _renderProfession($userProfession, $professionInfo)
{
    $ret = [];
    if ($userProfession) {
        $userProfessionIds = explode(',', $userProfession);
        foreach ($userProfessionIds as $key =&gt; $userProfessionId) {
            if (isset($professionInfo[$userProfessionId])) {
                $ret[$key] = $professionInfo[$userProfessionId];
            }
        }
    }
    return $ret;
}

调用 commonRender() 的代码,即展示数据源是怎么来的。

public static function getBatchUserIntro($userid, $userList)
{
    $retData = [];
    if (empty($userList)) {
        return $retData;
    }
    .
    .
    .
    $hobbyInfo = HobbyInfo::getAllInfo();
    //按需批量查职业,不全量查询职业
    $professionIds = array_column($batchUserInfo, 'profession');
    $professionIds = implode(',', $professionIds);
    $professionIds = array_unique(explode(',', $professionIds));
    $professionInfo = ProfessionInfo::batchGetByIds($professionIds);
    foreach ($batchUserInfo as $item) {
        $retData[$item['userid']] = CommonRender::renderUserinfo($item, $hobbyInfo, $professionInfo, $expectInfo);
    }
    return $retData;
}

封装的工具方法,通过id数组批量获得数据,做了特殊判断,兼容值为空的情况。

public static function batchGetByIds($ids = [])
{
    //兼容职业为空的情况
    foreach ($ids as $key =&gt; $id) {
        if (empty($id)) {
            unset($ids[$key]);
        }
    }
    if (empty($ids)) {
        return [];
    }
    return self::query()-&gt;selectRaw('id,name,pid')
        -&gt;whereIn('id', $ids)
        -&gt;get()
        -&gt;keyBy('id')
        -&gt;toArray();
}

核心代码就是上述4部分

性能对比

以此举例:每次列表返回30个用户信息,每个用户选择了2个职业标签,最大标签数量是60;

优化之前:全量查到的职业标签数量为2千,命中率只有3%;如果职业标签达到2万个,命中率就只有0.3%了。

优化之后:全量查到的职业标签数量为2千,命中率为100%;如果职业标签达到2万个,命中率仍然为100%。

反思总结

程序设计一定要结合业务场景,没有绝对正确的程序设计;

随着业务增长原本稳健的程序设计也可能遇到问题,技术人必须能和业务一起成长。

更多关于php性能优化for循环DB操作的资料请关注我们其它相关文章!

(0)

相关推荐

  • PHP中你可能忽略的性能优化利器:生成器

    前言 如果是做Python或者其他语言的小伙伴,对于生成器应该不陌生.但很多PHP开发者或许都不知道生成器这个功能,可能是因为生成器是PHP 5.5.0才引入的功能,也可以是生成器作用不是很明显.但是,生成器功能的确非常有用. 什么情况之下,会遇到PHP性能问题? 1:PHP语法使用不恰当. 2:使用PHP语言做了它不擅长的事情. 3:使用PHP语言连接的服务不给力. 4:PHP自身的短板(PHP自身做不了的事情). 5:我们也不知道的问题?(去探索.分析找到解决办法,提升开发境界). 优点 直

  • PHP+swoole+linux实现系统监控和性能优化操作示例

    本文实例讲述了PHP+swoole+linux实现系统监控和性能优化操作.分享给大家供大家参考,具体如下: 服务器监控 端口监控php运行shell脚本 class Server { const PORT = 8811; /** * 获取端口指定端口信息;如果在运行返回1:否则返回0: */ public function port() { $shell = "netstat -anp 2>/dev/null | grep ". self::PORT . " | gre

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

    目录 前言 场景说明 举例说明 进一步优化 性能对比 前言 如何提高程序运行速度,减轻服务器压力是服务端开发必须面对的一个问题. 简单且朴素的原则:不要在for循环中操作DB,包括关系型数据库和NoSql. 我们应该根据自己的业务场景,在for循环之前批量拿到数据,用尽量少的sql查询批量查到结果. 在for循环中进行数据的匹配组装. 场景说明 业务在多个情景下需要获得用户的详细信息,有点可以通过查询用户表直接获取到,有的需要查询关联关系表获取到,有的只保存了关联的id,并没有单独创建关联关系表

  • PHP性能优化大全(php.ini)

    第一章  针对系统调用过多的优化 我这次的优化针对syscall调用过多的问题,所以使用strace跟踪apache进行分析. 1.  apache2ctl -X & 使用-X(debug)参数启动httpd进程,这个时候只启动1个httpd进程 2. ps -ef | grep httpd 找到需要strace的pid 3. strace -p $PID -o /tmp/strace.log 发送一个http请求到httpd,就能看到strace信息了.   一.include_path问题

  • 浅谈PHP性能优化之php.ini配置

    内存 默认设置 memory_limit = 128M 单个进程可使用的内存最大值,这个值的设定可以从以下几点考虑: 应用的类型.如果是内存集中型应用,可增加该值: 单个 PHP 进程平均消耗的内存,该值可通过多次运行同一个脚本来计算平均值: 能负担多少个 php-fpm 进程:该值等于分配的总内存除以单个 PHP 进程平均消耗的内存: 文件上传 默认设置 file_uploads = On max_file_uploads = 20 upload_max_filesize = 2M max_e

  • php之性能优化案例

    php是一个很流行的脚本语言,现在很多公司(新浪.优酷.百度.搜狐.淘宝等等)在使用这种语言进行网站开发.我的这篇文章,我只是希望能够提高你的php脚本性能.请记住你的php脚本性能,很多时候依赖于你的php版本.你的web server环境和你的代码的复杂度. 优化你代码中的瓶颈 Hoare曾经说过"过早优化是一切不幸的根源".当你想要让你的网站更快运转的时候,你才应该去做优化的事情.当你要改变你代码之前,你需要做的事是什么原因引起了系统缓慢?你可以通过以下指导和其他方式优化你的ph

  • php性能优化进阶不要在for循环中DB操作

    目录 前言 场景说明 解题思路 代码示例: 性能对比 反思总结 前言 如何提高程序运行速度,减轻服务器压力是服务端开发必须面对的一个问题. 简单且朴素的原则:不要在for循环中操作DB,包括关系型数据库和NoSql. 我们应该根据自己的业务场景,在for循环之前批量拿到数据,用尽量少的sql查询批量查到结果. 在for循环中进行数据的匹配组装. 上一篇文章 性能优化反思:不要在for循环中操作DB ,被推荐到首页也收到了大家的互动评论,再接再厉,进阶一版. 说明:继续上一篇文档的demo整理,不

  • Android 进阶实现性能优化之OOM与Leakcanary详解原理

    目录 Android内存泄漏常见场景以及解决方案 资源性对象未关闭 注册对象未注销 类的静态变量持有大数据 单例造成的内存泄漏 非静态内部类的静态实例 Handler临时性内存泄漏 容器中的对象没清理造成的内存泄漏 WebView 使用ListView时造成的内存泄漏 Leakcanary leakcanary 导入 leakcanary 是如何安装的 leakcanary 如何监听Activity.Fragment销毁 RefWatcher 核心原理 流程图 本文主要探讨以下几个问题: And

  • 浅谈DOM的操作以及性能优化问题-重绘重排

    写在前面: 大家都知道DOM的操作很昂贵. 然后贵在什么地方呢? 一.访问DOM元素 二.修改DOM引起的重绘重排 一.访问DOM 像书上的比喻:把DOM和JavaScript(这里指ECMScript)各自想象为一个岛屿,它们之间用收费桥梁连接,ECMAScript每次访问DOM,都要途径这座桥,并交纳"过桥费",访问DOM的次数越多,费用也就越高.因此,推荐的做法是尽量减少过桥的次数,努力待在ECMAScript岛上.我们不可能不用DOM的接口,那么,怎样才能提高程序的效率? 既然

  • ASP.NET性能优化小结(ASP.NET&C#)

    ASP.NET: 一.返回多个数据集 检查你的访问数据库的代码,看是否存在着要返回多次的请求.每次往返降低了你的应用程序的每秒能够响应请求的次数.通过在单个数据库请求中返回多个结果集,可以减少与数据库通信的时间,使你的系统具有扩展性,也可以减少数据库服务器响应请求的工作量. 如果用动态的SQL语句来返回多个数据集,那用存储过程来替代动态的SQL语句会更好些.是否把业务逻辑写到存储过程中,这个有点争议.但是我认为,把业务逻辑写到存储过程里面可以限制返回结果集的大小,减小网络数据的流量,在逻辑层也不

  • Django查询数据库的性能优化示例代码

    前言 Django数据层提供各种途径优化数据的访问,一个项目大量优化工作一般是放在后期来做,早期的优化是"万恶之源",这是前人总结的经验,不无道理.如果事先理解Django的优化技巧,开发过程中稍稍留意,后期会省不少的工作量. 现在有一张记录用户信息的UserInfo数据表,表中记录了10个用户的姓名,呢称,年龄,工作等信息. models文件 from django.db import models class Job(models.Model): title=models.Char

  • Web性能优化系列 10个提升JavaScript性能的技巧

    Nicholas Zakas是一位 JS 大师,Yahoo! 首页的前端主程.他是<高性能 Javascript>的作者,这本书值得每个程序员去阅读. 当谈到 JS 性能的时候,Zakas差不多就是你要找的,2010年六月他在Google Tech Talk发表了名为<Speed Up Your Javascript>的演讲. 但 Javascript 性能优化绝不是一种书面的技术,Nicholas 的技术演进列出了10条建议,帮助你写出高效的 JS 代码. 1. 定义局部变量 当

  • JS性能优化笔记搜索整理

    通过网上查找资料了解关于性能优化方面的内容,现简单整理,仅供大家在优化的过程中参考使用,如有什么问题请及时提出,再做出相应的补充修改. 一. 让代码简洁:一些简略的表达方式也会产生很好的优化 eg:x=x+1;在不影响功能的情况下可以简写为x++; 二. 变量名方法名尽量在不影响语意的情况下简单.(可以选择首字母命名) eg:定义数组的长度可以取名为:ArrLen而不需要取为ArrayLength. 三. 关于JS的循环,循环是一种常用的流程控制. JS提供了三种循环:for(;;).while

  • js 性能优化之算法和流程控制

    循环处理是最常见的编程模式之一,也是提升性能必须关注的要点之一. 常见的优化方案有: ①JavaScript的四种循环(for.do-while.while.for-in)中,for-in循环比其他几种明显要慢.由于每次迭代操作会同时搜索实例或原型属性,for-in循环的每次迭代都会产生更多的开销,所以比其他类型要慢.因此遍历一个属性数量有限的已知属性列表,可以这样优化: var props = ['prop1', 'prop2'],i = 0; whlie(i < props.length){

  • Python 代码性能优化技巧分享

    如何进行 Python 性能优化,是本文探讨的主要问题.本文会涉及常见的代码优化方法,性能优化工具的使用以及如何诊断代码的性能瓶颈等内容,希望可以给 Python 开发人员一定的参考. Python 代码优化常见技巧 代码优化能够让程序运行更快,它是在不改变程序运行结果的情况下使得程序的运行效率更高,根据 80/20 原则,实现程序的重构.优化.扩展以及文档相关的事情通常需要消耗 80% 的工作量.优化通常包含两方面的内容:减小代码的体积,提高代码的运行效率. 改进算法,选择合适的数据结构 一个

  • JavaScript脚本性能优化注意事项

    循环是很常用的一个控制结构,大部分东西要依靠它来完成,在JavaScript中,我们可以使用for(;;),while(),for(in)三种循环,事实上,这三种循环中for(in)的效率极差,因为他需要查询散列键,只要可以就应该尽量少用.for(;;)和while循环的性能应该说基本(平时使用时)等价. 而事实上,如何使用这两个循环,则有很大讲究.我在测试中有些很有意思的情况,见附录.最后得出的结论是: 如果是循环变量递增或递减,不要单独对循环变量赋值,应该在它最后一次读取的时候使用嵌套的++

随机推荐