我是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循环中的插入查询是一种不好的方式,我只是想不出任何其他方法来获得完全相同的结果
如果我错误地格式化了我的问题,请提前致谢并抱歉
答案 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快得多,并且您应该在访问值之前一次获取所有内容。