核心差距在于内存结构、属性访问机制和运行时开销:asArray()是纯数据管道,直接映射为关联数组,无对象开销;ActiveRecord则触发完整实体生命周期,含属性绑定、事件、关联加载等,12.4万行时性能差异显著。

核心差距在内存结构、属性访问机制和运行时开销上,不是“ORM慢”,而是两种路径做了完全不同的事。
asArray() 是纯数据管道
它绕过整个 ActiveRecord 类体系,直接从 PDOStatement 读取原始结果集,逐行映射为 PHP 关联数组。没有对象实例化、没有属性绑定、不触发 getter/setter、不加载行为(Behaviors)、不初始化关联关系。本质上就是 数据库字段 → 字符串/数字 → 数组键值对 的线性搬运。
- 每行数据只分配一个数组结构,内存连续、哈希表小、无动态扩容压力
- 字段类型保持数据库返回的原始字符串形式(这也是为什么 asArray 默认全是字符串)
- 适合只读、导出、缓存、前端透传等场景
ActiveRecord 对象方式是完整实体生命周期
每一条记录都会创建一个完整的 AR 实例,触发整套框架逻辑:
- 构造函数执行、属性初始化、事件触发(如 init())
- 每个属性写入都走 PHP 对象的 properties_table(哈希表),动态插入带来 rehash 开销
- 私有/受保护属性需通过魔术方法 __set/__get 访问,比数组下标慢数倍
- 关联数据未预加载(with)时,后续调用会触发 N+1 查询;即使预加载,也需构建嵌套对象树
- 验证规则、场景控制、getter/setter 逻辑、行为附加方法全部就位,随时可 save() 或触发事件
124k 行放大了底层差异
PHP 对象的 properties_table 在频繁写入未声明属性时,会反复扩容、rehash、迁移数据——这在单个对象上不明显,但乘以 12.4 万次,就成了主要瓶颈。而数组是连续内存块,写入即定位,无哈希计算、无冲突解决、无内存重排。
- 实测中,AR 对象创建本身占总耗时 60% 以上,远超 SQL 执行时间
- 如果你不需要调用
$model->save()、$model->beforeSave、$model->getXXX()等行为,那用对象就是过度设计 - 即便只做简单遍历输出,AR 的 __get 魔术方法调用成本也比数组 $row['name'] 高 3–5 倍
该选哪个,取决于你要做什么
如果只是查出来给前端渲染列表、导出 Excel、写日志、生成统计报表,asArray() 完全够用且更合理。若需校验、修改、保存、触发业务逻辑、复用模型层封装(比如自动格式化金额、拼接用户名),才值得承担对象开销。
- 需要类型安全?用
asArray()后手动 cast(参考 schema 转换逻辑) - 需要部分对象能力?考虑组合:asArray() + 自定义 DTO 类,或只 new 一次空模型调用
populateRecord() - 真要批量操作又想保留 AR 行为?改用事务 + 批量 insert/update,避免逐条 new + save


















