使用PHP&;MySQL。。。如何释放内存?
要求: 我们在两台服务器中有两个类似的表。服务器中第一个具有唯一键列A、B、C的表,我们将表1行插入到具有唯一键列B、C、D的表2中 由于不同的唯一键列约束,表1将有大约500万行,表2将插入大约300万行 这里的要求是从表1中获取所有行,如果表2中不存在相同的记录,则插入到表2中,如果记录匹配,则增加计数并更新表2中的“cron_modified_date”列 对于这个设置,PHP版本是5.5,MySQL版本是5.7,DB服务器有6GB的RAM 当在脚本下执行时,在处理了200万条记录之后,处理速度变得非常慢,RAM没有释放出来,有时所有的RAM都被脚本消耗掉,之后脚本就根本不处理了 如您所见,我正在重置变量并关闭DB连接,但这并没有释放DB服务器RAM。经过一些阅读,我了解到,可能是PHP垃圾收集需要手动调用来释放资源,但它也不能释放RAM 我在这里做错了什么,以及如何使用PHP、MYSQL处理数百万条记录 在执行脚本时,是否有其他方法释放RAM,以便脚本能够与执行竞争使用PHP&;MySQL。。。如何释放内存?,php,mysql,performance,memory-leaks,database-performance,Php,Mysql,Performance,Memory Leaks,Database Performance,要求: 我们在两台服务器中有两个类似的表。服务器中第一个具有唯一键列A、B、C的表,我们将表1行插入到具有唯一键列B、C、D的表2中 由于不同的唯一键列约束,表1将有大约500万行,表2将插入大约300万行 这里的要求是从表1中获取所有行,如果表2中不存在相同的记录,则插入到表2中,如果记录匹配,则增加计数并更新表2中的“cron_modified_date”列 对于这个设置,PHP版本是5.5,MySQL版本是5.7,DB服务器有6GB的RAM 当在脚本下执行时,在处理了200万条记录之后,处
/* Fetch records count for batch insert*/
$queryCount = "SELECT count(*) as totalRecords FROM TABLE1 where created_date > = '2018-02-10'";
$rowsCount = $GLOBALS['db']->execRaw( $queryCount)->fetchAll();
$recordsPerIteration = 50000 ;
$totalCount = $rowsCount[0]['totalRecords'];
$start = 0;
gc_disable() ;
if ( $totalCount > 0 ) {
while ( $totalCount > 0 ) {
$query = "SELECT * FROM TABLE1
WHERE where created_date > = '2018-02-10'
ORDER BY suggestion_id DESC
LIMIT ".$start.",".$recordsPerIteration;
print "sql is $query" ;
$getAllRows = $GLOBALS['db']->execRaw( $query )->fetchAll();
$GLOBALS['db']->queryString = null;
$GLOBALS['db']->close() ;
foreach ($getAllRows as $getRow) {
$insertRow = " INSERT INTO TABLE2 (
Name,
Company,
ProductName,
Status,
cron_modified_date)
VALUE (
".$GLOBALS['db_ab']->quote($getRow['Name']).",
".$GLOBALS['db_ab']->quote($getRow['Company']).",
".$GLOBALS['db_ab']->quote($getRow['ProductName']).",
".$getRow['Status'].",
".$GLOBALS['db_ab']->quote($getRow['created_date'])."
)
ON DUPLICATE KEY UPDATE count = (count + 1) , cron_modified_date = '".$getRow['created_date']."'" ;
$GLOBALS['db_ab']->execRaw( $insertRow ) ;
$GLOBALS['db_ab']->queryString = null;
$getRow = null;
$insertRow = null;
$GLOBALS['db_ab']->close() ;
}
gc_enable() ;
$totalCount = $totalCount- $recordsPerIteration;
$start += $recordsPerIteration ;
$getAllRows = null;
gc_collect_cycles() ;
}
}
解决方案
根据@ABelikov提供的建议和一些命中跟踪方法。。。最后,下面的代码工作得非常好,在每插入50K条记录后释放RAM 以下是主要调查结果
- 在每次涉及大数据操作的主要操作后释放DB connections变量,并重新连接DB,以便刷新DB缓冲区
- 插入语句并一次性执行插入。不要在循环中执行单记录插入
谢谢大家的宝贵建议和帮助
/* Fetch records count for batch insert*/ $queryCount = "SELECT count(*) as totalRecords FROM TABLE1 where created_date > = '2018-02-10'"; $rowsCount = $GLOBALS['db']->execRaw( $queryCount)->fetchAll(); $recordsPerIteration = 50000 ; $totalCount = $rowsCount[0]['totalRecords']; $start = 0; if ( $totalCount > 0 ) { while ( $totalCount > 0 ) { $query = "SELECT * FROM TABLE1 WHERE where created_date > = '2018-02-10' ORDER BY suggestion_id DESC LIMIT ".$start.",".$recordsPerIteration; print "sql is $query" ; $getAllRows = $GLOBALS['db']->execRaw( $query )->fetchAll(); $GLOBALS['db']->queryString = null; $GLOBALS['db']->close() ; $insertRow = " INSERT INTO TABLE2 ( Name, Company, ProductName, Status, cron_modified_date) VALUE ( " ; foreach ($getAllRows as $getRow) { $insertRow .= (".$GLOBALS['db_ab']->quote($getRow['Name']).", ".$GLOBALS['db_ab']->quote($getRow['Company']).", ".$GLOBALS['db_ab']->quote($getRow['ProductName']).", ".$getRow['Status'].", ".$GLOBALS['db_ab']->quote($getRow['created_date'])."),"; } $insertRow=rtrim($insertRow,','); // Remove last ',' $insertRow.= " ON DUPLICATE KEY UPDATE count = (count + 1) , cron_modified_date = '".$getRow['created_date']."'" ; $GLOBALS['db_ab']->execRaw( $insertRow ) ; //Flushing all data to freeup RAM $GLOBALS['db_ab'] = null ; $GLOBALS['db'] = null ; $insertRow = null; $totalCount = $totalCount- $recordsPerIteration; $start += $recordsPerIteration ; $getAllRows = array(); $getAllRows = null; print " \n Records needs to process ".$totalCount."\n"; } }
$insertRow = " INSERT INTO TABLE2 (
Name,
Company,
ProductName,
Status,
cron_modified_date) VALUES ";
foreach ($getAllRows as $getRow) {
$insertRow.="(".$GLOBALS['db_ab']->quote($getRow['Name']).",
".$GLOBALS['db_ab']->quote($getRow['Company']).",
".$GLOBALS['db_ab']->quote($getRow['ProductName']).",
".$getRow['Status'].",
".$GLOBALS['db_ab']->quote($getRow['created_date'])."),";
}
$insertRow=rtrim($insertRow,','); // Remove last ','
$insertRow .= " ON DUPLICATE KEY UPDATE count = (count + 1) , cron_modified_date = '".$getRow['created_date']."'" ;
$GLOBALS['db_ab']->execRaw( $insertRow ) ;
$GLOBALS['db_ab']->queryString = null;
$getRow = null;
$insertRow = null;
$GLOBALS['db_ab']->close() ;
只有当您的foreach“body”通常运行不止一次时,这才有帮助
2.MySQL服务器端解决方案
尝试使用事务处理
只需在脚本开始时开始一个脚本,并在脚本结束时提交。
取决于你的服务器,这真的很有帮助。
但是要小心!这取决于您的MySQL服务器配置集。需要测试。- PHP在内存方面没有实用的上限;MySQL是这样的
- 大部分时间用于在MySQL和PHP之间传输数据
INSERT INTO table2
(Name, Company, ProductName, Status, cron_modified_date, count)
SELECT Name, Company, ProductName, Status, created_date, 1
FROM table1
ON DUPLICATE KEY UPDATE
count = count + 1
cron_modified_date = created_date;
请注意,在某些更新中可能需要使用伪函数VALUES()
这避免了将所有(甚至5000)行提取到PHP中,这可能是内存问题的根源。PHP中的一个简单变量大约需要40个字节。MySQL的设计目的是在不破坏RAM的情况下处理任意数量的行。@AtaurRahman:是的,你是对的,但他有关于重复密钥更新的
。
声明。@SalimIbrogimov,这有什么关系?正如我所知,在重复密钥更新上
可以很好地工作。@AtaurRahman:在重复密钥更新计数=(计数+1),cron_modified_date=“$getRow['created_date']。”;
为什么要使用gc_disable();?是否使用PDO或MySQLi作为数据库层?是的,我正在使用PDO,在调用gc_collect_cycles()之前;,我正在禁用GC。INSERT…SELECT
不是这种情况的答案。“我们在两台服务器中有两个类似的表”@RaymondNijland:是的,同意。不知道我是如何丢失它的。我该怎么办?删除答案?编辑问题您的答案中有3个选项?