这代码需要优化吗
下面是一个定时任务,半小时执行一次,然后会给用户发送邮件,这块代码有什么地方能提升的,或者优化的?
public function handle()
{
Log::info('order_send_care_mails======================start');
$cur_time = Carbon::now()->format('Y-m-d');
O::where('send_number', '<', 3)->where('expected_time','=',$cur_time)
->chunk(100, function ($result) {
$care_mails = config('care_mails');
foreach ($result as $row) {
$send_number = $row->send_number + 1;
$type = $this->type[$send_number]['type'];
$view_template = 'email.' . $row->lang . '.care.' . $type;
//获取标题
$title = '';
if (isset($care_mails[$row->lang]["{$type}_title"])) {
$title = $care_mails[$row->lang]["{$type}_title"];
}
if (stripos($title, '{{product_name}}') !== false) {
$title = str_replace('{{product_name}}', $row->product_name, $title);
}
//处理内容
$link = [];
if (isset($care_mails[$row->lang]['link'][$row->product_name])) {
$link = $care_mails[$row->lang]['link'][$row->product_name];
if (!empty($row->product_feature) && isset($link[$row->product_feature])) {
$link = $link[$row->product_feature];
}else{
continue;
}
}
if (empty($link['Reached']) || empty($link['Guide']) || empty($link['FAQ'])){
continue;
}
$body = view($view_template, [
'link' => $link,
'name' => $row->name,
'product_name' => $row->product_name,
'version' => $row->version
])->render();
//发送邮件
self::sendmail($row->email, $title, $body, $send_number);
//更新数据
$row->send_number = $send_number;
$row->expected_time = self::getNextExpectedTime($send_number, $row->created_at);
$row->send_time = date("Y-m-d H:i:s");
$row->save();
}
});
Log::info('order_send_care_mails======================end');
}
public function sendmail($email, $title, $body, $send_number)
{
Log::info($email . ' ' . $title);
$form = $this->type[$send_number];
Mail::queue(new \App\Mail\Common\Send($form['email'], $email, $form['name'], $title, $body));
}
关于 LearnKu
高认可度评论:
O::where 这个看着就很nice
不用优化,关注用户量增长就好了。到了你半小时发不完的时候,就要优化了
不用,能跑就别动
关注发送量就行了
抽象一下,这个代码要查出数据库的一个集合,然后对集合的每条记录做一个处理。
这样,两个方法就可以被抽象出来了。
至少是这个程度。
不需要,这样公司永远需要你。
使用
chunk()的话,看你后面更新字段里有send_number这个也是条件语句,用chunk会有可能导致少处理数据的哦,建议使用chunkById()。比如有50条满足条件的数据(
send_number都都是3),方便举例的话id从1-50send_number + 1则都大于3,那么第二次基于条件 [[sql条件:limit 10 offset 1],那实际是从第 30 处理,因为第一次已经将数据更新则前面10条已经不满足where条件了。这就会出现少处理数据的情况,而
chuckById()是会记录上一次查询的最大 id,从而避免这种情况。具体可以自己看看sqlO::where 这个看着就很nice
1.命名统一使用驼峰写法
2.重复的变量可以抽出来,可以使用
phpstorm的Refactor -> Introduce Variable3.
$title = $care_mails[$row->lang]["{$type}_title"] ?? '';写法更加直观4.配置文件可以提到闭包外面使用

use引入加锁 万一意外情况半小时跑不完
优化建议:
在循环中,每次都会执行一次数据库查询,可以使用 Eloquent 的
with方法预加载关联模型,减少查询次数。在循环中,每次都会进行字符串拼接,可以使用 PHP 的
sprintf函数进行格式化输出,减少字符串拼接次数。在循环中,每次都会发送一封邮件,可以将邮件地址和邮件内容存储到一个数组中,循环结束后再进行批量发送,减少邮件发送次数。
在循环中,每次都会记录一次日志,可以将日志记录放在循环外面,减少日志记录次数。
优化后的代码如下:
public function handle() { Log::info('order_send_care_mails======================start'); $cur_time = Carbon::now()->format('Y-m-d'); O::with('product')->where('send_number', '<', 3)->where('expected_time','=',$cur_time) ->chunk(100, function ($result) { $care_mails = config('care_mails'); $mails = []; foreach ($result as $row) { $send_number = $row->send_number + 1; $type = $this->type[$send_number]['type']; $view_template = sprintf('email.%s.care.%s', $row->lang, $type); //获取标题 $title = ''; if (isset($care_mails[$row->lang]["{$type}_title"])) { $title = $care_mails[$row->lang]["{$type}_title"]; } if (stripos($title, '{{product_name}}') !== false) { $title = str_replace('{{product_name}}', $row->product->name, $title); }
}
public function sendmail($email, $title, $body, $send_number) { Log::info($email . ' ' . $title); $form = $this->type[$send_number]; Mail::queue(new \App\Mail\Common\Send($form['email'], $email, $form['name'], $title, $body)); }
我一般不会在
Job里面写数据库查询之类的业务逻辑,因为我认为Job的职责只负责定义队列中的行为,如:传递负载、重试策略和唯一性等。业务逻辑部分我会单独进行封装。
另外考虑到你的代码中是根据查询结果迭代发送邮件,你的代码会访问邮件服务器,执行时间取决于
迭代次数 * 邮件服务器通讯时长,那么当这两个变量不可控时,你的任务超时的几率将非常不稳定,所以我建议你将发送邮件单独拆出一个新Job来执行。最后要解决的问题是总数据量的问题,我注意到你这里在使用
chunk,将数据分批处理,当你的数据量足够大时,分批的数量也将是不可控的。这也可能造成任务执行超时,所以我建议当前任务中或在分发任务的位置只取要处理邮件的总数,并将分批参数(offset,limit)拆分好分发到任务中。如果你使用的是 Laravel 10 版本也可以使用任务批处理功能,这样能监视到各个批次执行的状态。邮件不多就不用管,邮件太多的话可以专门用几个队列来处理发邮件的逻辑
减少数据库查询次数
当前代码中的每次查询都是通过数据库来获取需要处理的订单,这会增加数据库负载。可以考虑缓存订单数据,或者使用更轻量级的缓存(如Redis)来存储订单数据。这样可以减少数据库查询次数,提高性能。
批量处理数据
目前的代码使用了 Laravel 的 chunk 方法来处理数据,但是这样每次只会处理100个订单。可以尝试调整该值,或者使用更高效的批量处理方法来提高处理效率。
减少邮件发送次数
目前的代码对于每个订单,会在不同的时间点发送多封邮件,这会导致邮件发送次数过多。可以考虑将多封邮件合并成一封,或者对邮件发送进行限制,避免发送过多的邮件。
使用异步发送邮件
当前代码使用了 Laravel 的邮件队列来发送邮件,但是这些邮件是同步发送的,即处理每个订单时会等待邮件发送完成后才会继续处理下一个订单。可以尝试使用异步发送邮件,将邮件发送放入队列中,并且使用单独的进程或服务器来处理邮件发送任务,这样可以避免主程序的阻塞。
使用更快的邮件发送服务
当前代码使用 Laravel 的默认邮件发送方式,但是这样可能会导致邮件发送速度较慢。可以尝试使用更快的邮件发送服务,如 SendGrid、Amazon SES 等。
减少模板渲染次数
当前代码中的邮件正文使用了 Blade 模板引擎进行渲染,但是这样会增加模板渲染的次数,导致性能降低。可以尝试使用其他更高效的模板引擎,或者将模板渲染结果缓存起来,避免重复渲染。
不用能跑就行 跑不动上队列,扔到队列里面消费就好
建议丢队列
人跟代码其中一个能跑就行