如何在 PHP 的 foreach 循环中优化大型 mySQL?

Posted

技术标签:

【中文标题】如何在 PHP 的 foreach 循环中优化大型 mySQL?【英文标题】:How to optimise large mySQL within a foreach loop in PHP? 【发布时间】:2014-12-05 07:14:52 【问题描述】:

我有一个函数可以获取用户详细信息并返回一个关联数组,该数组由每个用户及其相关数据的数组组成。我的函数可以工作,只是当它必须从 mysql 获取大量行时它不能正常工作。

function function_name($DB, $id)

    //Prepare, bind and execute statements
    //Returns one value or an array


function main_function($DB, $id_list)

    foreach($id_list as $user_id)
    
        //Calls function_name
        $data = function_name($DB, $user_id);
    

    //Returns a nested associative array

有人告诉我,在我的情况下,我应该将绑定参数语句移到 foreach 循环之外,但我已经尝试过,但一直收到错误消息“MySQL 已消失”。当我可能一次查询 10,0000 个 id 时,如何优化从 mysql 的查询?

详细解释请参考下面的代码sn-p。

function getUserEvent($DB_3308, $user_id) 

    $user_event = array ();

    $sql_get_user_event = "SELECT * FROM user_event WHERE user_id = ?";

    $statement_user_event = $DB_PUMA_3306->link->prepare ( $sql_get_user_event);
    $statement_user_event ->bind_param ( "s", $user_id );
    $statement_user_event ->execute ();

    if ($rs_user_event = $statement_user_event->get_result ()) 
    
        while ( $row = $rs_user_event->fetch_assoc () ) 
        
            $user_event [] = $row;
        
    

    return $user_event;


function getUserDetails($DB_3306, $DB_3308, $user_list)

    $user_details = array ();

    foreach ( $user_list as $user_id )
    
        $temp = array ();    
        $user_personal = null;
        $user_event = null;

        $user_personal = getUserContact ( $DB_3306, $user_id );
        $user_event = getUserEvent( $DB_3308, $userid );

        $temp ['user_id'] = $user_id;
        $temp ['full_name'] = $user_personal ['full_name'];
        $temp ['tel_no'] = $user_personal ['tel_no'];
        $temp ['email'] = $user_personal ['email'];
        $temp ['events'] = $user_event ;


        $user_details [] = $temp;
    

    return $user_details;

【问题讨论】:

如果是为了显示,那为什么不使用分页呢? @AbhikChakraborty - 很抱歉,我不确定您所说的分页是什么意思。 我的意思是,如果您有一个显示页面,其中列出了数据库中的所有用户,您可以选择在第一次分页时显示 100,查看此链接我所说的分页是什么意思 @987654321 @ 用于展示。但是,当我在这里没有显示更多计算内容时,它是否有效?例如计算结果总数,比较两个数组以获得相交总数等? 当然它可以工作,同时加载所有用户从来都不是一个好主意,而且更多地存储在数组中会耗尽所有内存。您可以在 php 上找到许多分页脚本,并且可以自定义您想要的方式。 【参考方案1】:

为什么你不能在从数据库中获取大约 50 或 100 个用户 ID 并批量获取它以减少更多查询负载之前在数组中获取?

$implodedUserIDs = implode(',', $userIDs);
$query = "SELECT * FROM user_event WHERE user_id IN ($implodedUserIDs)";

它会减少一些负载。你也可以在每次负载时给你一些睡眠。尽量优化你的代码。 :)

【讨论】:

这是我想尝试的事情之一,但有人告诉我,使用“SELECT .. WHERE...IN”会增加 mysql 的工作量。我理解错了吗? 如果使用它会减少查询次数。请尝试一次..你可以看到加载时间的差异.. 非常感谢。我会试试看。 如果您想快速将用户 ID 放入数组中,请尝试以下操作: $query = "SELECT user_id FROM user_list_tmp"; $result = $mysqli->query($query); $userIDs = array_column($result->fetch_all(), 0);【参考方案2】:

您似乎在循环(可能)10000 个用户,并且每个用户至少执行 2 个查询。每个查询都有一个小的开销来解析它等等,因此如果有大量的查询,这可以迅速加起来。

如果可能的话,我建议您将 2 个查询合并在一起,进行联接以获取用户联系方式和用户事件详细信息。

我还建议您对所有用户 ID 总共执行一次此单一查询,而不是每个用户 ID 执行一次。通常,使用带有用户 ID 列表的 IN 很容易做到这一点,但是对于 10000,这实际上并不可行。因此会生成一个包含您的用户 ID 列表的临时表。

非常粗略(并对您的数据库类和您的实际数据做出假设)如下:-

function getUserDetails($DB_3306, $DB_3308, $user_list)


    $sql = 'CREATE TEMPORARY TABLE user_list_tmp
            (
                user_id INT
            )';

    $DB_3306->execute($sql);

    $user_list_split = array_chunk($user_list, 250);

    foreach($user_list_split as $user_list_split_chunk);
    
        $sql = 'INSERT INTO user_list_tmp (user_id) VALUES ('.implode('),(', $user_list_split_chunk).')';
        $DB_3306->execute($sql);
    

    $sql = "SELECT a.user_id, b.full_name, b.tel_no, b.email, c.event_id
            FROM user_list_tmp a
            INNER JOIN user_contact b
            ON a.user_id = b.user_id
            LEFT OUTER JOIN user_event c
            ON a.user_id = c.userid
            WHERE user_id = ?
            ORDER BY a.user_id, c.event_id";

    $statement_user_event = $DB_3306->link->prepare ( $sql);
    $statement_user_event ->execute ();

    $user_details = array();

    if ($rs_details = $statement_user_event->get_result ()) 
    
        while ( $row = $rs_details->fetch_assoc () ) 
        
            $user_details[$row['user_id']]['user_id'] = $row['user_id'];
            $user_details[$row['user_id']]['full_name'] = $row['full_name'];
            $user_details[$row['user_id']]['tel_no'] = $row['tel_no'];
            $user_details[$row['user_id']]['email'] = $row['email'];
            $user_details[$row['user_id']]['events'][] = $row['event_id'];
        
    
    return $user_details;

这会将您传递的用户 ID 数组,将其分成 250 个数组并将它们插入到临时表中(我倾向于以 250 个为一组插入,作为可读和快速插入语句与执行最小数量之间的合理平衡单独的语句 - 您可以选择将其分成更大或更小的块)。

然后它执行一个查询,将 temp 表与 user_contact 表连接起来,并将它与 user_event 表连接起来。每个用户将返回多行,每个偶数行(但如果没有事件,仍然是一行)。它将这些放入一个数组中,我在这里使用 user_id 作为数组的键有点作弊。因此,对于用户 ID 的第一行,它将保存用户的详细信息,并且在用户的任何后续行(对于进一步的事件),用户详细信息将自己覆盖。事件详细信息只是放入该用户的事件数组的下一个数组成员中。

【讨论】:

以上是关于如何在 PHP 的 foreach 循环中优化大型 mySQL?的主要内容,如果未能解决你的问题,请参考以下文章

如何在 Laravel 的 PHP/Blade 中使用 Foreach 循环?

PHP10秒学会优化视图渲染中的foreach循环语句!

PHP10秒学会优化视图渲染中的foreach循环语句!

PHP编程优化的10条秘诀

php效率

如何在 foreach 循环中创建数组 - php? [关闭]