如何解决Yii2中Kartik ExportMenu的内存溢出问题
问题描述
触发内存耗尽错误:
PHP Fatal Error – yii\base\ErrorException
Allowed memory size of 373293056 bytes exhausted (tried to allocate 163840 bytes)
使用kartik-v的ExportMenu::widget导出数据,传入ActiveDataProvider后出现上述问题,已排查常规代码优化思路,需针对Yii2框架及ExportMenu特性的解决方案。
用户代码如下:
数据提供者代码
$query = MyModel::find()->with(['A', 'B'])->alias('q'); $dataProvider = new ActiveDataProvider([ 'query' => $query, 'sort' => [ 'defaultOrder' => ['id' => SORT_DESC], 'attributes' => [ 'id', ..., 'created_at' => [ 'asc' => ['q.id' => SORT_ASC], 'desc' => ['q.id' => SORT_DESC], ], ] ] ]);
视图层代码
<?= ExportMenu::widget([ 'dataProvider' => $dataProvider, 'columns' => $gridColumns, 'filename' => "fname" . date('Y_m_d_h_m_s') ]);
解决方案
1. 限制查询字段(优先推荐)
ActiveRecord默认加载全量字段,关联模型也会读取所有数据,这是内存占用大的核心原因。修改查询仅保留导出需要的字段:
$query = MyModel::find() ->select(['id', 'col1', 'col2', 'created_at']) // 替换为实际需导出的主模型字段 ->with([ 'A' => function($q) { $q->select(['id', 'a_col1', 'a_col2']); // 关联模型A需导出的字段 }, 'B' => function($q) { $q->select(['id', 'b_col1']); // 关联模型B需导出的字段 } ]) ->alias('q');
2. 启用ExportMenu分批处理
kartik-v的ExportMenu支持batchSize参数,分批读取数据导出,避免一次性加载全量数据到内存:
<?= ExportMenu::widget([ 'dataProvider' => $dataProvider, 'columns' => $gridColumns, 'filename' => "fname" . date('Y_m_d_h_m_s'), 'batchSize' => 1000, // 每批处理1000条,可根据服务器配置调整 ]);
3. 临时调整PHP内存限制(治标方案)
若代码优化后仍有内存不足,可在导出动作开头临时提高内存限制:
// 在控制器的导出动作中添加 ini_set('memory_limit', '512M'); // 可根据实际情况调整为1G等更高值
4. 改用数组数据代替ActiveDataProvider
针对超大量数据,直接用SQL查询获取数组,避免ActiveRecord对象的额外内存开销:
// 控制器中构建SQL查询并获取数组数据 $sql = "SELECT q.id, q.col1, A.a_col1, B.b_col1 FROM my_model q LEFT JOIN model_a A ON q.a_id = A.id LEFT JOIN model_b B ON q.b_id = B.id ORDER BY q.id DESC"; $data = Yii::$app->db->createCommand($sql)->queryAll(); // 转换为ArrayDataProvider $arrayDataProvider = new \yii\data\ArrayDataProvider([ 'allModels' => $data, ]); // 视图中使用ArrayDataProvider <?= ExportMenu::widget([ 'dataProvider' => $arrayDataProvider, 'columns' => $gridColumns, 'filename' => "fname" . date('Y_m_d_h_m_s'), ]);
5. 移除不必要的关联加载
如果导出列中不需要某些关联模型的数据,直接删除with(['A', 'B'])中对应的关联项,减少内存占用。
内容的提问来源于stack exchange,提问作者Tayyab Hussain
相关产品推荐
相关产品推荐

