php中从数据库到csv的内存使用导出
我需要将数据从mysql导出到csv。我必须从几个表中选择数据,将它们放入数组中,然后对它们进行处理,并将它们作为.csv返回浏览器。 我注意到数组消耗了大量的行。 例如,我在1.8M的数据库中导入了一个.csv,然后我尝试从.csv的数据库中导出该数据。内存_get_peak_usage()显示超过128M的数据存储阵列 例如,这个小数组占用700多个字节:php中从数据库到csv的内存使用导出,php,mysql,export,memory-management,Php,Mysql,Export,Memory Management,我需要将数据从mysql导出到csv。我必须从几个表中选择数据,将它们放入数组中,然后对它们进行处理,并将它们作为.csv返回浏览器。 我注意到数组消耗了大量的行。 例如,我在1.8M的数据库中导入了一个.csv,然后我尝试从.csv的数据库中导出该数据。内存_get_peak_usage()显示超过128M的数据存储阵列 例如,这个小数组占用700多个字节: $startMemory = memory_get_usage(); //get constant fields o
$startMemory = memory_get_usage();
//get constant fields of the subscriber
$data = array(array('subscriber_id' => 1315444, 'email_address' => 'test0@gmail.com',
'first_name' => 'Michael', 'last_name' => 'Allen'));
echo memory_get_usage() - $startMemory;
因此,即使导出数兆字节的数据,也需要php脚本中数百兆字节的内存。
有没有办法解决这个问题?
表:
还有其他字段表,类似于带有数字、日期字符串的表。对于它们来说,主键是订户id、字段id
当查询失败时(例如,我们有几个自定义字段):
选择订阅者
电子邮件地址
,订阅者
名字,订阅者
姓氏,
组_CONCAT(t1.值分隔符“|”)作为颜色,组_CONCAT(t2.值分隔符“|”)作为语言
来自订阅者
左键连接订阅服务器上的订阅服务器多值ASt1
。订阅服务器\u id=t1.subscriber\u id和t1.field\u id=112
订阅服务器上的左连接subscribers\u多值ASt2
。订阅服务器\u id=t2。订阅服务器\u id和t2。字段\u id=111
其中(列表id=40)
按订户
电子邮件地址
,订户
姓名
,订户
姓氏
它将返回以下内容:
test1000@gmail.com米歇尔布什红|红|蓝|蓝英语|西班牙语|英语|西班牙语而非西班牙语
test1000@gmail.com米歇尔布什红|蓝
英语|西班牙语
感谢您提供的任何信息。如果您的业务逻辑允许,您可以在mysql中进行转换并执行
SELECT * from table INTO OUTFILE 'file_name.csv'
它具有与加载数据填充相同的选项,您要写入的文件必须不存在。如果您的业务逻辑允许,您可以在mysql中进行转换并执行
SELECT * from table INTO OUTFILE 'file_name.csv'
它具有与加载数据填充相同的选项,您要写入的文件必须不存在。将代码重构为单个函数,该函数读取第n行的数据,对其进行处理,输出该行而不进行输出缓冲,并丢弃所有临时数据。反复调用该函数。这会将内存使用量减少到给定行上所需的内存,而不是将所有行一起处理
完成后,您可以将其扩展为同时读取任意数量的行,以调整内存使用和性能折衷。将代码重构为单个函数,该函数读取第n行的数据,对其进行处理,输出该行而无需输出缓冲,并丢弃所有临时数据。反复调用该函数。这会将内存使用量减少到给定行上所需的内存,而不是将所有行一起处理
完成此操作后,您可以将其扩展为同时读取任意数量的行,以调整内存使用和性能折衷。仅使用两个表: 您的原始查询:
SELECT subscribers.email_address,
subscribers.first_name,
subscribers.last_name,
t1.value AS Languages
FROM subscribers
LEFT JOIN (SELECT subscriber_id,
field_id,
GROUP_CONCAT(value SEPARATOR '|') AS value
FROM subscribers_multivalued
WHERE field_id=37
GROUP BY subscriber_id, field_id
) AS t1
ON subscribers.subscriber_id=t1.subscriber_id
AND t1.field_id=37
WHERE (list_id=49)
AND (state=1)
给出了以下方面的说明计划:
id select_type table type possible_keys key key_len ref rows Extra
1 PRIMARY subscribers ref FK_list_id FK_list_id 4 const 2 Using where
1 PRIMARY <derived2> ALL NULL NULL NULL NULL 5
2 DERIVED subscribers_multivalued ALL field_fk field_fk 4 11 Using filesort
id select_type table type possible_keys key key_len ref rows Extra
1 SIMPLE subscribers ref FK_list_id FK_list_id 4 const 2 Using where; Using filesort
1 SIMPLE t1 ref subscriber_fk,field_fk subscriber_fk 4 test.subscribers.subscriber_id 1
给出了以下方面的说明计划:
id select_type table type possible_keys key key_len ref rows Extra
1 PRIMARY subscribers ref FK_list_id FK_list_id 4 const 2 Using where
1 PRIMARY <derived2> ALL NULL NULL NULL NULL 5
2 DERIVED subscribers_multivalued ALL field_fk field_fk 4 11 Using filesort
id select_type table type possible_keys key key_len ref rows Extra
1 SIMPLE subscribers ref FK_list_id FK_list_id 4 const 2 Using where; Using filesort
1 SIMPLE t1 ref subscriber_fk,field_fk subscriber_fk 4 test.subscribers.subscriber_id 1
虽然我只使用非常小的数据量填充了这两个表,但这对我来说意味着我的查询版本将更有效地针对数据库执行,因为它不使用查询生成的派生表
其他表也可以以大致相同的方式链接到查询中,整个结果直接假脱机到csv文件,而不是用PHP进一步解析
这将使您的运行速度更快,内存效率更高
编辑
SELECT subscribers.email_address,
subscribers.first_name,
subscribers.last_name,
GROUP_CONCAT(DISTINCT t1.value SEPARATOR '|') AS Colors,
GROUP_CONCAT(DISTINCT t2.value SEPARATOR '|') AS Languages
FROM subscribers
LEFT JOIN subscribers_multivalued AS t1
ON subscribers.subscriber_id=t1.subscriber_id
AND t1.field_id=112
LEFT JOIN subscribers_multivalued AS t2
ON subscribers.subscriber_id=t2.subscriber_id
AND t2.field_id=37
WHERE (list_id=49)
GROUP BY subscribers.email_address,
subscribers.first_name,
subscribers.last_name
请注意,仅使用两个表在GROUP_CONCAT()函数中使用DISTINCT: 您的原始查询:
SELECT subscribers.email_address,
subscribers.first_name,
subscribers.last_name,
t1.value AS Languages
FROM subscribers
LEFT JOIN (SELECT subscriber_id,
field_id,
GROUP_CONCAT(value SEPARATOR '|') AS value
FROM subscribers_multivalued
WHERE field_id=37
GROUP BY subscriber_id, field_id
) AS t1
ON subscribers.subscriber_id=t1.subscriber_id
AND t1.field_id=37
WHERE (list_id=49)
AND (state=1)
给出了以下方面的说明计划:
id select_type table type possible_keys key key_len ref rows Extra
1 PRIMARY subscribers ref FK_list_id FK_list_id 4 const 2 Using where
1 PRIMARY <derived2> ALL NULL NULL NULL NULL 5
2 DERIVED subscribers_multivalued ALL field_fk field_fk 4 11 Using filesort
id select_type table type possible_keys key key_len ref rows Extra
1 SIMPLE subscribers ref FK_list_id FK_list_id 4 const 2 Using where; Using filesort
1 SIMPLE t1 ref subscriber_fk,field_fk subscriber_fk 4 test.subscribers.subscriber_id 1
给出了以下方面的说明计划:
id select_type table type possible_keys key key_len ref rows Extra
1 PRIMARY subscribers ref FK_list_id FK_list_id 4 const 2 Using where
1 PRIMARY <derived2> ALL NULL NULL NULL NULL 5
2 DERIVED subscribers_multivalued ALL field_fk field_fk 4 11 Using filesort
id select_type table type possible_keys key key_len ref rows Extra
1 SIMPLE subscribers ref FK_list_id FK_list_id 4 const 2 Using where; Using filesort
1 SIMPLE t1 ref subscriber_fk,field_fk subscriber_fk 4 test.subscribers.subscriber_id 1
虽然我只使用非常小的数据量填充了这两个表,但这对我来说意味着我的查询版本将更有效地针对数据库执行,因为它不使用查询生成的派生表
其他表也可以以大致相同的方式链接到查询中,整个结果直接假脱机到csv文件,而不是用PHP进一步解析
这将使您的运行速度更快,内存效率更高
编辑
SELECT subscribers.email_address,
subscribers.first_name,
subscribers.last_name,
GROUP_CONCAT(DISTINCT t1.value SEPARATOR '|') AS Colors,
GROUP_CONCAT(DISTINCT t2.value SEPARATOR '|') AS Languages
FROM subscribers
LEFT JOIN subscribers_multivalued AS t1
ON subscribers.subscriber_id=t1.subscriber_id
AND t1.field_id=112
LEFT JOIN subscribers_multivalued AS t2
ON subscribers.subscriber_id=t2.subscriber_id
AND t2.field_id=37
WHERE (list_id=49)
GROUP BY subscribers.email_address,
subscribers.first_name,
subscribers.last_name
请注意,在GROUP_CONCAT()函数中使用DISTINCT似乎是为了防止超级分组,可以在聚合函数GROUP_CONCAT中使用DISTINCT:
SELECT `subscribers`.`email_address`, `subscribers`.`first_name`, `subscribers`.`last_name`,
GROUP_CONCAT(DISTINCT t1.value SEPARATOR '|') AS Colors, GROUP_CONCAT(DISTINCT t2.value SEPARATOR '|') AS Languages
FROM `subscribers`
LEFT JOIN `subscribers_multivalued` AS `t1` ON subscribers.subscriber_id=t1.subscriber_id AND t1.field_id=49
LEFT JOIN `subscribers_multivalued` AS `t2` ON subscribers.subscriber_id=t2.subscriber_id AND t2.field_id=48
WHERE (list_id=63)
GROUP BY `subscribers`.`email_address`, `subscribers`.`first_name`, `subscribers`.`last_name`
看起来要防止超级分组,可以在聚合函数组中使用DISTINCT\u CONCAT:
SELECT `subscribers`.`email_address`, `subscribers`.`first_name`, `subscribers`.`last_name`,
GROUP_CONCAT(DISTINCT t1.value SEPARATOR '|') AS Colors, GROUP_CONCAT(DISTINCT t2.value SEPARATOR '|') AS Languages
FROM `subscribers`
LEFT JOIN `subscribers_multivalued` AS `t1` ON subscribers.subscriber_id=t1.subscriber_id AND t1.field_id=49
LEFT JOIN `subscribers_multivalued` AS `t2` ON subscribers.subscriber_id=t2.subscriber_id AND t2.field_id=48
WHERE (list_id=63)
GROUP BY `subscribers`.`email_address`, `subscribers`.`first_name`, `subscribers`.`last_name`
因此,即使导出数兆字节的数据,也需要php脚本中数百兆字节的内存。有没有办法解决这个问题
由于被标记为这一部分的副本,我将回答您问题的这一部分,而不考虑您询问的所有其他具体细节
PHP中基于行的处理
为了减少PHP在任何时候所需的内存量,您可以一次从MySQL服务器获取一行,并将它们传递到客户端浏览器(或服务器上的某个文件),而无需缓冲
要一次获取一行,请将MYSQLI\u USE\u RESULT
作为resultmode参数添加到调用中,以便一次迭代一行结果,而不将它们全部转移到PHP中。有关详细信息,请参见文档
确保没有使用,这样整个文档就不需要内存。如果您将内容写入服务器上的文件(例如,使用),您可以稍后使用或类似工具将该文件传递给客户端。如果需要,您可以将该文件用作缓存