翻译:在 Laravel 5 中使用 Repository 模式 (2)关联关系和渴求式加载

和上一篇一样 也是一篇翻译的文章,原文在这里 ,原作者:Mirza Pasic。

前言

开始这篇文章之前,我们先聊聊我们可能需要面对的问题。最近,有个客户抱怨页面打开非常慢。我决定打开 debug 信息来看看。其中 Query 部分令我非常吃惊,显示页面竟然用了 16500+ 个查询。

检查了一下。我找到了问题的源头—— 3 个  foreach  循环。它们通过 Model 里定义的关联关系来获取一些属性。它本来工作得非常正常,直到数据库里有 大约 5500 条数据。代码如下:

如果,$main_object = MainObject::all(); 返回了 5500 条数据 ,第一个 foreach 将会运行 5500 次,第二个 foreach 也会运行 5500 次。使用 ORM 经常会使开发人员写出非常低效的 ,消耗非常大的代码,而 ORM 另这些错误难以被发现。上面的提到的这个问题被称为 N + 1 问题。为了解决这个问题,我们可以使用渴求式加载。

什么是渴求式加载

简单地说,渴求式加载就是在一开始的时候就把一切都准备好。懒惰式加载与之相反。懒惰式加载是等需要用到的时候才去准备。渴求式加载可以帮助我们避开性能陷阱。可能有个例子会更好理解,想象一下下面的这个情况:

stores_model

我们有三个关连的模型。他们之间的关系可以这样描述,每个 member 可以拥有多个 store ,但是每个 store 只能属于一个 member 。每个 store 可以用个多个 product 但是每个 product 只能属于一个 store 。

下面是它们三个的 Eloquent model 文件:

Member:

 

Store:

 

Product:

假设一个这样的场景,构建一个应用,这个应用允许用户构建自己的 store ,当然,每个 store 里又可以插入 许多的 product 。还需要一个页面来显示所有的 store 和 store 里的主要的 product 。

storelist_mockup

 

在 controller 里你可能会这样写:

在 view 里你可能是这么写的:

 

结果是:

n_plus_one_problem

在这个例子里 ,我在数据库里放置了 5 个 member ,3 个 store ,和 4 个 product 。第一个查询就是从数据库里获取 所有的 store ,这也是 N + 1 问题的一部分。在这个例子 N 就是指第一个查询里返回的 store 的数量。我们有 3 个 store 那么我们还要查询 member  3 次,在 product 里再查询 3 次。那么总共就是 3 + 3 + 1 次查询。

想象一下,如果我们有 5000 或者 10000 个 store 呢,每当用户访问的时候,就可能要执行 10-20k 次查询。如果你每天有 10k 或者 100k 的 PV 。那简直就是一个噩梦。现在非常清楚了,这种方式就是一个性能杀手。无论使用什么 数据库,多么强大的服务器,这些方法都会造成很大的问题。你可能想用缓存来提高性能,比如 Redis 。它只能暂时撑一阵子。不过这会花费你很多的钱和时间,在此期间,你可能会失去很多的用户。

渴求式加载可破。在 Laravel 中使用渴求式加载非常的简单,你只需要在查询上使用 with 方法:

现在,你可以发现有了非常大的改善只用了 3 个查询:

n_plus_one_problem_eager

即使你有 10k 个 store 也只用 3 个查询,渴求式加载是非常有用的东西。正确使用它可以大幅提高程序的性能。当然了,我们应该有一个 id 字段并且这个字段建立了正确的索引,在没有建立索引的字段上执行 where in 查询也会消耗很多时间。

在介绍玩了 渴求式加载之后,可以看看在 Repository 模式里怎么使用。

扩展 Repostory 类

我会向你展示在具体的 Repostory 类中怎么使用关联关系。这有个例子:

我们将会有个办法使用 Model 里定义的关联关系,这个方法类似于  Laravel’s Query Builder with  方法。

然后我们需要把这些关联关系链接到 Model 上:

最后,我们需要修改一下我们的 all() 函数:

好了,那么现在我们来看看 controller :

然后 在 view 里你就可以尽情的显示你想显示的数据了。

同样的,只用 3 个查询:

eager_loading

结论

正确使用渴求式加载可以大幅提高应用的性能。但是有时候,使用渴求式加载还是不够用。下一个教程里,我将演示如何使用缓存来获取更高的性能。

发表评论

电子邮件地址不会被公开。 必填项已用*标注