MYSQL和PHP:在PHP while循环中运行INSERT INTO SELECT查询,运行缓慢

时间:2014-11-06 21:15:00

标签: php mysql performance

我是php和MYSQL的新手,我一个月前对此一无所知,所以请原谅我的草率/糟糕的代码:)

我的PHP中有以下代码:

$starttime = microtime(true);
$q_un = 'SELECT i.id AS id
            FROM items i 
            WHERE i.id NOT IN (SELECT item_id FROM purchased_items WHERE user_id=' . $user_id . ')';
$r_un = mysqli_query($dbc, $q_un);
if (mysqli_num_rows($r_un) > 0) {
while ($row_un = mysqli_fetch_array($r_un, MYSQLI_ASSOC)) {
    $item_id = $row_un['id'];
    $q_rec = 'INSERT INTO compatibility_recommendations (
                `recommendation`,
                `user_id`,
                `item_id`)
                SELECT
                    ((SUM(a.rating*(a.compat-80)))/(SUM(a.compat-80)))*10 AS rec,
                    a.user_id AS user_id,
                    a.item_id AS item_id
                FROM
                    (SELECT r.rating AS rating, 
                        c.user2_id AS rater, 
                        c.user1_id AS user_id, 
                        c.compatibility AS compat, 
                        r.item_id AS item_id 
                    FROM ratings r
                    RIGHT JOIN compatibility_ratings c ON r.user_id=c.user2_id
                    WHERE c.user1_id=' . $user_id . ' AND r.item_id=' . $item_id . ' AND c.compatibility>80) a
                ON DUPLICATE KEY UPDATE
                    recommendation = VALUES(recommendation)';
    $r_rec = mysqli_query($dbc, $q_rec);
}
}
$endtime = microtime(true);
$duration = $endtime - $starttime;</code>

第一个查询选择当前用户$ user_id尚未购买的商品列表。然后,我在返回的每一行(item)上运行while循环,在此循环中执行主查询。

下一个查询是从评级表中获取信息,其中item_id等于正在查询的当前item_id,并将其连接到具有右连接的预先计算的用户兼容性表。

然后我对评级和兼容性评级运行算术以形成推荐值,然后将推荐,item_id和user_id插入到另一个表中以便稍后调用。 (item_id,user_id)列上有一个2列唯一键,因此最后是ON DUPLICATE KEY UPDATE

所以我今天早上写了这段代码,对自己很满意,因为它完全符合我的需要。

问题是,可以预见的是,它很慢。在我的测试数据库中,有5个测试用户和100个测试项目以及200个评级的随机分类,它需要2.5秒才能完成while循环。我原以为它很慢,但不是很慢。一旦添加了更多的用户和项目,它真的会挣扎。主要问题是插入...在重复密钥更新部分,我的磁盘利用率达到100%,我可以告诉我的笔记本电脑的HDD正在寻找疯狂。我知道我可能会在生产中使用固态硬盘,但我仍然预计会有数千个项目和用户出现重大问题。

所以我的主要问题是:任何人都可以就如何优化我的代码提出任何建议,或者完全重新调整以提高速度。我确定while循环中的插入查询是一种不好的方式,我只是想不出任何其他方法来获得完全相同的结果

如果我错误地格式化了我的问题,请提前致谢并抱歉

3 个答案:

答案 0 :(得分:0)

我找到了我正在寻找的答案here

每个项目的第二个查询仅为选择时间为0.002秒,但随后插入了0.06秒,因此我对查询进行了分析,发现“查询结束”占用了99%的查询时间。我已经设置了innodb_flush_log_at_trx_commit = 0,但对该答案的评论不以为然。我不使用交易,所以这种方法有什么后果/替代方案吗?它确实将我的while循环时间从2.5秒减少到0.08秒。

答案 1 :(得分:0)

$starttime = microtime(true);
$q_un = "

 INSERT INTO compatibility_recommendations 
 (recommendation
 ,user_id
 ,item_id
 )
 SELECT ((SUM(a.rating*(a.compat-80)))/(SUM(a.compat-80)))*10 rec
      , a.user_id 
      , a.item_id 
   FROM
      ( SELECT r.rating rating
             , c.user2_id rater
             , c.user1_id user_id
             , c.compatibility compat
             , r.item_id 
          FROM compatibility_ratings c
          JOIN ratings r
            ON r.user_id = c.user2_id

          JOIN items i
            ON i.id = r.item_id

          LEFT
          JOIN purchased_items p
            ON p.item_id = i.id
           AND p.user_id = $user_id

         WHERE c.user1_id =  $user_id
           AND c.compatibility > 80
           AND p.item_id IS NULL
      ) a
 GROUP BY a.item_id
 ON DUPLICATE KEY UPDATE recommendation = VALUES(recommendation);

 ";

$r_rec = mysqli_query($dbc, $q_rec);
}
}
$endtime = microtime(true);
$duration = $endtime - $starttime;</code>

对于任何进一步的改进,我们确实需要查看正确的DDL和上面的SELECT的EXPLAIN。

答案 2 :(得分:-1)

请参阅https://stackoverflow.com/a/14456661/2782404

fetch_assoc可能比fetch_array快得多,并且您应该在访问值之前一次获取所有内容。