php中从数据库到csv的内存使用导出

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

我需要将数据从mysql导出到csv。我必须从几个表中选择数据,将它们放入数组中,然后对它们进行处理,并将它们作为.csv返回浏览器。 我注意到数组消耗了大量的行。 例如,我在1.8M的数据库中导入了一个.csv,然后我尝试从.csv的数据库中导出该数据。内存_get_peak_usage()显示超过128M的数据存储阵列

例如,这个小数组占用700多个字节:

$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.值分隔符“|”)作为语言 来自
订阅者
左键连接订阅服务器上的订阅服务器多值AS
t1
。订阅服务器\u id=t1.subscriber\u id和t1.field\u id=112 订阅服务器上的左连接
subscribers\u多值
AS
t2
。订阅服务器\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中。有关详细信息,请参见文档

确保没有使用,这样整个文档就不需要内存。如果您将内容写入服务器上的文件(例如,使用),您可以稍后使用或类似工具将该文件传递给客户端。如果需要,您可以将该文件用作缓存