【问题标题】:Best way to optimize multiple queries when using OOP使用 OOP 时优化多个查询的最佳方法
【发布时间】:2014-08-14 01:55:17
【问题描述】:

在使用 OOP 时,您一般会如何优化如下类的数据库性能,同时仍然遵循 DRY 原则?

class AccountExampleClass
{
    private $_mysqli;

    // Current account ID
    private $_accountId;

    public function loginActions()
    {
        // Stuff to do on login
        // ...

        // Store the current time in last_activity and last_login, using 
        // two queries instead of one.
        $this->updateLastActivity();
        $this->_updateLastLogin();
    }

    // Run on every pageview
    public function updateLastActivity()
    {
        $this->_mysqli->query("UPDATE `accounts` SET `last_activity` = NOW()
            WHERE `id` = $this->_accountId LIMIT 1");
    }

    // Only run on login
    private function _updateLastLogin()
    {
        $this->_mysqli->query("UPDATE `accounts` SET `last_login` = NOW()
            WHERE `id` = $this->_accountId LIMIT 1");
    }
}

这只是一个用于说明问题的快速模型。 last_activity 和 last_login 字段都有更新数据库的方法,但有时需要同时更新两者(如 loginActions 方法所示)。使用多个查询会浪费大量资源并增加延迟。

我现在能想到很多方法来做到这一点。其中一些很糟糕,而另一些则可用。

这是一个列表:

  • 手动进行专门查询,不要使用这些方法。
    • +不浪费资源。
    • -降低可维护性。
  • 创建一个类似代理的类,可以告诉它在执行一组查询之前对其进行优化。
    • +根据要求创建优化查询。
    • +可以在类中使用方法。
    • - 必须指定它应该优化哪些传入查询并告诉它之后执行查询。
    • - 对应用服务器的性能影响很小。
    • - 随着涉及更多外部组件,复杂性增加。
  • 使用对象来表示行的当前状态并一次性保存。
    • +简单。
    • -大量不必要的数据被发送到数据库。
    • -浪费资源。
    • -如果在获取行后数据发生变化,情况会变得更糟。
  • 使用对象表示行的状态,但只保存更改的列。
  • ...

在这种情况下,性能、复杂性和可维护性之间的最佳平衡是什么?

【问题讨论】:

    标签: php oop optimization mysqli dry


    【解决方案1】:

    我认为这里的答案是使用 ORM 层来为您处理此问题,允许您更新对象属性,然后在完成后保留这些更改

    在实体上调用 persist 方法不会立即导致 要对数据库发出的 SQL INSERT。学说应用策略 称为“事务性后写”,这意味着它将延迟 大多数 SQL 命令,直到 EntityManager#flush() 被调用,这将 然后发出所有必要的 SQL 语句来同步您的对象 以最有效的方式和单一、简短的数据库 事务,负责维护引用完整性。

    http://docs.doctrine-project.org/en/2.0.x/reference/working-with-objects.html

    【讨论】:

    • 这是同样的问题:在 OP 的示例中,谁应该调用 flush:loginActionsupdateLastActivity
    • 和什么一样的问题?关键是该原则允许您将更新对象和最终更新数据库的行为分开。至于何时调用 flush - 有各种明智的选择,包括可能使用 __destruct()
    • 这就是我所说的“类代理类”。我认为它可能是考虑 DRY 的最佳解决方案,即使存在(小)性能损失并且它需要一些额外的代码/复杂性。如果没有人很快提出一个很棒的解决方案,我会将此标记为答案。我想我们必须在性能和可维护性之间做出选择,因为我们不能两全其美。用 C 编写的一个好的 PHP ORM 扩展会很棒......项目想法?嗯……
    【解决方案2】:

    不要抢先优化,尤其是以增加复杂性为代价。我认为您关于增加一堆开销的想法是错误的。

    如果您真的想这样做,只需添加第三种方法(类似于 updateBoth)或将所有这些选项合并到一个方法中,该方法根据您想要的模式接受参数...

    function updateTime($mode){
         if ($mode == "login){}
         else if ($mode == "activity"){}
         else {
           //update both
         }
    }
    

    【讨论】:

    • 测试基准:使用 php 循环的单个稍微繁重的查询速度提高 96.2%。我不会将此称为微优化,因为如果查询很重,单个查询的速度将接近两倍。微基准测试表明,一个简单的页面只创建一个类,然后在方法内循环查询,如果速度快 92.1%(这是使用查询缓存)。在对整个 LAMP 堆栈进行基准测试时,在每个 http 请求上使用单个查询时,“只有一个查询”示例也快 18%。因此,如果这是在浪费时间来考虑,那就是查询的简单性问题。
    • 运行时间是多少?我猜我们正在谈论微秒。对我的其他想法没有意见?
    【解决方案3】:

    当这样的项目开始时,许多新手程序员直接编写 SQL 语句是很常见的。更有经验的程序员,使用其他更复杂的库,如 ORM、MVC、N-Tier 层。

    您可能想开始使用 ORM 库,它们支持您提到的“制作类似代理的类”和“使用对象表示行的当前状态”点。

    对象关系映射 (O.R.M.) 库,有时支持“使用对象表示行的当前状态”方法,有时支持“几行或所有行”,具体取决于您想要做什么。

    其中一些,允许在不得已的情况下直接编写SQL代码,altought,常用操作,插入,更新,查询,由方法处理。

    如果您需要,它们也有助于更改数据库服务器,或者只是更改表或字段的标识符。或者,当您使用具有相同结构/架构的多个数据库或多个表时。示例:Sales-January、Sales-February。

    这些库可能有点难以理解,但是一旦你习惯了它们,它们就会帮助你处理更复杂的操作,比如你发布的那个。

    只要我的 2 美分。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2014-07-19
      • 2012-07-14
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多