防止口才查询中的模型水化

And*_*gan 5 php mysql performance laravel eloquent

是否有可能使雄辩的查询构建器返回StdClass而不是返回Model

例如User::where('age', '>', 34)->get()返回CollectionUser车型。

DB::table('users')->where('age', '>', 34)->get()回报CollectionStdClass对象。快多了。

因此:

是否可以StdClass数据库查询构建器一样防止复杂的表述模型和返回对象,但仍然利用雄辩的查询构建器语法的作用?


我不明白为什么这个问题被否决。它的期望和措辞很明确。人们应该将鼠标悬停在不赞成投票的图标上,并看到提示“此问题未显示任何研究成果;尚不清楚且无助。”

Laravel社区坚信,如果您离开Eloquent,您就会过早地进行优化。这是不正确的,在有10,000多行水合的情况下,数据库结果中的雄辩模型非常缓慢。我们谈论的是秒,有时是数十秒,而且内存很大。

然后Laravelers会说“ DB::query()快点做”。是的..是的...它是即时通知的...虽然我在问这个问题...就在这里...上面..措辞简洁明了...我可以使用Eloquent Query Builder来构建查询并返回StdClass对象,以使所有开销的Eloquent模型都不会水化。我已经听过Laravelers的“为什么不使用DB::query()呢?” 因为我不是在问...我问我们是否可以使用Eloquent语法比进行连接更容易,更易读。

我不是在这里询问有关应用程序体系结构的建议。我们想要执行此操作有很多深层原因,例如与我们的ElasticSearch存储库的输出相匹配等等。但是我不必进入为什么-这是一个问答论坛。不是讨论或建议平台。

  • 脸掌

Rod*_*ore 19

是的,可以使用“getQuery”或“toBase”方法。例如:

User::where('age', '>', 34)->getQuery()->get();
Run Code Online (Sandbox Code Playgroud)

或者

User::where('age', '>', 34)->toBase()->get();
Run Code Online (Sandbox Code Playgroud)

  • @AndrewMcLagan 这不应该是公认的答案吗? (2认同)

Par*_*ras 8

首先,这是一个非常好的问题。所有那些讨厌的人,让OP休息一下!他是亲拉拉维尔(Laravel),这不是一个Eloquent sucks问题,所以很酷。

在我看来,

水化模型很少影响应用程序性能

那里有太多的ORM,如果您看任何框架,这些问题会不断出现-但是,正如我已经意识到的,事实是ORM几乎不会影响性能。

罪魁祸首通常是查询本身而不是ORM

让我举几个例子说明为什么雄辩的模型可能比数据库外观查询慢的原因:

1.模型事件

模型中模型事件(例如保存,创建等)时,它们有时会减慢处理速度。并不是说应该避免事件,您只需要在何时以及不使用它们时小心一点即可。

2.加载关系

我无数次看到人们使用Eloquent提供的附加列表加载关系,有时模型有5-10个关系。每次您触发一个雄辩的查询时,就有5-10个联接!如果将其与DB Facade查询进行比较,肯定会更快。但是话又说回来,真正的罪魁祸首是谁?不是ORM,而是查询(带有额外的联接!)

举例来说,不久之后有人问了一个问题,他/她想知道为什么雄辩的查询要比原始查询慢。看看这个!

3.不了解是什么触发了口才查询

这是迄今为止人们认为ORM速度较慢的最突出原因。他们通常(并非总是)不了解是什么触发了查询。

例如,假设您要更新products表格并将产品#25的价格设置为$ 250。

也许,您在控制器中编写了以下内容:

$id = 25;
$product = Product::findOrFail($id);
$product->price = 250;
$product->save();
Run Code Online (Sandbox Code Playgroud)

然后,您的同事说,嘿,这太慢了。尝试使用数据库外观。所以你写:

$id = 25;
DB::table('products')->where('product_id', $id)->update(['price' => 250]);
Run Code Online (Sandbox Code Playgroud)

和繁荣!它更快。同样,罪魁祸首不是ORM。这是查询。上面的一个实际上是2个查询,findOrFail触发一个select *查询并save触发一个更新查询。

您可以并且应该使用Eloquent ORM将其编写为单个查询,如下所示:

Product::where('product_id', 25)->update(['price' => 250]);
Run Code Online (Sandbox Code Playgroud)

查询优化的一些好的做法

  1. 让您的数据库代替PHP来完成大部分工作:例如,而不是遍历Eloquent集合,也许以数据库为您完成工作的方式来构造数据库查询。

  2. 大量更新超过单个更新:显而易见。避免将模型保存在for循环中,!

  3. 对于繁重的查询,请使用事务:数据库事务避免在每个插入上重新索引。如果您真的需要在单个函数调用中调用数千条插入/更新查询,请将它们包装到事务中

  4. 最后但并非最不重要的一点是,如果有疑问,请检查您的查询:如果您有任何疑问,也许ORM是真正的罪魁祸首-再想一想!检查您的查询,尝试并对其进行优化。

如果ORM减慢了速度,请使用观察器或Laravel调试栏来比较带有或不带有ORM的查询。通常,您会发现查询是不同的,差异不是水合而是实际的查询本身!

  • 不,不是我所知道的。如果您希望返回 StdClass 对象,我认为唯一的方法是使用 DB 门面。但是当然,您需要告别所有 Eloquent 功能,例如关系、模型事件等。虽然就像我之前所说的,10k 行是一个非常小的数据集,我 100% 肯定,可以补充 10k行不能花费 20-30 秒!如果你将它包装在一个事务中并使用最优查询,不要认为它应该是一个问题。我的猜测是您的查询未优化 (2认同)