9

我继承了一个用 PHP 编写的电子商务软件项目。当我检查代码库时,我在代码中发现了很多 SQL 语句。有很多类,如 Product、Category、User、Customer 等,每个类都有很多数据库查询。

我不知道如何处理这种情况,决定统计单页访问的总查询数。我封装了MySQL的查询功能,增加了一个计数器。

我对这个结果有点震惊。仅访问索引页,执行了 1633 个(!)MySQL 选择查询。列出一个类别的产品触发了近 2000 个查询。

我将查询输入到一个文本文件中进行分析。超过 90% 是可能只有一个或两个值的单选语句。现在我应该怎么做才能清理这个烂摊子?你有什么建议?我在 MySQL 服务器上启用了缓存。加载页面大约需要 490 毫秒。

附加细节

例如,有一个名为 Product 的类。在这个类中有 8 个单独的小 SQL 选择语句。

当您现在打开类别列表以显示产品时,最初的程序员使用一个 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 查询。同样的方法也用于其他类(用户、客户、类别等)。

前一段时间

现在,几周/几个月过去了,我想分享我到目前为止所做的解决方案。

我可以将查询减少到每次页面访问 < ~50 次,并将页面加载时间减少到 400 毫秒以下。

我很容易做到了。我试图识别热点并建立一个表缓存类。每次访问此静态类都会将整个表内容加载到内存中,并且从现在开始的每个表请求都将从静态类的内存中提供出来。嗯,很脏,不是很好,但它可以工作,更快,减少总查询并节省服务器硬件。

我想我们也会把硬件扔到这个问题上,只要用户数量像现在这样增加。

如果我们到了用另一个替换应用程序的地步,我们肯定会选择一个数据库查询很好的解决方案

谢谢大家的建议

4

4 回答 4

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 系统有一个学习曲线,但它们最终的回报通常比自己编写整个事情要好。

于 2013-02-27T14:36:15.957 回答
1

好吧,对于初学者来说,您需要确定的第一件事在于 php 领域,以及其中有多少调用被多次执行。仅此一项就可以减少您的人数。

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

  1. 如果您有多个使用相同参数的查询(例如 productId,并且您带来了产品表、产品类别表等,那么您始终可以加入这些查询并根据您的需要提供一个结果(或者查看最常见的查询加入并使用)
  2. 如果你有一个设置表,最好的办法是将所有表加载到内存中并从那里读取所有值,这样你就不必在需要设置的任何时候去数据库
于 2013-02-27T13:44:52.670 回答
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>

另一种解决方案是拥有自己的缓存并从中查询公共值,更改或过期它们以刷新旧数据。这实际上取决于您的应用程序和用户群的大小。

于 2013-02-27T13:45:34.447 回答
0

可能有一个(或多个)循环(foreach 或 while 等)为每次迭代调用 mysql_query。

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

于 2013-02-27T13:42:58.617 回答