仅在实时服务器上升级到 PHP 7.1 后 CSV 导入超级慢
CSV Import Super Slow after upgrading to PHP 7.1 only on live server
我们将生产服务器升级到新服务器,我们从使用 PHP 5.6 到 PHP 7.1,我们在上面托管了一个 Drupal 7 网站。在大多数情况下,该站点似乎运行正常。服务器移动后我看到的问题是 CSV 导入与以前相比需要很长时间。 csv 导入将每一行添加到数据库中。
$fp = fopen($file_path, 'r');
if ($fp === FALSE) {
// Failed to open file.
watchdog('ea_test', 'Failed to open %file_path', array('%file_path' => $file_path));
$context['finished'] = TRUE;
return;
}
fseek($fp, $context['sandbox']['offset']);
for ($i = 0; $i < $limit; $i++) {
$line = fgetcsv($fp);
if ($line == FALSE) {
$done = TRUE;
}
// If first line just skipping ahead.
elseif ($context['sandbox']['records'] < 1) {
$context['sandbox']['records']++;
}
else {
try {
db_insert('ea_csv_test')
->fields(array(
'agent_number' => $line[0],
'total_signatures' => $line[1],
'template_number' => $line[2],
'photo' => $line[3] ? 1 : 0,
'total_views' => $csv_line[4],
'total_clicks' => $csv_line[5],
'annual_cost' => $csv_line[7],
'monthly_cost' => $csv_line[8],
'popular_button' => $csv_line[9],
'month' => $csv_line[10],
'asof' => date('Y-m-d 00:00:00'),
))->execute();
}
catch (Exception $e) {
// By setting the databse error it will fail and display the last bad
// record. Normally I would log/email this but I think it
// will send too many. More than likely if one is bad they all
// will be bad.
$database_insert_error = $e->getMessage();
$error = TRUE;
}
// Set the current position of the file so it starts from there.
$context['sandbox']['offset'] = ftell($fp);
$context['sandbox']['records']++;
}
}
$eof = feof($fp);
事情发生在本地和我的暂存站点上,我无法复制该问题。唯一的区别是我在本地使用 PHP 7.1.17,在生产环境使用 PHP 7.1.25。 local 和 prod 都在使用 mariadb 5.5.5-10.2.19-MariaDB-log.
时间比较:
本地/暂存处理需要 9.51 秒。
生产需要 276.12 秒来处理。
编辑
我 运行 直接从数据库导入(没有任何 PHP),速度非常快。在我看来,它必须是某种 php 设置:
truncate TABLE ea_csv_test;
LOAD DATA INFILE '/tmp/test.csv' into table ea_csv_test
FIELDS TERMINATED BY ','
ENCLOSED BY '"'
LINES TERMINATED BY '\n'
IGNORE 1 ROWS;
编辑 2
注意到未启用生产 APCu。我启用了它,它有点帮助。它从 276 秒变为 166 秒。
我开始比较 innodb 设置,我发现我在本地将 innodb_flush_log_at_trx_commit
设置为 2,但在生产中它设置为 1。一旦我在生产中将其更改为 2,它就解决了我的问题。
根据我的理解,如果设置为 2,它会每秒记录一次,而不是任何事务。最坏的情况是,如果服务器出现故障,我们可能会丢失 1 秒的数据。就我而言,如果我们正在导入并且服务器出现故障,我无论如何都需要重新上传,因为导入永远不会完成。我认为如果我们正在进行货币交易,将值保持在 1 是有意义的。
我们将生产服务器升级到新服务器,我们从使用 PHP 5.6 到 PHP 7.1,我们在上面托管了一个 Drupal 7 网站。在大多数情况下,该站点似乎运行正常。服务器移动后我看到的问题是 CSV 导入与以前相比需要很长时间。 csv 导入将每一行添加到数据库中。
$fp = fopen($file_path, 'r');
if ($fp === FALSE) {
// Failed to open file.
watchdog('ea_test', 'Failed to open %file_path', array('%file_path' => $file_path));
$context['finished'] = TRUE;
return;
}
fseek($fp, $context['sandbox']['offset']);
for ($i = 0; $i < $limit; $i++) {
$line = fgetcsv($fp);
if ($line == FALSE) {
$done = TRUE;
}
// If first line just skipping ahead.
elseif ($context['sandbox']['records'] < 1) {
$context['sandbox']['records']++;
}
else {
try {
db_insert('ea_csv_test')
->fields(array(
'agent_number' => $line[0],
'total_signatures' => $line[1],
'template_number' => $line[2],
'photo' => $line[3] ? 1 : 0,
'total_views' => $csv_line[4],
'total_clicks' => $csv_line[5],
'annual_cost' => $csv_line[7],
'monthly_cost' => $csv_line[8],
'popular_button' => $csv_line[9],
'month' => $csv_line[10],
'asof' => date('Y-m-d 00:00:00'),
))->execute();
}
catch (Exception $e) {
// By setting the databse error it will fail and display the last bad
// record. Normally I would log/email this but I think it
// will send too many. More than likely if one is bad they all
// will be bad.
$database_insert_error = $e->getMessage();
$error = TRUE;
}
// Set the current position of the file so it starts from there.
$context['sandbox']['offset'] = ftell($fp);
$context['sandbox']['records']++;
}
}
$eof = feof($fp);
事情发生在本地和我的暂存站点上,我无法复制该问题。唯一的区别是我在本地使用 PHP 7.1.17,在生产环境使用 PHP 7.1.25。 local 和 prod 都在使用 mariadb 5.5.5-10.2.19-MariaDB-log.
时间比较:
本地/暂存处理需要 9.51 秒。
生产需要 276.12 秒来处理。
编辑
我 运行 直接从数据库导入(没有任何 PHP),速度非常快。在我看来,它必须是某种 php 设置:
truncate TABLE ea_csv_test;
LOAD DATA INFILE '/tmp/test.csv' into table ea_csv_test
FIELDS TERMINATED BY ','
ENCLOSED BY '"'
LINES TERMINATED BY '\n'
IGNORE 1 ROWS;
编辑 2
注意到未启用生产 APCu。我启用了它,它有点帮助。它从 276 秒变为 166 秒。
我开始比较 innodb 设置,我发现我在本地将 innodb_flush_log_at_trx_commit
设置为 2,但在生产中它设置为 1。一旦我在生产中将其更改为 2,它就解决了我的问题。
根据我的理解,如果设置为 2,它会每秒记录一次,而不是任何事务。最坏的情况是,如果服务器出现故障,我们可能会丢失 1 秒的数据。就我而言,如果我们正在导入并且服务器出现故障,我无论如何都需要重新上传,因为导入永远不会完成。我认为如果我们正在进行货币交易,将值保持在 1 是有意义的。