
引言
在PHP开发中,处理Excel文件的导入导出是非常常见的需求。当数据量较小时,使用PHPExcel或PhpSpreadsheet等库可以轻松应对。然而,当数据量达到百万级别时,传统的内存读取和写入方式会导致PHP内存溢出(OOM)或执行超时。XlsWriter是一个基于C语言开发的PHP扩展,它以极低的内存占用和极高的性能著称,特别适合处理百万级大数据的导入导出。本文将详细介绍如何使用XlsWriter实现百万级数据的高效导入与导出。
环境准备
在使用XlsWriter之前,需要先安装扩展。可以通过PECL快速安装:
pecl install xlswriter
安装完成后,在php.ini中添加扩展并重启PHP服务:
extension=xlswriter
可以通过命令php -m | grep xlswriter检查是否安装成功。
百万级数据导出
传统导出方式是将所有数据加载到内存中再生成文件,这必然导致内存溢出。XlsWriter采用流式写入(边生成边写磁盘),内存占用始终保持在一个极低的常量级别。在导出百万级数据时,建议将数据分批从数据库中取出并连续写入Excel。
<?php
$config = ['path' => '/tmp/'];
$excel = new VtifulKernelExcel($config);
// 创建文件并初始化Sheet
$fileName = 'million_data_export.xlsx';
$fileObject = $excel->fileName($fileName, 'Sheet1');
// 写入表头
$fileObject->header(['ID', '用户名', '金额', '创建时间']);
// 模拟分批从数据库读取并写入100万条数据
$batchSize = 10000;
$totalRows = 1000000;
for ($i = 0; $i < $totalRows; $i += $batchSize) {
// 实际业务中这里替换为数据库的 limit 偏移查询
// 例如: Db::table('users')->skip($i)->take($batchSize)->get()->toArray();
$batchData = [];
for ($j = 0; $j < $batchSize; $j++) {
$currentId = $i + $j + 1;
if ($currentId > $totalRows) break;
$batchData[] = [
$currentId,
'User_' . $currentId,
mt_rand(100, 9999) / 10,
date('Y-m-d H:i:s')
];
}
// 流式追加数据写入
$fileObject->data($batchData);
}
// 生成文件并获取文件路径
$filePath = $fileObject->output();
// 实际业务中可结合Nginx的X-Accel-Redirect或直接输出流供用户下载
echo '文件已生成: ' . $filePath;百万级数据导入
百万级数据的导入同样面临内存问题。XlsWriter提供了游标式逐行读取的功能,每次只将当前行加载入内存,读取速度极快且内存消耗恒定。对于导入,核心逻辑是逐行读取并分批组装SQL语句入库。
<?php
$config = ['path' => '/tmp/'];
$excel = new VtifulKernelExcel($config);
// 打开目标文件
$fileObject = $excel->openFile('million_data_export.xlsx')
->openSheet();
// 设置读取规则,跳过表头(可选)
$fileObject->setSkipRows(1);
$batchData = [];
$batchSize = 1000; // 每1000条执行一次数据库插入
while (($row = $fileObject->nextRow()) !== NULL) {
// 过滤空行
if (empty($row[0])) {
continue;
}
$batchData[] = [
'id' => $row[0],
'name' => $row[1],
'amount' => $row[2],
'created' => $row[3]
];
// 达到批次数量,执行入库
if (count($batchData) === $batchSize) {
// 实际业务中替换为批量插入操作
// Db::table('users')->insert($batchData);
$batchData = []; // 清空重置
}
}
// 处理尾部不足批次大小的剩余数据
if (!empty($batchData)) {
// Db::table('users')->insert($batchData);
}
echo '数据导入完成';性能优化建议
虽然XlsWriter在IO层面已经做到了极致优化,但在处理百万级数据时,数据库交互依然是最大的瓶颈。请务必注意以下几点:
数据库分批查询:导出时不要一次性
SELECT *查询所有数据,务必使用LIMIT和偏移量分批获取,或者使用游标查询,防止PHP端数据库内存溢出。批量插入(Batch Insert):导入时坚决避免逐条执行
INSERT,应当组装多行数据执行一次INSERT INTO ... VALUES (...), (...), ...,建议每500到1000条执行一次。去除脚本时间限制:百万级数据处理时间可能较长,需在脚本顶部设置
set_time_limit(0),确保脚本不会超时中断。异步处理:如果是面向用户的请求,不要让用户同步等待百万级数据导出或导入完成。建议将任务推入消息队列,后台异步执行,完成后通过通知系统告知用户。
总结
使用PHP原生扩展XlsWriter处理百万级Excel数据是当前最优的解决方案。其流式写入和游标读取机制从根本上解决了内存溢出问题,而底层C语言的实现则保障了极速的运行效率。配合数据库的分批查询与批量插入机制,即使是普通的Web服务器也能轻松应对海量数据的导入导出需求。更多高级用法可参考官方文档(访问 www.ipipp.com 获取最新文档与示例)。