Php Laravel块和删除

Php Laravel块和删除,php,laravel,eloquent,jobs,Php,Laravel,Eloquent,Jobs,我想从数据库中删除大量的项目(1M+),我会安排一个后台工作来处理,这样用户就不必等待它完成他/她正在做的任何事情,问题是,当项目被删除时,应用程序变得没有响应,所以我想我会一块一块地处理这些项目,然后睡上几秒钟,然后继续 以下是处理删除操作的代码: // laravel job class // ... public function handle() { $posts_archive = PostArchive::find(1); // just for the purpose of

我想从数据库中删除大量的项目(1M+),我会安排一个后台工作来处理,这样用户就不必等待它完成他/她正在做的任何事情,问题是,当项目被删除时,应用程序变得没有响应,所以我想我会一块一块地处理这些项目,然后睡上几秒钟,然后继续

以下是处理删除操作的代码:

// laravel job class
// ...
public function handle()
{
    $posts_archive = PostArchive::find(1); // just for the purpose of testing ;)
    Post::where('arch_id', $posts_archive->id)->chunk(1000, function ($posts) {
        //go through the collection and delete every post.
        foreach($posts as $post) {
            $post->delete();
        }
        // throttle
        sleep(2);
    });
}
预期结果:将帖子分块并处理每个分块,然后空闲2秒,重复该操作直到删除所有项目

实际结果:删除一次随机数目的项目,然后过程结束。没有错误没有指标没有线索


有没有更好的方法来实现这一点?

如果我理解正确,问题是删除大量条目需要太多资源。 一次写一篇文章会花费太长时间

尝试获取post.id的最小值和最大值,然后在类似的

for($i = $minId; $i <= $maxId-1000; $i+1000) {
    Post::where('arch_id', $posts_archive->id)->whereBetween('id', [$i, $i+1000])->delete();
    sleep(2);
}
for($i=$minId;$i id)->其中($id',[$i,$i+1000])->delete();
睡眠(2);
}

自定义数据块和睡眠时间,因为它适合您的服务器资源。

如果我理解正确,问题是删除大量条目会占用太多资源。 一次写一篇文章会花费太长时间

尝试获取post.id的最小值和最大值,然后在类似的

for($i = $minId; $i <= $maxId-1000; $i+1000) {
    Post::where('arch_id', $posts_archive->id)->whereBetween('id', [$i, $i+1000])->delete();
    sleep(2);
}
for($i=$minId;$i id)->其中($id',[$i,$i+1000])->delete();
睡眠(2);
}

自定义区块和睡眠时间,因为它适合您的服务器资源。

对于您处理此问题的方式,Laravel没有任何具体说明。如果作业中的删除查询冻结了UI的其余部分,则听起来您的数据库服务器需要检查或优化

检索每个模型并单独运行delete查询肯定不是优化此功能的好方法,因为您将执行数百万个查询。如果希望尝试限制应用程序中每秒的负载,而不是优化数据库服务器以处理此查询,则可以使用带有删除限制的while循环:

do {
    $deleted = Post::where('arch_id', $posts_archive->id)->limit(1000)->delete();
    sleep(2);
} while ($deleted > 0);

关于你处理这件事的方式,拉雷维尔没有什么特别的。如果作业中的删除查询冻结了UI的其余部分,则听起来您的数据库服务器需要检查或优化

检索每个模型并单独运行delete查询肯定不是优化此功能的好方法,因为您将执行数百万个查询。如果希望尝试限制应用程序中每秒的负载,而不是优化数据库服务器以处理此查询,则可以使用带有删除限制的while循环:

do {
    $deleted = Post::where('arch_id', $posts_archive->id)->limit(1000)->delete();
    sleep(2);
} while ($deleted > 0);

实际结果与预期结果不同的原因在于Laravel如何对数据集进行分块

Laravel一次分页一页数据集,并将
Post
模型的集合传递给回调函数

由于要删除集合中的记录,Laravel在每次迭代中都会跳过一页数据,因此最终会丢失原始查询中大约一半的数据

以下面的场景为例–您希望在10个区块中删除24条记录:

预期的

+-------------+--------------------+---------------------------+ | Iteration | Eloquent query | Rows returned to callback | +-------------+--------------------+---------------------------+ | Iteration 1 | OFFSET 0 LIMIT 10 | 10 | | Iteration 2 | OFFSET 10 LIMIT 10 | 10 | | Iteration 3 | OFFSET 20 LIMIT 10 | 4 | +-------------+--------------------+---------------------------+
注意:如果您只想删除数据库中的记录,这里的其他解决方案会更好。如果需要进行任何其他处理,则使用光标将是更好的选择。

实际结果与预期结果不同的原因在于Laravel如何对数据集进行分块

Laravel一次分页一页数据集,并将
Post
模型的集合传递给回调函数

由于要删除集合中的记录,Laravel在每次迭代中都会跳过一页数据,因此最终会丢失原始查询中大约一半的数据

以下面的场景为例–您希望在10个区块中删除24条记录:

预期的

+-------------+--------------------+---------------------------+ | Iteration | Eloquent query | Rows returned to callback | +-------------+--------------------+---------------------------+ | Iteration 1 | OFFSET 0 LIMIT 10 | 10 | | Iteration 2 | OFFSET 10 LIMIT 10 | 10 | | Iteration 3 | OFFSET 20 LIMIT 10 | 4 | +-------------+--------------------+---------------------------+
注意:如果您只想删除数据库中的记录,这里的其他解决方案会更好。如果您需要进行任何其他处理,那么使用光标将是一个更好的选择。

正如Kelvin Jones指出的,删除随机数项的原因是您在翻页时删除了记录

chunk
只需使用offset和limit在表中“分页”。但是,如果您从第1页(IDs1-100)删除100条记录,然后转到第2页,实际上您现在跳过了IDs101-200并跳到了201-300

chunkById
是解决这个问题的一种方法

Post::where('arch\u id',$posts\u archive->id)->chunkById(1000,函数($posts){
//浏览收集并删除所有帖子。
foreach($posts作为$post){
$post->delete();
}
});

从字面上讲,只需替换方法名即可。现在,它不再使用offset&limit进行分页,而是从第一页查看最大主键(100),然后下一页将查询
其中ID>100
。因此,第2页现在正确地为您提供了ID 101-200,而不是201-300。

正如开尔文·琼斯所指出的,删除随机数项的原因是,您在翻页时删除了记录

chunk
只需使用offset和limit在表中“分页”。但是,如果您从第1页(IDs1-100)删除100条记录,然后转到第2页,实际上您现在跳过了IDs101-200并跳到了201-300

chunkById
是解决这个问题的一种方法

Post::where('arch\u id',$posts\u archive->id)->chunkById(1000,函数($posts){
//浏览收集并删除所有帖子。
foreach($posts作为$post){
$post->delete();
}
});
从字面上说,只需更换