在使用laravel执行长时间命令行脚本操作数据会导致内存溢出,辛苦各位同学指点
laravel版本:6.20.19 , 8.35.1
PHP版本:7.3.16 , 7.4.16
操作系统:macOS 11.2.3 , centos7
脚本类型:artisan console command
问题描述:
我需要一个长时间监控某项数据的脚本,但是在使用过程中出现内存泄露,尝试了以下方法:
- 销毁了所有的不再使用的变量
- 使用模型静态方法:firstOrCreate 之前使用了unsetEventDispatcher
- DB的insert之前使用了unsetEventDispatcher;
- 原生PDO操作
- 修改了flare.php将所有的true都改成了false
- 使用事件机制,在监听器中创建db对象,执行sql操作,销毁db对象,销毁事件对象
发现内存一直都在增加,没有得到释放,但是不使用框架时,使用PDO操作,内存得到释放,控制的很好。
后面根据 @振翅飞翔 的评论提醒下,删除了 composer remove --dev facade/ignitio 也没有变化,不过却让我意识到可能还是日志的问题,于是把代码里面的Log调用注释掉,发现内存稳定了。
但是仍旧不知道应当如何解决此问题,因为在项目中日志是必要的。
目前基本的结论是:
- 数据库操作确实会导致内存溢出
- Log组件也会导致内存溢出
以下是示例代码:
while(true){
$signal_record = [
'column1' => mt_rand(1,200),
'column2' => 'testB',
'type' => 'LIMIT',
'signal' => 'IN',
'opening_time' => date('Y-m-d H:i:s'),
'closing_time' => date('Y-m-d H:i:s'),
'strategies_name' => 'B',
];
$this->info(date('Y-m-d H:i:s') . ' 内存占用' . memory_get_usage());
TradeSignalRecords::unsetEventDispatcher();
$r = TradeSignalRecords::create($signal_record);
//$r = ModelsTradeSignalRecords::firstOrNew($signal_record);
Log::debug( ' 发现信号 ' .json_encode($signal_record));
$r = null;
$this->info(date('Y-m-d H:i:s') . ' 内存占用' . memory_get_usage());
sleep(1);
}
使用DB操作的示例代码
while(true){
$signal_record = [
'column1' => mt_rand(1,200),
'column2' => 'testB',
'type' => 'LIMIT',
'signal' => 'IN',
'opening_time' => date('Y-m-d H:i:s'),
'closing_time' => date('Y-m-d H:i:s'),
'strategies_name' => 'B',
];
$this->info(date('Y-m-d H:i:s') . ' 内存占用' . memory_get_usage());
$connection_name = 'test';
$table_name = 'trade_records';
$connection = DB::connection($connection_name)->table($table_name);
$connection->getConnection()->unsetEventDispatcher(); // https://learnku.com/laravel/t/52660?order_by=vote_count
$this->_connection->insert($signal_record);
Log::debug( ' 发现信号 ' .json_encode($signal_record));
$connection = null;
$signal_record = null;
$connection_name = null;
$table_name = null;
$this->info(date('Y-m-d H:i:s') . ' 内存占用' . memory_get_usage());
sleep(1);
}
使用PDO版本代码示例,此代码在laravel command中内存没有回收,但是不用框架时内存得到了释放
while(true){
echo date('Y-m-d H:i:s') . ' 内存占用' . memory_get_usage() . "\r\n";
$conn = new \PDO("mysql:host=localhost;dbname=test", 'root', '123456');
$conn->setAttribute(\PDO::ATTR_ERRMODE, \PDO::ERRMODE_EXCEPTION);
$sql = "INSERT INTO trade_records (column1, `column2`, type,`signal`,opening_time,closing_time,strategies_name) VALUES (" . mt_rand(1,100). ", 'testB','LIMIT','IN','2021-04-01 19:15:00','2021-04-01 19:15:00','B')";
// 在laravel框架中使用下面这句日志记录
// Log::debug( ' 发现信号 ' .json_encode($signal_record));
$r = $conn->exec($sql);
$conn = null;
$r = null;
$sql = null;
sleep(1);
}
参考资料:
有什么好办法解决进程内存一直增长
DB 添加数据如何实时释放掉内存?
PHP 常驻任务不会释放内存的吗?
Laravel 控制台程序 循环执行 ORM 查询 内存溢出
记一次laravel 内存泄漏
【PHP内存泄漏案例】PHP对象递归引用造成内存泄漏
Laravel7使用日志内存会一直往上增长最后导致内存泄漏
关于 LearnKu
$this->info(date('Y-m-d H:i:s') . ' 内存占用' . memory_get_usage());
这个是输出到哪里
把 connection 操作放到 while 外部去吧
sleep 1s 的话, 直接 crontab 管理而不是 while true 会不会好点, 这样每次执行完都能清理掉内存
盲猜是这个问题?问答:Laravel7使用日志内存会一直往上增长最后导致泄漏,求大佬们指教~
这是两个不一样的模型对象,所以第二个模型依然会使用事件。
把
TradeSignalRecords::unsetEventDispatcher();改成DB::connection()->unsetEventDispatcher();你这是 一个常驻内存的进程了,所以得用supervisor管理,把whiler(true)写到artisan命令里,然后用supervisor去监控这个srtisan就可以了
最近刚好也在纠结脚本长时间运行内存溢出的问题,我的场景是 kafka 消费场景,两套一模一样的代码一个消费数据量小就没有任何问题,另一个消费数据量大内存就在不断的缓慢增长,排疑目前也没有什么好的思路。
你改这种常驻内存型代码不如开个定时任务来做,又没差在哪里
@lidongyoo crontab最多是一分钟执行一次,他这个是要秒级的监控
@lidongyoo @Complicated 更多时候是因为业务耗时比较长,只能单线程操作。
@Complicated github.com/spatie/laravel-short-sc...
@Where 为什么耗时长只能单线程?不知道你在说什么
如果非要 while true 的话,可以参考鸟哥的文章:PHP CLI模式下的多进程应用
@lidongyoo
不想用 crontab 可以装一个毫秒级的定时器包啊。我记得有的啊。while(true)确实不好。
@huanbird 毫秒级的定时包也得有workman 或者swoole之类的辅助工具才能实现毫秒级的吧