命令执行时超时
Command times out when executing
Laravel version:
8.x
PHP version:
8.0
MySQL version:
8.0
Server:
Larave Forge
我是 运行 我网站上的一个命令
php artisan command:here
几分钟后我得到这个状态“超时”,这里有什么可以优化以防止超时吗?
subscriptions
table 有 40,000
条记录,incomes
table 有 8,000,000
条记录。每个订阅在 incomes
table 中最多有 200
条记录。
为了解释这个命令的作用,这是修复(通过插入)每个 ACTIVE
订阅的收入缺失,这将根据与上次插入的收入的小时差来识别。每个订阅在 incomes
table 中最多有 200
条记录,因此如果脚本检测到 subscription
已经达到 200
条收入记录,它将 status
更新为
COMPLETED
.
Subscription.php(型号)
public function latestIncome()
{
return $this->hasOne(Income::class)->latestOfMany();
}
Income.php(型号)
public function subscription()
{
return $this->belongsTo(Subscription::class);
}
namespace App\Console\Commands;
class SomeCommand extends Command
{
protected $signature = 'command:here';
public function handle()
{
ini_set('max_execution_time', 0);
foreach (Subscription::with('latestIncome')->withCount('income')->where('status', 'ACTIVE')->lazy() as $subscription) {
$count_earnings = $subscription->income_count;
$recent_bonus = $subscription->latestIncome;
if ($recent_bonus) {
if ($count_earnings < 200) {
$hour_difference = now()->diffInHours($recent_bonus->created_at);
if ($hour_difference > 1) {
$to_insert = 200 - $count_earnings;
$max = $hour_difference;
if ($hour_difference > $to_insert) {
$max = $to_insert;
}
for ($i = 0; $i < $max; $i++) {
$income = new Income;
$income->user_id = $subscription->user_id;
$income->subscription_id = $subscription->id;
$income->amount = (100 * 0.002) * 100;
$income->save();
}
if (($count_earnings + $max) >= 200) {
$subscription->update(['status' => 'COMPLETED']);
}
Log::info('Fix for:'.$subscription->id.' | User:'.$subscription->user_id.' | Total:'.$max);
}
} else {
$subscription->update(['status' => 'COMPLETED']);
}
}
}
}
}
您可以在 __construct 函数或您的索引控制器中定义一个时间限制,如果您想要一个较大的时间限制。
public function __construct()
{
set_time_limit(8000000);
}
如果您批量更新,您可能会获得一些性能:
namespace App\Console\Commands;
class SomeCommand extends Command
{
protected $signature = 'command:here';
public function handle()
{
ini_set('max_execution_time', 0);
// Update everything you can before the loop
Subscription::has('income', '>=', 200)
->where('status', '!=' 'COMPLETED')
->update([ 'status' => 'COMPLETED' ]);
foreach (Subscription::with('latestIncome')->withCount('income')->where('status', 'ACTIVE')->lazy() as $subscription) {
$count_earnings = $subscription->income_count;
$recent_bonus = $subscription->latestIncome;
if ($recent_bonus) {
$hour_difference = now()->diffInHours($recent_bonus->created_at);
if ($hour_difference > 1) {
$to_insert = 200 - $count_earnings;
$max = $hour_difference;
if ($hour_difference > $to_insert) {
$max = $to_insert;
}
// Some performance gain here possibly
Income::insert(collect()->pad($max, [
'user_id' => $subscription->user_id,
'subscription_id' => $subscription->id,
'amount' => (100 * 0.002) * 100,
])->all());
Log::info('Fix for:'.$subscription->id.' | User:'.$subscription->user_id.' | Total:'.$max);
}
}
}
// Update anything that got pushed over that threshold
Subscription::has('income', '>=', 200)
->where('status', '!=' 'COMPLETED')
->update([ 'status' => 'COMPLETED' ]);
}
}
我认为子查询是主要的滞后部分。
这不是“sargable”:AND Date(
created_at) >= '2022-02-04'
。如果可能的话,说
AND created_at >= '2022-02-04'
AND created_at < '2022-02-04' + INTERVAL 1 DAY
在上面之后,subscriptions
上的这个索引将是有益的:
INDEX(status, deleted_at, created_at)
哦,我发现这个隐藏在评论中;你想要那个优化吗?
update `subscriptions`
set status = COMPLETED,
subscriptions.updated_at = 2022-02-14 17:53:10
where
(
SELECT count(*)
from incomes
where subscriptions.id = incomes.subscription_id
and incomes.deleted_at is null
) >= 200
and status != COMPLETED
and subscriptions.deleted_at is null)
可能受益于这些索引:
subscriptions: INDEX(deleted_at, status)
incomes: INDEX(subscription_id, deleted_at)
这行不通:updated_at = 2022-02-14 17:53:10
;日期时间需要引号。您可以考虑使用 NOW()
而不是构建字符串。
更重要的是,当超过 200 个时,Update 将一次检查 200+ 个中的每一个并更新它们 -- 每个 200+ 次!!努力将 UPDATE
重新排列为“Multitable”更新,其中一个 table 是一个子查询,如下所示:
SELECT subscription_id
FROM incomes
WHERE deleted_at IS NULL
GROUP BY subscription_id
HAVING COUNT(*) >= 200
这应该会使更新 运行 快一百倍。
Laravel version:
8.x
PHP version:
8.0
MySQL version:
8.0
Server:
Larave Forge
我是 运行 我网站上的一个命令
php artisan command:here
几分钟后我得到这个状态“超时”,这里有什么可以优化以防止超时吗?
subscriptions
table 有 40,000
条记录,incomes
table 有 8,000,000
条记录。每个订阅在 incomes
table 中最多有 200
条记录。
为了解释这个命令的作用,这是修复(通过插入)每个 ACTIVE
订阅的收入缺失,这将根据与上次插入的收入的小时差来识别。每个订阅在 incomes
table 中最多有 200
条记录,因此如果脚本检测到 subscription
已经达到 200
条收入记录,它将 status
更新为
COMPLETED
.
Subscription.php(型号)
public function latestIncome()
{
return $this->hasOne(Income::class)->latestOfMany();
}
Income.php(型号)
public function subscription()
{
return $this->belongsTo(Subscription::class);
}
namespace App\Console\Commands;
class SomeCommand extends Command
{
protected $signature = 'command:here';
public function handle()
{
ini_set('max_execution_time', 0);
foreach (Subscription::with('latestIncome')->withCount('income')->where('status', 'ACTIVE')->lazy() as $subscription) {
$count_earnings = $subscription->income_count;
$recent_bonus = $subscription->latestIncome;
if ($recent_bonus) {
if ($count_earnings < 200) {
$hour_difference = now()->diffInHours($recent_bonus->created_at);
if ($hour_difference > 1) {
$to_insert = 200 - $count_earnings;
$max = $hour_difference;
if ($hour_difference > $to_insert) {
$max = $to_insert;
}
for ($i = 0; $i < $max; $i++) {
$income = new Income;
$income->user_id = $subscription->user_id;
$income->subscription_id = $subscription->id;
$income->amount = (100 * 0.002) * 100;
$income->save();
}
if (($count_earnings + $max) >= 200) {
$subscription->update(['status' => 'COMPLETED']);
}
Log::info('Fix for:'.$subscription->id.' | User:'.$subscription->user_id.' | Total:'.$max);
}
} else {
$subscription->update(['status' => 'COMPLETED']);
}
}
}
}
}
您可以在 __construct 函数或您的索引控制器中定义一个时间限制,如果您想要一个较大的时间限制。
public function __construct()
{
set_time_limit(8000000);
}
如果您批量更新,您可能会获得一些性能:
namespace App\Console\Commands;
class SomeCommand extends Command
{
protected $signature = 'command:here';
public function handle()
{
ini_set('max_execution_time', 0);
// Update everything you can before the loop
Subscription::has('income', '>=', 200)
->where('status', '!=' 'COMPLETED')
->update([ 'status' => 'COMPLETED' ]);
foreach (Subscription::with('latestIncome')->withCount('income')->where('status', 'ACTIVE')->lazy() as $subscription) {
$count_earnings = $subscription->income_count;
$recent_bonus = $subscription->latestIncome;
if ($recent_bonus) {
$hour_difference = now()->diffInHours($recent_bonus->created_at);
if ($hour_difference > 1) {
$to_insert = 200 - $count_earnings;
$max = $hour_difference;
if ($hour_difference > $to_insert) {
$max = $to_insert;
}
// Some performance gain here possibly
Income::insert(collect()->pad($max, [
'user_id' => $subscription->user_id,
'subscription_id' => $subscription->id,
'amount' => (100 * 0.002) * 100,
])->all());
Log::info('Fix for:'.$subscription->id.' | User:'.$subscription->user_id.' | Total:'.$max);
}
}
}
// Update anything that got pushed over that threshold
Subscription::has('income', '>=', 200)
->where('status', '!=' 'COMPLETED')
->update([ 'status' => 'COMPLETED' ]);
}
}
我认为子查询是主要的滞后部分。
这不是“sargable”:
AND Date(
created_at) >= '2022-02-04'
。如果可能的话,说AND created_at >= '2022-02-04' AND created_at < '2022-02-04' + INTERVAL 1 DAY
在上面之后,
subscriptions
上的这个索引将是有益的:INDEX(status, deleted_at, created_at)
哦,我发现这个隐藏在评论中;你想要那个优化吗?
update `subscriptions` set status = COMPLETED, subscriptions.updated_at = 2022-02-14 17:53:10 where ( SELECT count(*) from incomes where subscriptions.id = incomes.subscription_id and incomes.deleted_at is null ) >= 200 and status != COMPLETED and subscriptions.deleted_at is null)
可能受益于这些索引:
subscriptions: INDEX(deleted_at, status) incomes: INDEX(subscription_id, deleted_at)
这行不通:
updated_at = 2022-02-14 17:53:10
;日期时间需要引号。您可以考虑使用NOW()
而不是构建字符串。更重要的是,当超过 200 个时,Update 将一次检查 200+ 个中的每一个并更新它们 -- 每个 200+ 次!!努力将
UPDATE
重新排列为“Multitable”更新,其中一个 table 是一个子查询,如下所示:SELECT subscription_id FROM incomes WHERE deleted_at IS NULL GROUP BY subscription_id HAVING COUNT(*) >= 200
这应该会使更新 运行 快一百倍。