应优先用 asarray() + 显式 select() 减少内存,格式化用纯函数避免重操作;超大数据改用 batch 分批或 pdo 原生查询以平衡内存与 cpu。

直接用 asArray() 查 10 万行再 array_map 格式化,内存压得低,但 CPU 会明显上升——这不是非此即彼的选择,而是要控制“查多少”和“怎么改”两个关键点。
查出来就别留对象,asArray 是底线
ActiveRecord 每个模型实例默认带行为、事件监听、属性变更追踪等开销。10 万行若不用 asArray(),光是构造对象就能吃掉 200MB+ 内存,还可能触发 GC 频繁停顿。
必须写成:
Test::find()->select(['id', 'name', 'status'])->asArray()->all()- 显式
select()只取真正需要的字段,避免*带来冗余列和类型转换开销 - 不要在
asArray()后又调toArray()或json_encode($model)—— 那等于白转
array_map 不要干重活,拆成轻量级纯函数
array_map 本身不耗内存,但里面做的操作决定 CPU 走多高。比如在回调里调 date()、mb_convert_encoding()、或访问外部服务,10 万次就是 10 万次开销。
推荐做法:
- 把格式化逻辑写成无副作用的纯函数:只做字符串拼接、简单条件判断、数字四舍五入
- 日期处理提前预计算时间戳,避免每次调
date('Y-m-d', $ts);可用strftime()替代(更快) - 状态码映射用预定义数组查表:
$map = [0 => '待处理', 1 => '已完成'],比switch或if-else更快
超大结果集建议分批 + yield 流式处理
即使用了 asArray(),10 万行全 load 到内存仍是风险点(尤其 CLI 脚本)。更稳的方式是放弃 all(),改用流式迭代:
foreach (Test::find()->select(['id', 'name'])->asArray()->batch(500) as $rows) { ... }- 每批处理完立即
unset($rows),配合gc_collect_cycles()主动回收(PHP 7.3+ 更有效) - 如果最终要输出 CSV 或写文件,直接边查边写,不累积中间数组
真要兼顾内存和 CPU?换 PDO 原生查询
当 asArray() + array_map 仍卡 CPU,说明 ActiveRecord 层仍有隐性开销(如查询构建、参数绑定、日志上下文)。此时可退一级:
- 用
Yii::$app->db->createCommand("SELECT id, name FROM test WHERE ...")->queryAll() - 它跳过 AR 元数据解析和行为链,比
ActiveRecord::find()->asArray()->all()快 15–25%,内存也更干净 - 格式化仍走
array_map,但起始数据更“薄”,CPU 峰值更平缓











