性能惩罚php类

时间:2014-12-20 10:10:42

标签: php oop

我正在为我的公司开发一个Web应用程序。该应用程序用于跟踪我们购买(和使用)的工具。

每次公司的工作人员使用工具时,都会在应用程序中注册(mysql db)。 到目前为止,我已经用程序风格编写了代码。在OOP中仍然是一个新手,我的程序代码工作正常并且相对较快。

现在应用程序变得越来越大,我真的遇到了维护旧编码模式的问题,因此我认为现在是时候继续尝试实现OOP了。我现在面临的问题是我以某种方式制作了我的课程,因此应用程序比以前慢了10倍。我的一个页面需要1秒才能加载 - 没有类需要0.1秒。 我使用php microtime来测量它。

我创建了一个公司类,其中包含每个公司的属性。 该课程的片段:

class Company {

    private $companyId;
    private $name;
    private $cvr;
    etc…..

    private $tools = array();

}

到目前为止,它的工作正如预期的那样。现在我想创建一个方法来获取所有的公司工具并将其附加到$ tools数组。

public function tools() {

    $purchases = $db
    ->table('CompanyTools')
    ->select(
        'id'
    )
    ->where('CompanyTools.companyId', '=', $this->companyId)
    ->get();

    $objects = array();

    foreach ($purchases as $purchase) {

        $objects[] = new CompanyTool($purchase['id']);

    }

    $this->tools = $objects;

    return $this->tools;

}

现在当我像这样使用它时,我得到了提到的性能问题

foreach ($company->tools() as $purchase) {

    echo $purchase->id;

}

我怀疑它与创建CompanyTool类的新实例的循环有关。 CompanyTool类看起来像这样

class CompanyTool {

    function __construct($id = null) {

    if(!$id) die("We need the id");

    $this->id = $id;

    $attributes = $db
        ->table('CompanyTools as ct')
        ->select(
            'ct.*'
        )
        ->where('ct.id', '=', $this->id)
        ->take(1)
        ->get();

    foreach($attributes as $attribute)
        foreach ($attribute as $key => $value) {
            if(property_exists($this, $key))  $this->{$key} = $value;
        }
    return $this;       


}

我希望很明显我想要实现的目标和缺乏OOP知识:)

感谢您的输入

/ j

5 个答案:

答案 0 :(得分:1)

您的方法需要审查很多事情。

其中一些:

  1. 在课程中使用Namespaces。总是。
  2. 运行数据库查询并在constructor中迭代结果集是非常糟糕的主意。
  3. 在迭代结果集时运行另一个数据库查询更糟糕。可能这是您的应用程序的主要性能瓶颈。
  4. 您应该为您的班级中的每个属性和方法定义visibility
  5. 基本上,似乎公司 CompanyTool 类是您的域对象(entites。请查看this answer)。实体很简单POPO,不应该知道数据库,查询或任何其他应用程序逻辑。
  6. 您可以通过从实体中解耦数据库查询来开始重构,您还需要利用sql JOINS来提高性能。
  7. 我强烈建议您阅读一些关于Object Relation Mappers的文章。您不需要使用它们,但您必须了解真正的问题是尝试解决ORM的问题。

答案 1 :(得分:1)

您可以进行简单的重新排列,以节省一些周期:

// get all fields in this query
$purchases = $db
  ->table('CompanyTools')
  ->select('ct.*')
  ->where('CompanyTools.companyId', '=', $this->companyId)
  ->get();

foreach ($purchases as $purchase) {
    // and pass each row to the constructor
    $objects[] = new CompanyTool($purchase);
}

// ====
class CompanyTool 
{
    function __construct(array $attributes) 
    {
        if (!isset($attributes['id'])) die("We need the id");

        // no need to perform any query here
        foreach ($attributes as $key => $value) {
            if (property_exists($this, $key))  $this->{$key} = $value;
        }
    }
}

答案 2 :(得分:0)

您发布的代码中没有真正的线索来了解您的代码为何比以前慢。我唯一能想到的是你在OOP代码中比在程序代码中做更多的MySQL查询。

