【问题标题】:Caching data with class properties - why is it a bad idea?使用类属性缓存数据 - 为什么这是一个坏主意?
【发布时间】:2012-08-22 10:17:27
【问题描述】:

我最近阅读了许多关于 PHP 应用程序可扩展性的文章。几乎我读过的所有文章都提到了缓存,所以我想出了在类属性中缓存数据库数据的想法,以防止过多的数据库查询。我想分享这个想法,所以我在博客上发布了它,只是让我的老师告诉我这是毫无意义和愚蠢的。除了用无意义和愚蠢这两个词之外,他无法真正解释为什么它很糟糕。有人可以解释一下为什么这种缓存方法来帮助扩展 PHP 应用程序是不好的吗?

方法:

理论:

我认为最好有一个类属性(变量)来存储获取的数据库数据,以防止需要重复查询或返回相同数据的查询。

如果你不明白,下面是我博客中的一个例子:


我将把 Facebook 带到这个例子中,只是为了简化解释。假设我们正在为社交网络重新编码用户类。

class FBuser
{

}

这个类将包含的明显方法:

getStatusUpdates()
getAccountInfo()
getFriendIDs()

最初,这些方法都必须执行数据库查询,以获取所需的数据。但是使用缓存方法,我会定义一个类属性来存储缓存的数据,并且所有数据库查询都在一个方法中进行:

class FBuser
{
    private $userCache = array();

    private function getData( $dataToGet = '' )
    {

    //all of my db querying would happen here

    }

}

但是在同样的方法中,如果允许的话,我也会寻找缓存:

private function getData( $dataToGet = '' , $useCache = true )
{
   //am I allowed to use cache?
   if ( $useCache === true )
   { 
       //does the appropriate data exist in cache?
       if ( isset($this->userCache[ $dataToGet ]) )
       {
           return $this->userCache[ $dataToGet ];//return the cached data, and forget about the DB queries
       }

   }

   //if we get here, caching was disabled or the required data has not yet been cached :(
   //all of my db querying would happen here

   //store the data that's just been fetched by the queries in the cache property

}

这样,当我想从数据库中获取数据时,我可以调用getData( 'the data I want' , true );,允许我在可能的地方和时间使用缓存的数据。

因此,如果我需要多次调用getAccountInfo()getStatusUpdates()getFriendIDs(),此方法将阻止执行多个数据库查询 = 有利于扩展(我认为)。


【问题讨论】:

  • 缓存数据持久化在哪里?如果仅在财产方面-几乎没有意义。在一个页面请求期间执行相同请求的可能性很小。更有意义的是有一些快速的键值存储(如 memcached)并将检索到的数据存储在那里
  • 另外 - 拥有$useCache = true 并不是一个好的设计。改用缓存装饰器。
  • 但是不同的方法可能需要获取相同的数据。在此示例中,getAccountInfo() 需要获取与 getEmailAddress() 相同的数据。
  • 这不值得。如果你想缓存数据 - 使用一些持久性存储。是的,将数据保留在属性中以供进一步使用也可能是一件好事,但真正的缓存将大大提高整体性能。因此,从“可扩展性”的角度来看 - 您的解决方案几乎毫无意义。所以我什至会同意你老师的观点
  • @zerkms 有一个观点,请参阅我的回答,我在其中扩展了他试图指出的内容。

标签: php performance caching scalability


【解决方案1】:

你的老师很傻:p

我要说的主要一点是,这种类型的缓存实际上非常有用,具体取决于上下文。我在自己开发的 web 框架中做了一些这样的工作,这种重构是通过使用 XDebug 对缓存研磨的仔细分析来驱动的。

这样想。您的数据库访问是您的 PHP 脚本将执行的一些最昂贵的(就性能而言)工作。很容易找到与 DB 相关的调用占页面总执行时间 50%(或更多)的页面。为什么不缓存结果,以便数据的任何重用都会自动受益?

在 PHP 资源分配方面没有理由不这样做,因为在幕后,PHP 将共享对 zval 的引用,除非它们被修改,因此您的脚本也不需要堆上更多的内存.

对于那些对此表示怀疑的人,我会挑战他们在一个进行一次数据库调用而不是两次调用的页面上运行 XDebug,并向全世界宣布他们看不到显着的结果。实现这一点的代码如此简单,为什么不进行改进呢?

