提问人:softya 提问时间:9/6/2023 更新时间:9/11/2023 访问量:124
带有 2k 的 Laravel Foreach 循环,并从 200k 大头钉长长时间获取关系
laravel foreach loop with 2k and get relation from 200k tack long long time
问:
我有这个模型叫做产品 我有一个名为 Note_voucher_line 的模型
这是产品内部的关系
public function get_note_voucher_lines()
{
return $this->hasMany('App\Models\Note_voucher_line','product_id','id')->orderBy('date','asc')->orderBy('note_voucher_id','asc');
}
现在有时我必须循环这样的产品代码
$products = Product::whereBetween('id',[$num1,$num2])->get();
foreach($products as $product)
{
$lines = $product['get_note_voucher_lines'];
// when i use this relation it tack long long time
}
该模型有超过 300k 行
我有索引,这是里面的索引迁移Note_voucher_line
migration
note_voucher_lines
Schema::table('note_voucher_lines', function($table) {
$table->foreign('note_voucher_id')->references('id')->on('note_vouchers');
$table->foreign('user_id')->references('id')->on('users');
$table->foreign('journal_entry_id')->references('id')->on('journal_entries');
$table->foreign('warehouse_id')->references('id')->on('warehouses');
$table->foreign('product_id')->references('id')->on('products');
$table->foreign('cost_center_id')->references('id')->on('cost_centers');
$table->foreign('unit_id')->references('id')->on('units');
$table->foreign('is_it_bonus')->references('id')->on('status');
$table->foreign('note_voucher_type_id')->references('id')->on('note_voucher_types');
$table->foreign('posting_state_id')->references('id')->on('posting_status');
$table->foreign('product_total_quantity_id')->references('id')->on('product_total_quantitys');
$table->foreign('is_componentable')->references('id')->on('status');
$table->foreign('approved_state_id')->references('id')->on('approval_status');
$table->foreign('currency_id')->references('id')->on('currencies');
$table->foreign('branch_id')->references('id')->on('branches');
$table->foreign('created_by')->references('id')->on('users');
$table->foreign('deleted_by')->references('id')->on('users');
});
有一个名为 product_id 的索引属于 Products 表 这里有任何帮助可以加快速度 谢谢
答:
在这种情况下,急切地加载您的关系可能会有所帮助。
$products = Product::whereBetween('id',[$num1,$num2])->with('get_note_voucher_lines')->get();
foreach($products as $product)
{
$lines = $product->get_note_voucher_lines;
// This should be faster and note that this is the right way to fetch laravel relation not as an array
}
这里发生的情况是,我们使用该方法在每次迭代中从表中预加载 2k 行(假设您的 products 表包含 2k 行),而不是之前加载的一行。这减少了对数据库服务器进行的网络调用次数,因此它现在不再进行 300k/2k 调用,而是进行 300k/2k 调用。with()
note_voucher_lines
注意:您还应该考虑对产品使用块加载,以避免随着数据的持续增长而达到内存限制。https://laravel.com/docs/10.x/eloquent#chunking-results
评论
如果不需要一次处理所有 300k+ 行note_voucher_lines,请考虑在代码中实现分页。这样,您可以一次获取和处理有限数量的记录,这可以大大提高性能。
$perPage = 50; // Adjust the number of records per page as needed
$products = Product::whereBetween('id', [$num1, $num2])->get();
foreach ($products as $product) {
$lines = $product->get_note_voucher_lines()->paginate($perPage);
// Process the lines for this product
}
确保数据库服务器已正确调整性能。这包括设置适当的内存和缓冲区大小、优化查询以及定期维护数据库(例如,清理未使用的数据、重建索引)。
如果数据不经常更改,请考虑实现缓存机制来临时存储成本高昂的查询结果。这可以显著减少数据库上的负载。
就像 Alika Matthew 所说的那样,您需要使用急切加载来避免对每个产品执行额外的查询来获取它的凭证,如果您的服务器内存不足,您也可以使用 chunk 来处理大量数据
Product::whereBetween('id',[$num1,$num2])->with('get_note_voucher_lines')->chunk(100, function ($products) {
foreach ($products as $product) {
$lines = $product->get_note_voucher_lines;
}
});
延迟的主要原因似乎是get_note_voucher_lines关系的延迟加载。
每次在循环中访问此关系时,Laravel 都会进行单独的查询以获取相关行。这被称为 N+1 问题。
若要缓解此问题,请使用 Eager loading:
$products = Product::whereBetween('id',[$num1,$num2])->with('get_note_voucher_lines')->get();
此外,您还可以使用分块来处理大数据:
Product::whereBetween('id',[$num1,$num2])->with('get_note_voucher_lines')->chunk(100, function ($products) {
foreach ($products as $product) {
$lines = $product->get_note_voucher_lines;
}});
确保 id 字段上有索引。你提到有一个索引,但要确保它是一个正确的索引,而不仅仅是一个外键约束。
评论