游标分批读取更省内存,因pdo持游标句柄逐行fetch,避免全量加载至php内存;需禁用pdo缓冲、写原生sql、精简字段、fputcsv流式写入,并调大mysql的max_allowed_packet。

游标分批读取为什么比 select * from 更省内存
直接查几百万行进 PHP 内存,PHP 进程瞬间吃光几百 MB,超限被 kill 是常态。Cursor 不是把结果全捞进数组,而是让 PDO 持有数据库连接上的“游标句柄”,每次 fetch() 只拿一行(或小批量),上一行用完就释放。MySQL 的 SELECT 本身支持服务器端游标(需启用 MYSQLI_USE_RESULT 或 PDO 的 PDO::MYSQL_ATTR_USE_BUFFERED_QUERY => false),关键在 ThinkPHP 层要绕过默认的缓冲式查询。
- ThinkPHP 默认用
fetch()+fetchAll(),本质是客户端缓冲,数据全在 PHP 内存里 - 必须显式禁用自动缓冲:在数据库配置加
'pdo_attr' => [PDO::MYSQL_ATTR_USE_BUFFERED_QUERY => false] - 不能调用
count()、paginate()或任何触发全量拉取的方法,否则前功尽弃 - 字段尽量精简,避免
SELECT *,尤其别带大文本字段(TEXT、BLOB)
ThinkPHP 6.x 中正确创建无缓冲查询游标
TP6 的 query() 和原生 Db::query() 都会走缓冲;得退到 PDO 底层,用 Db::getPdo() 拿连接,再手动 prepare() + execute()。注意:不能用 where() 链式调用,那些方法最终都生成缓冲查询。
- 写原生 SQL,用占位符防注入:
$sql = "SELECT id,name,email FROM user WHERE status = ? AND created_time > ?" - 获取 PDO 实例后,设属性:
$pdo->setAttribute(PDO::MYSQL_ATTR_USE_BUFFERED_QUERY, false) $stmt = $pdo->prepare($sql); $stmt->execute([$status, $start_time]);- 循环
while ($row = $stmt->fetch(PDO::FETCH_ASSOC)) { /* 写入 CSV / Excel 流 */ } - 导出中途别调
Db::table()->...->select(),会重置 PDO 属性或新建连接,游标失效
导出时内存不涨但卡住?检查 MySQL 的 net_buffer_length 和 max_allowed_packet
即使 PHP 端用了游标,MySQL 服务端仍可能把整结果集缓存在自己的网络缓冲区里,尤其当某一行特别大(比如 JSON 字段含几 MB 数据),就会卡住或报错 Packets larger than max_allowed_packet are not allowed。
- 查当前值:
SHOW VARIABLES LIKE 'max_allowed_packet';,默认常为 4MB,不够就改大(如 64M) -
net_buffer_length控制单次网络包大小,太小会导致频繁拆包,影响游标流速,建议设为 128K~1M - 修改后需重启 MySQL 或用
SET GLOBAL(需权限),且 PHP 连接要重新建立才生效 - 更稳妥的做法是在 SQL 里对大字段做截断:
SUBSTR(content, 1, 1000),避免意外撑爆缓冲
用 fputcsv() 直接写文件流,别拼字符串再 file_put_contents
拼完整 CSV 字符串再写,等于又建了一次内存副本;而 fputcsv() 接文件句柄,边 fetch 边 write,全程零中间字符串。
- 打开输出流:
$fp = fopen('php://output', 'w')(浏览器下载)或fopen('/tmp/export.csv', 'w')(后台生成) - 写表头:
fputcsv($fp, ['ID', '姓名', '邮箱']) - 每 fetch 一行,立刻
fputcsv($fp, $row),$row必须是索引数组,不是关联数组 - 如果字段含逗号、换行、双引号,
fputcsv()自动处理转义,不用自己str_replace - 导出完记得
fclose($fp),不然文件可能不完整
游标真正起效的前提,是整条链路——SQL、PDO 属性、PHP 处理、文件写入——全部避开“全量加载”这个陷阱。最容易漏的是中间某次调试用的 dump($list) 或日志里不小心打印了结果集,一执行就崩内存。
php免费学习视频:立即使用
踏上前端学习之旅,开启通往精通之路!从前端基础到项目实战,循序渐进,一步一个脚印,迈向巅峰!