现在,有些人可能会指出更持久的缓存形式,并说您应该使用它们来代替它。我不同意这种回应所暗示的普遍性。也许该数据集太大而无法在整个服务器上缓存。例如,当每天只有 1% 的用户登录时,我不会将每个人的数据缓存在内存中。服务器上的内存不值得。也许数据经常更新,在这种情况下,同步成为一个问题/负担,可能超过缓存的好处。我想说的是,在某些情况下,更持久的缓存形式并不合适。

保持绿色,每个周期都很重要:)

【讨论】:

  • 很好的答案,很高兴你同意我的看法。谢谢xD
  • “您的数据库访问是您的 PHP 脚本将执行的一些最昂贵的(就性能而言)工作。” - 问题是 OP 不会在脚本之间共享缓存,而只会在每次调用时缓存它。所以 - 这没有多大意义。它可能会带来一些改进,但正如@Mahn 指出的那样——仅在非常具体场景中,而持久缓存解决方案总是效果很好。
  • @zerkms,12% 的收益肯定会帮助您扩大规模,并且就问题中提供的简单示例而言,我已经看到类似代码的收益接近或超过 12%。跨度>
  • @Adam:我个人确实遵循所介绍的技术,但从不将其称为缓存,因为多次执行相同的查询是一件奇怪的事情。 PS:不知道你从哪里得到 12%
  • @zerkms,您不必将其称为缓存。我的观点只是,老师说这很愚蠢是愚蠢的,因为像你我这样的人、ORM 和许多其他工具实际上就是这样做的。我喜欢 12% 这个数字,因为 Knuth 在他早期的一部开创性作品中引用了它(如果你还没有读过它,请查看它,对 go to 声明也有很好的看法:)pplab.snu.ac.kr/courses/adv_pl05/papers/p261-knuth.pdf
【解决方案2】:

为什么这是个坏主意?

严格来说这不是一个坏主意本身,因为它会按照你的预期去做,并且可以获得一点性能如果您的脚本中有重复的查询。

然而,在实践中,除非您的脚本正在执行非常非常好的操作,不寻常,否则您的典型 PHP 脚本的每个请求的数据库调用次数不会超过 15 或 20,并且超出那些也许只有 2 o 3 是重复的顶部。如果数据库调用已经相对较快,那么处理 2 或 3 个数据库调用将在性能上产生可忽略的差异。更不用说数据库本身可能已经有缓存系统了!

根据您的应用/脚本,实现持久缓存(存在于请求之间)是潜在的性能大奖所在。

我不是说“不要这样做”,我只是说除非你计划在同一个请求/脚本中运行同一个查询数百次,这通常是不太可能,如果没有持久的解决方案,您将看不到太多东西;但绝对不会痛。

【讨论】:

  • 如果一个页面发出 15 - 20 个页面请求,那么您的问题就更大了。当根据发出 2 到 4 个 DB 请求(一个更常见的数字)的页面来衡量性能改进时,结果是显着的(即,接近 Donald Knuth 等人所说的 12% 的改进是值得的性能改进。)
  • 算法并不是要达到“潜在的性能大奖”。当 Knuth 博士说“过早的优化是万恶之源”时,他这样做的背景是他正在通过优化的先验评估来解决问题(任何人在没有测试的情况下质疑这个想法都是有罪的。)是关于找到有意义的(对我来说,我经常争取提高 10% 或更高的性能,但你可以选择你想要的任何百分比的改进)并尽可能地实施它们。
  • 如果您每页发出 2-4 个 DB 请求,甚至其中两个 完全相同,那么每个请求的缓存将为您带来性能提升,那么你的模型层肯定做错了什么。
  • 你假设一个模型层(有很多可能的方法来抽象数据库调用,我来自函数式编程背景。)也就是说,如果模型层正在执行这种类型的工作,即使它对用户是透明的,模型层也在做学生建议的事情,这支持了他关于这种缓存类型的价值的案例。
  • @Mahn, “...如果我在脚本中有 4 个类似以下的查询:SELECT * from users WHERE userID = 1,删除其中一个不会给我任何帮助。”好的,我刚刚在 Rackspace Cloudsites 上使用我的 wordpress db 进行了测试(首先使用 4 SELECT * from wp_posts WHERE id = 1,然后使用 3、2、1。)查看缓存研磨,我看到增加了 7%,增加了 9%,增加 16%。而且,我启动了 PHP 会话并加载了我的 Web 框架(以增加 PHP 的工作量),这发生在非常小的数据库上的非常小的、简单的查询中。在我看来,这些性能提升并非毫无意义。
猜你喜欢
  • 1970-01-01
  • 2019-02-01
  • 1970-01-01
  • 1970-01-01
  • 2021-08-23
  • 2011-11-29
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多