提问,怎么使用 PHP 短时间内处理大量数据

业务需求

  1. 现在数据库里面有5000个左右的数据
  2. 每隔3分钟我要调用外部API接口更新每个数据的状态,也就是说我要调用5000次
  3. 3分钟内必须更新完成,因为3分钟以后还要更新,每一次都要留下log,落下一次就有可能造成金额损失
  4. 更新一个数据大概会用掉1~1.2秒的时间

面临的问题

以这个处理速度来看的话,3分钟内是肯定不能处理完5000个数据的更新,而且这个数据量很有可能增加,假设1~1.2秒的处理时间已经没有优化的空间。

这个项目是用Laravel6, PHP7.2.8构建的,现在是用laravel自带的schedule::run cron 每3分处理一次,现在是测试阶段,所以只处理100个左右,但是到线上的话就得处理5000个了

有什么方法解决呢

个人的想法

  1. 是把数据分散开来,并列执行 (具体怎么施行还没想清楚)
  2. 真想不出来了

各位有什么好的建议吗?不吝赐教

2020-04-13 最近情况

这个系统现在向不用队列的方向发展
而是在Kernel.php中写死代码添加了PHP process

$offset_3 = 0;
$limit_3 = 100;
$totalKeywordCount = 500;

do {
       $schedule->command('autoprocess:min3', [$offset_3, $limit_3])->cron('*/3 * * * *')->runInBackground();

       $offset_3 += $limit_3;

} while ($offset_3 + $limit_3 < $totalKeywordCount);

这么整的话5000个可能是够呛,但是首先要确定这个方法的极限处理量,所以待我继续更新项目的走势吧。

《L02 从零构建论坛系统》
以构建论坛项目 LaraBBS 为线索,展开对 Laravel 框架的全面学习。应用程序架构思路贴近 Laravel 框架的设计哲学。
《G01 Go 实战入门》
从零开始带你一步步开发一个 Go 博客项目,让你在最短的时间内学会使用 Go 进行编码。项目结构很大程度上参考了 Laravel。
Epona
最佳答案

这种一般是写个命令,然后队列执行的吧。另外 每隔3分钟调用接口,处理数据,你应该还需要考虑数据覆盖的问题

4年前 评论
aodaobi 4年前
songxue77 (楼主) 4年前
讨论数量: 11

我们也有类似场景,也同时有出现过请求太快对面扛不住的情况。其实这类问题很简单,我非常赞成 @Epona 的回答,把任务推到队列启动多个 Queue workers 即可。

不知道为什么,最近几年国内的 PHP 圈子出现了盲目跟风推崇 Swoole 的现象。或许是因为大量 PHP 程序员认为 Swoole 是银弹吧。但我个人并不推荐你现在的场景用什么 Swoole,实际结果与使用 Queue workers 无异,反而增添了许多传统 PHP 程序不需要考虑的心智负担。

工程师的时间是最宝贵的。任务多了大不了升级一下服务器多运行几个 Workers,一个月千百块钱,相比于开发的时间成本,九牛一毛而已。

最后,千万不要被「协程」所迷惑了,记得回想一下当年的 Node.js 和异步回调。

4年前 评论
songxue77 (楼主) 4年前
Epona

这种一般是写个命令,然后队列执行的吧。另外 每隔3分钟调用接口,处理数据,你应该还需要考虑数据覆盖的问题

4年前 评论
aodaobi 4年前
songxue77 (楼主) 4年前

队列多开几条同时处理 或者并发 curl 请求

4年前 评论
songxue77 (楼主) 4年前
aodaobi

https://www.kancloud.cn/onanying/mixphp2-1... 使用swoole 协程 我们有些场景类似,也涉及到curl 请求接口更新本地数据,,目前我项目内 + mixphp(命令行模式使用协程) 做这个数据处理

4年前 评论
songxue77 (楼主) 4年前
wenqingzzz

php的话swoole协成最方便。go也可以

4年前 评论
songxue77 (楼主) 4年前

基于消息队列,如:RabbitMQ 来处理。具体流程如下

  1. 通过计划任务一次可以取100、1000条,根据需要进行动态limit
  2. 将以上数据进行分块(并行处理),比如10、50或100为一组生产消息至队列
  3. 在请求时,如果有使用swoole框架或扩展。直接开协程,协程数量以以上分组为量为基准,以你说的一条消息耗时1~1.2s, 通过协程处理也是这么长时间。也就是说一组里有100个。1~1.2秒就处理这些。如果是1000,同样是1~1.2秒。如果未使用swoole,也可以使用curl并行请求来处理
  4. 以上处理后,保守估计在1分钟左右就可以处理完成5000条。
  5. 如果因为网络抖动等其它原因失败的可以requeue处理,所以需要消息确认

最后,我们也有类似的业务,170w+数据。在一个进程并行10个请求时耗时1小时40分钟完成。其实可以更快。只是对方抗不住。逃。。。。。。 :speak_no_evil:

4年前 评论
songxue77 (楼主) 4年前

我们也有类似场景,也同时有出现过请求太快对面扛不住的情况。其实这类问题很简单,我非常赞成 @Epona 的回答,把任务推到队列启动多个 Queue workers 即可。

不知道为什么,最近几年国内的 PHP 圈子出现了盲目跟风推崇 Swoole 的现象。或许是因为大量 PHP 程序员认为 Swoole 是银弹吧。但我个人并不推荐你现在的场景用什么 Swoole,实际结果与使用 Queue workers 无异,反而增添了许多传统 PHP 程序不需要考虑的心智负担。

工程师的时间是最宝贵的。任务多了大不了升级一下服务器多运行几个 Workers,一个月千百块钱,相比于开发的时间成本,九牛一毛而已。

最后,千万不要被「协程」所迷惑了,记得回想一下当年的 Node.js 和异步回调。

4年前 评论
songxue77 (楼主) 4年前
leo

这个问题很明显瓶颈在于「更新一个数据大概会用掉 1~1.2 秒的时间」,不是很清楚具体是慢在哪里,比如执行一条很慢的 SQL,或者是取到数据之后需要用 PHP 进行大量的计算。

如果是前者很难通过使用队列的方式来解决,不管是用 Laravel 的队列、 swoole 甚至是换成 go 都没用,因为瓶颈在数据库,你需要的是升级服务器;如果是后者, @Epona 给的方案是最简单成本最低的。

4年前 评论
songxue77 (楼主) 4年前

模拟计算一下,按你说到1.2秒处理一个,那么三分钟处理完5000个需要多少个任务?
5000/(1.2x3x60) ≈ 23.14 看来你使用队列开启的任务起码都要23个。
我的看法跟大家一样,使用专业队列。数据覆盖的问题可以检测的,就是updated_at。如果当前使用的数据的updated_at是三分之内,正常。如果不是,就马上调用更新接口单独更新这条数据。

4年前 评论
songxue77 (楼主) 4年前

能不能更改外部api,让他一次性能传递多个数据呢

4年前 评论
songxue77 (楼主) 4年前

pcntl_fork() 去了解下

4年前 评论

队列(任务多开)+redis锁(防止重复问题),基本问题不大,完全够用

4年前 评论

讨论应以学习和精进为目的。请勿发布不友善或者负能量的内容,与人为善,比聪明更重要!