从MySQL读取数据到PHP数组时内存占用过高的原因及优化方案
MySQL数据转PHP数组内存占用过高的原因及优化方案
问题描述
从MySQL表读取14300行数据(表大小10.8MB),通过以下代码存储为多维PHP数组后,内存占用达到约74MB:
$res = mysqli_query( $con, "SELECT * FROM myTable WHERE 1 ORDER BY type ASC, added DESC" ); while( $row = mysqli_fetch_assoc( $res ) ) { $array[$row['cat']][$row['type']][] = $row; }
内存检测代码:
echo memory_get_usage(); // 返回74.6MB echo memory_get_peak_usage(); // 返回74.6MB
注:表中type、cat、added字段均已建立索引
内存占用过高的原因
- PHP数组的结构开销:PHP关联数组底层基于哈希表实现,每个键值对都需要额外内存存储哈希表节点、zval容器(包含变量类型、值、引用计数等元数据),这些结构开销远大于原始数据本身的大小。
- 多维数组嵌套的额外消耗:你创建的是三维数组,每一层子数组都会生成独立的哈希表结构,进一步累加内存占用。
- 关联数组的键名开销:
mysqli_fetch_assoc返回的是字符串键名的数组,字符串键本身需要存储字符数据,相比数字索引数组,每个元素的内存消耗更高。 - 数据类型转换开销:MySQL返回的原始数据(如整数、日期)会被PHP转换为对应变量类型(通常是字符串或PHP原生类型),转换过程会产生额外内存占用。
优化方案
1. 只查询必要字段
避免使用SELECT *,明确指定业务所需字段,直接减少每行数据的体积:
// 示例:仅查询cat、type及业务核心字段 $res = mysqli_query( $con, "SELECT cat, type, col1, col2 FROM myTable ORDER BY type ASC, added DESC" );
2. 使用索引数组代替关联数组
用mysqli_fetch_row或mysqli_fetch_array(MYSQLI_NUM)获取数字索引数组,省去字符串键名的内存开销:
while( $row = mysqli_fetch_row( $res ) ) { // 注意按查询字段顺序对应cat、type的位置 $array[$row[0]][$row[1]][] = $row; }
3. 减少数组嵌套层级
如果业务逻辑允许,将多维结构改为一维数组,用组合键替代嵌套:
while( $row = mysqli_fetch_assoc( $res ) ) { $key = "{$row['cat']}_{$row['type']}"; $array[$key][] = $row; }
4. 使用生成器分批处理(PHP 5.5+)
如果不需要一次性加载所有数据到内存,用生成器逐个返回数据,彻底降低内存峰值:
function getTableData($con) { $res = mysqli_query($con, "SELECT cat, type, col1, col2 FROM myTable ORDER BY type ASC, added DESC"); while ($row = mysqli_fetch_assoc($res)) { yield [$row['cat'], $row['type']] => $row; } } // 遍历生成器处理数据 foreach (getTableData($con) as $key => $row) { // 逐个处理逻辑 }
5. 分批查询数据
如果必须保留完整数组,可通过LIMIT和OFFSET分批读取并合并,避免一次性加载所有数据:
$totalRows = 14300; $batchSize = 1000; $array = []; for ($offset = 0; $offset < $totalRows; $offset += $batchSize) { $res = mysqli_query( $con, "SELECT cat, type, col1, col2 FROM myTable ORDER BY type ASC, added DESC LIMIT $offset, $batchSize" ); while( $row = mysqli_fetch_assoc( $res ) ) { $array[$row['cat']][$row['type']][] = $row; } }
6. 调整PHP内存配置(治标不治本)
若以上优化仍无法满足需求,可临时修改php.ini中的memory_limit参数,但这仅为缓解手段,不推荐作为优先方案。
内容的提问来源于stack exchange,提问作者ipel
相关产品推荐
相关产品推荐

