PHP排序本身不耗内存,真正吃内存的是全量加载数据到数组再排序;应优先用数据库ORDER BY、分块处理、精简数据字段并显式unset。

PHP排序本身不耗内存,真正吃内存的是「把全部数据加载进数组再排」——尤其是用 usort、sort 处理几万行数据库结果或大文件解析后的数组时,内存会瞬间翻倍。必须切断“全量加载→全量排序”这个链路。
为什么 sort/usort 会让内存暴涨
PHP数组底层是哈希表,每个元素带类型标记、引用计数、键结构等元数据。一个含10万个关联数组的 $rows,实际内存占用可能是原始JSON或DB记录的3–5倍。而 sort() 或 usort() 在排序过程中还会额外申请临时空间做比较、交换,峰值内存常达初始值的1.5倍以上。
- 常见错误:先
$data = $pdo->query($sql)->fetchAll();,再usort($data, $callback); - 更糟情况:
json_decode(file_get_contents('big.json'), true)后直接usort()—— 文件内容+解析后数组+排序临时结构三重叠加 - 注意:
array_multisort()同样全量加载,不解决根本问题
用数据库原生排序代替 PHP 排序
只要排序字段在数据库里,就别把数据捞出来再排。99% 的场景下,SQL 的 ORDER BY 更快、更省内存,且不占 PHP 进程资源。
- 把
SELECT * FROM user改成SELECT id,name,created_at FROM user ORDER BY created_at DESC LIMIT 1000 - 避免
ORDER BY RAND()这类低效写法;如需随机抽样,改用应用层随机 ID 查询或TABLESAMPLE(PostgreSQL) - 确保排序字段有索引,否则 DB 排序也会变慢甚至磁盘临时表溢出
大数据量必须用 PHP 排序时,改用生成器 + 分块
如果业务逻辑强制要求 PHP 层排序(比如按自定义权重函数、混合多种来源数据),就不能一次性全 load。得拆成流式处理。
立即学习“PHP免费学习笔记(深入)”;
- 用生成器逐条读取并写入临时文件:
yield每行数据,避免fetchAll() - 分块排序:把 10 万条数据按 5000 条一组读入、排序、写入临时文件,最后用外部归并(如
proc_open('sort -k3'))合并 - 内存敏感场景可用
SplFixedArray替代普通数组存储纯整数/短字符串数据,减少哈希开销(但不支持usort,得手写插入排序)
检查并清理排序前的冗余数据
很多内存浪费不是排序本身导致的,而是排序对象里混了不该有的东西。比如 Eloquent 模型调用 toArray() 后,每个元素都带完整关系数据、访问器、隐藏字段。
- Laravel 中改用
select('id','name','score')->get()->toArray(),而不是Model::get()->toArray() - Yii2 中用
(new Query)->select(['id','name'])->from('user')->all(),避免ActiveRecord::find()->all() - 排序前用
array_map(fn($x) => ['id' => $x['id'], 'score' => $x['score']], $rows)抽离最小字段集,再usort()
最易被忽略的一点:排序操作本身不会触发 GC,但大数组长期持有引用会阻塞回收。哪怕你只在函数内排序,若该数组来自全局缓存、静态变量或闭包捕获,unset() 必须显式写,不能依赖作用域结束。



