当您开始使用OOP时,即使您不打算在特定HTML页面上使用此数据,也可能会在创建对象时完全使用数据库中的数据填充对象。这种方法显然存在缺陷。类只应在实际需要时检索数据。

例如,您有一个列出工具的页面,但每页只显示25个工具。在这种情况下,您不应该检索所有工具并将它们放在类的实例中,只需要25个。

这只是一个例子,但我认为你明白了吗?

检查您执行的所有MySQL查询并不困难。只需回显所有执行到屏幕的查询,或将它们写入文件或数据库,并记录它们花了多长时间。然后,您将能够看到服务器花费时间的位置。

但我的观点是只在实际需要数据时访问数据库,而不仅仅是填充对象。那将毫无意义。

答案 3 :(得分:0)

是的,只有10倍慢,你很幸运!

每次创建CompanyTool时都会运行查询

$attributes = $db ......这会伤害表现

特别是因为您正在使用某种ORM,所以通常情况下效果好三倍 通过它自己。

如果你需要完全实例化所有工具,你将会想要进行某种连接查询来获取所有数据并在一个查询中实例化它们,否则找出一种方法使用占位符并让它们在需要时填写缺失的细节。

答案 4 :(得分:0)

你肯定发现了你的主要性能损失,每次通过循环时初始化一个新的构造函数是一个非常糟糕的主意,特别是因为它每次构建时都运行一个查询 - 这导致每个对象的完整数据库获取周期您正在初始化的行通常本身并不具有计算成本,但是在更大的记录子集中会导致巨大的性能损失。

更好的处理方法是可能有一个单独的类封装所有工具,在启动时它会运行一个SQL查询,以便在数据库中回退并将其存储到您可以访问的数组中,这只会导致一个DB获取周期。

然后,您可以构建一些访问器方法,使用get_product_by_id($prod_idget_product_by_category($prod_cat)等特定关键字从工具数组中提取数据,以获取所需的项目

一些基本的伪代码可能看起来像这样 - 请注意,您应该将此类实现为单例,因为您只希望有一个CompanyTools数组可用。如果您不知道如何做到这一点,我强烈推荐Gooling Singleton设计模式:

class AllCompanyTools
{
     private $tools; // Array to hold all of your tools

     function __construct()
     {
         $query = SELECT * FROM company_tools ORDER BY product_id DESC
         $res = // Do your execution and capture these tools into an array

         set_results($res); // This method would check the array is not null and then set the locally defned 
     }

     function set_results($results)
     {
         int i = 0;

         // Check size of $res_array, ensure its not null and then load into $tools,
         // make sure $tools is an array of CompanyTools
         .. 

         // Assign each result into the tools array
         foreach($results as $tool)
         {
             // Insert into the index incrementally with the values from the assoc array
             $tools[i] = new CompanyTool($tool[id], $tool[name], $tools[category]);
             i++;
         }
     }

     function get_product_by_id($id) 
     {
          foreach($tools as $tool)
          {
              if($tool->$get_id() == $id)
              {
                 return $tool;
              }
          }
     } 
}

您的CompanyTool类将更加简单化

class CompanyTools
{
     // Swap for your vars...
     private $id;
     private $name;
     private $category;

     // Constructor
     function __construct($id_in, $name_in, $cat_in)
     {
        $id = $id_in;
        $name = $name_in;
        $category = $cat_in;
     }

     // Accessors
     function get_name() {
         return self->$name;
     }

     .. Other accessors below
}

因为$toolsCompanyTools的数组,我们可以简单地检查foreach循环中该工具是否与请求参数具有相同的id,如果是,我们将从数组中返回它,而不是连接到我们的数据库处理事务,返回结果,然后终止显着更昂贵的事务。

注意请注意上面的代码不是最终的解决方案,您需要做一些工作来构建CompanyTools数组,我只想展示如何处理性能它将使您有利于执行一个初始提取请求,然后使用该存储的数组来获取所需的数据,而无需与数据库保持连接。