我有一个脚本,可以运行数年(如果不是数千个)来自多年备份的表,以更改人们要求的内容。
截至本月,它开始使用如此多的RAM,服务器将其杀死。
那么,每次从数据库中拉出后,有没有办法,在我拉出它并让我的perl脚本运行数据后,搜索要更改的信息,它会在拉出下一个之前重置RAM?
所以基本上它会重置它刚刚提取的数据,所以所有这些都不在RAM中?
我在2007年创造了这个东西,我当然希望我做得与众不同,但这需要进行大修和更新,而现在我无法做到这一点。或许在将来。但截至目前,我不能那样做。
所以我需要一种方法来防止这种情况发生。
如果你知道如何冲洗那段记忆,请告诉我。
如果你能指出我的话,请提前感谢你。
---更新----添加了部分实际提取所有数据的网站代码----
$_startingTime = time();
$dbh->do(qq{update `tun_changes` set `status` = "working", `startedT` = ? where `unid` = ?}, undef, $_startingTime, $_tr->{unid});
$sth2 = $dbh->prepare(qq{select * from `allmembers` where `mId` = ?});
$sth2->execute($_tr->{mId});
$_tregmem = $sth2->fetchrow_hashref();
$sth2->finish();
$dbh->do(qq{delete from `sessions` where `id` = "$_tregmem->{last_sessid}"}); #kill their logged in session, so it does not lock them up if they are logged in when we change their username...
$_un = $_tregmem->{tusername};
$_cust_id = $_tr->{mId};
$_notfinished = 0;
$_newUname = lc($_tr->{newun});
$_csusername = $_tr->{csusername};# if customer service rep...
$_count = 0;
$_changeFailed = 0;
$_changed = 0;
$_debugChg = 1;
if($_debugChg) {
open(DBG,">>/home$_website_username/required/files/urgent/all_chg_uname_debug_track.txt");
seek(DBG,0,2);
print DBG "Checking Table at " . Format_Date_For_Viewing($_startingTime,"") . " - indented two lines at least for this record.... until _end_ is shown\n";
}
@_mb = $dbh->tables;
$_tablesAffected = 0;
foreach $_k (@_mb) {
$_sql = "select * from $_k";
if($_debugChg) {
print DBG "\tTable $_k (" . duration(time() - $_startingTime) . " into action)\n";
}
$sth2 = $dbh->prepare($_sql);
$sth2->execute();
@_mb2 = @{$sth2->{NAME}};
foreach $_k2 (@_mb2) {
if($_debugChg) {
print DBG "\t\tColumn $_k2";
}
if($_k2 =~ /tusername/i) {
if($_debugChg) {
print DBG "\t Checking for username entries in $_k2\n";
}
$_updated = $dbh->do(qq{update $_k SET `$_k2` = ? WHERE `$_k2` = ?}, undef, $_newUname, $_un);
if($_updated) {
$_changed += $_updated;
} else {
$_changeFailed += $_updated;
}
} else {
if($_debugChg) {
print DBG " - Not UN\n";
}
}
}
$sth2->finish();
if($_debugChg) {
print DBG "\n";
}
}
if($_debugChg) {
print DBG "\nFinished - Duration was: " . duration(time() - $_startingTime) . "\n";
print DBG "\n_end_\n\n";
close(DBG);
}
我离开了调试代码,以便在出现故障时可以看到已完成的操作。
答案 0 :(得分:1)
这里的问题不是很明显。看起来没有明显的内存泄漏,因此尝试释放已分配的内存可能不是您需要解决的问题。 Perl理论上可以在运行时将内存释放回操作系统,但是必须满足某些条件,根据我的经验,它并不常见。
连接到数据库后设置$dbh->{mysql_use_result} = 1;
可能会有很大帮助。
这改变了DBI(实际上,libmysqlclient)从套接字读取传入数据的方式,导致它在每次调用fetchrow*
方法时根据需要读取数据,而不是先将它全部缓冲到内存中,然后交给你从内存中已有的一行开始,这是默认行为。
DBD::mysql
并且底层库可能是负责大量内存使用的实体,所以这值得一试。
答案 1 :(得分:0)
如果不知道至少有关程序的组织方式,那真的很难说。
我认为您将数据库中的数据读入合适的数据结构。我还想象一下,当您继续处理时,您会将新数据读入新的数据结构中。保留所有记忆,我不知道如何摆脱它。我想你不能;据我所知,Perl在运行时不会将内存返回给操作系统。以下是一些选项,可以防止程序的内存占用增长。
要做的第一件事是重用数据结构。处理完一组数据后,将新数据读入相同的数组或散列(或任何使用的数据)。如果您使用复杂的数据结构,则可以重复使用它们的组件。
从您的问题看来,这可能构成了太多的变化。如果是这样的话,我可以想到的另一件事是将代码的适当部分包装到subs中,这样一旦完成处理,它就会超出范围并释放内存。请注意,内存仍将与解释器一起使用,但它可以重新用于下一个这样的组合子,因此您的足迹不会增长。这种改变可能更可行。
如果你的内存需求增长是由于一些简单的事实,即只有更多的数据进入一些数据结构,这是另一个想法。一次只读取(获取,查询)一大块数据,然后当您完成处理时,将下一个块读入同一个数组等。这可能会也可能不会影响数据库读取性能,但会导致内存减少该计划的足迹。
更新(已发布代码)我仍然无法分辨大多数内存的去向,但以下内容可能是合理的。似乎增加的原因是一些数据结构(数组?)随着数据的增长而变得越来越大。在这种情况下,上面的最后一个选项可能是合适的。
答案 2 :(得分:0)
可能有用的一件事是,SQL语句"select * from $_k"
仅用于获取列的名称。您可能希望将其更改为"select * from $_k LIMIT 0"
,以便不会检索任何数据。您还应该在$sth2->finish
循环之前移动for
,尽管LIMIT 0
修复位置会产生很小的差异
结果代码如下所示
$_sql = "select * from `$_k` LIMIT 0";
if ( $_debugChg ) {
print DBG "\tTable $_k (" . duration( time() - $_startingTime ) . " into action)\n";
}
$sth2 = $dbh->prepare($_sql);
$sth2->execute;
@_mb2 = @{ $sth2->{NAME} };
$sth2->finish;
我不知道你是使用在文件顶部或包变量中声明的my
变量(用our
声明,或者如果你没有{{1}则只是未声明可能值得在尽可能最严格的范围内用use strict 'vars'
声明所有内容(这是标准的最佳实践)。这样每个变量都是临时的,并且会在块的末尾被销毁。如果没有看到完整的程序,很难说更多。