减少复杂自定义应用程序中的数据库查询数量

时间:2013-02-27 13:32:33

标签: php mysql

我继承了用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毫秒以下。

我很容易做到了。我试图确定热点并构建一个表缓存类。 每次访问此静态类都会将整个表内容加载到内存中,从现在开始,每个表请求都将从静态类中提取出内存。非常脏,不是很好,但它可以工作,更快,减少了总查询和备件服务器硬件。

我想我们也会把硬件抛给这个问题,只要用户数量增加,就像现在一样。

如果我们想要用另一个替换应用程序,我们一定会找到一个数据库查询很好的解决方案

感谢所有的建议

4 个答案:

答案 0 :(得分:1)

嗯,对于初学者来说,你需要识别的第一件事就在于php领域,并且这些调用中有多少是多次执行的。仅这一点就会减少你的数字。

除此之外,你可以做两件事。

  1. 如果您有多个使用相同参数的查询(例如productId,并且您带有产品表,产品类别表等等),您始终可以加入这些查询并带来您需要的所有结果(或制作加入并使用的最常见查询的视图)
  2. 如果您有设置表,最好的办法是将所有表加载到内存中并从那里读取所有值,这样您就不必在需要设置时随时转到数据库

答案 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。

找到该循环后,您可以决定如何优化它 - 例如,您可以将所有记录一次性加载到数组中,然后循环在数组上工作,而不是每次都调用数据库。