我继承了用PHP编写的电子商务软件项目。当我检查代码库时,我在代码中发现了很多SQL语句。有很多类,如Product,Category,User,Customer等,每个类都有很多数据库查询。
我不知道如何处理这种情况,并决定计算单页访问的总查询数。我封装了MySQL查询函数并增加了一个计数器。
我对结果感到有些震惊。要单独访问索引页面,执行了1633(!)个MySQL选择查询。列出类别的产品会触发近2000个查询。
我将查询传输到文本文件中进行分析。超过90%是可能有一个或两个值的单选语句。现在我该怎么做来清理这个烂摊子?你有什么建议?我在MySQL服务器上启用了缓存。加载页面大约需要490毫秒。
例如,有一个名为Product的类。在这个类中有8个单个小的SQL select语句。
当您现在打开类别列表以显示产品时,原始程序员使用一个select语句来获取所需产品的列表,然后为每个产品创建一个产品对象。
假设这个结果给了我们20个产品:
select id from products where price <= 10;
然后他遍历结果并为每个条目创建一个产品对象:
$qresult = query("select id from products where price <= 10");
$products = array();
foreach ($qresult as $prod) {
$products[] = new Product($prod['id']);
}
仅此一项就可以为产品生成20 * 8个SQL查询。同样的方法也用于其他类(用户,客户,类别等)。
前一段时间
现在,经过几周/几个月后,我想分享我到目前为止所做的解决方案。
我可以将查询减少到&lt;每页访问约50页,并将页面加载时间减少到400毫秒以下。
我很容易做到了。我试图确定热点并构建一个表缓存类。 每次访问此静态类都会将整个表内容加载到内存中,从现在开始,每个表请求都将从静态类中提取出内存。非常脏,不是很好,但它可以工作,更快,减少了总查询和备件服务器硬件。
我想我们也会把硬件抛给这个问题,只要用户数量增加,就像现在一样。
如果我们想要用另一个替换应用程序,我们一定会找到一个数据库查询很好的解决方案
感谢所有的建议
答案 0 :(得分:1)
嗯,对于初学者来说,你需要识别的第一件事就在于php领域,并且这些调用中有多少是多次执行的。仅这一点就会减少你的数字。
除此之外,你可以做两件事。
答案 1 :(得分:1)
我认为你是对的。 2,000个查询似乎很多。
您已经确定了重构的两个很好的理由,查询量和页面响应时间 - 既可以测量也适合重构。
尽管MySQL确实存在查询缓存,但每次查询基础数据时,如果它能够从缓存中完成查询,那么仍然可能存在与MySQL通信的网络成本。
您是否考虑过将值保存到内存或使用会话变量?
<?php
session_start();
// store session data
$_SESSION['sharedvalue']= "result of common MySQL query"
?>
< html>
< body>
< ?php
//retrieve session data - now each time you do this it isn't asking MySQL again
echo "Common Value=". $_SESSION['sharedvalue'];
?>
< /body>
< /html>
另一个解决方案是拥有自己的缓存并从中查询常用值,更改或过期它们以刷新旧数据。这实际上取决于您的应用程序和用户群的大小。
答案 2 :(得分:1)
我遇到的问题与我使用的遗留应用程序一样,以及相同级别的查询效率低下!在短期内,我们只是抛出硬件问题,但至少任何新的工作都会写得更好。听起来像没有必要功能的自定义ORM,或者没有以最佳方式使用的ORM。
尝试重构:
$products = array();
foreach ($products as $prod) {
$products[] = new Products($prod['id']);
}
进入这个:
// Assuming products is an array of arrays, with each inner array
// containing all the values that make up an array
$products = array();
foreach ($products as $prod) {
$products[] = Products::convertToObject($prod['id']);
}
那将为每个循环保存N个查询。如果您确保每个ORM类(Products
等)都继承自BaseORM
之类的东西,则执行此操作的公共代码只能写入一次。
如果您发现自己没有太多的连接方式,那么请对当前代码进行分支,并使用Propel或Doctrine等替换您自己开发的ORM。请注意,使用您不知道的ORM系统存在学习曲线,但最终它们的回报通常比自己编写整个系统更好。
答案 3 :(得分:0)
每次迭代都可能有一个(或多个)循环(foreach或while等)调用mysql_query。
找到该循环后,您可以决定如何优化它 - 例如,您可以将所有记录一次性加载到数组中,然后循环在数组上工作,而不是每次都调用数据库。