V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
MrMike
V2EX  ›  PHP

PHP 如何高效快速解析 csv 数据并插入更新数据库?

  •  
  •   MrMike · 2017-03-11 06:11:23 +08:00 · 3784 次点击
    这是一个创建于 2602 天前的主题,其中的信息可能已经有所发展或是发生改变。

    功能需求:批量插入更新产品数据。

    工作流程:将需要更新的产品数据(csv)+产品图片压缩成一个 zip 文件,上传至服务器指定目录,然后通过 web 方式,解压 zip 文件,读取解析 csv 文件,分离出产品的主要数据和属性数据,并根据 csv 中每一个产品的 ID 获取该产品的图片路径,然后复制图片到以产品序号命名的文件夹中,然后通过字符串连接的方式,组成多条 SQL 语句,最后统一插入数据库。 另外,产品的搜索使用 elasticsearch ,所以在更新产品数据时,同时需要更新 elasticsearch 中的数据。

    ZIP 文件结构: 1,product.csv 2,以 csv 中每一条产品的 ID 为命名的文件夹,文件夹里包含三个文件夹(缩略图,产品图,附件),缩略图为一个图片,产品图和附件为多个文件; 3 , csv 文件和产品 ID 为命名的文件夹同级;

    涉及到的数据表: 1,product:存储产品的主要数据:标题,缩略图,单价,详细描述,店铺 ID ,创建人 ID ,分类 ID 等等 2,product_attribute:存储产品的属性数据:规格,尺寸,重量,以及其他属性内容; 3,product_images:存储产品图片以及附件,根据其中 type 字段进行区分

    服务器配置(均为阿里云服务器): 1,测试服务器:CPU:2 核,内存:4G 内存,mysql 在服务器上; 2,线上服务器:CPU:4 核,内存:8G 内存,mysql 为单独的 RDS:CPU:1 核,内存:1G 内存,最大连接数为 300;

    PHP 框架: Symfony 3 数据插入语句是: $productAttachmentSQLExec = $dataConnection->prepare($productAttachmentSQL); $productAttachmentSQLExec->execute();

    现在遇到的问题是: 就算处理一个 100M 以内的压缩包,都会导致内存溢出,数据导入不成功, csv 中的数据仅仅为 100 多条数据; 经过测试,如果只解析 csv 文件,就算有 1 万条数据,都可以处理,但是要涉及到图片的处理,就内存溢出。

    我想过通过写 shell 脚本来处理 zip 的解压以及移动,但是不熟悉这个 shell 脚本的编写,所以进行不下去。

    请教下 V2 上的朋友,有没有好的解决方案。

    18 条回复    2017-03-12 11:28:47 +08:00
    yangqi
        1
    yangqi  
       2017-03-11 06:16:41 +08:00
    你不要告诉我你们图片直接存数据库里面?

    好的解决方案就是把图片寸硬盘上,数据库最多存个图片的路径名
    willakira
        2
    willakira  
       2017-03-11 07:23:15 +08:00
    图片处理?是压缩么
    不用存在数据库,否则很难用 nginx 之类的加速
    顺便说下 4G 内存解压一个 100M 的普通压缩包绰绰有余(只要你不在内存里面做其他操作)
    fuxkcsdn
        3
    fuxkcsdn  
       2017-03-11 08:25:11 +08:00 via iPhone
    主要是压缩包里的图片解压后导致 php 内存溢出,很明显楼主犯了和这个链接的 po 主一样的错误
    http://stackoverflow.com/questions/3263129/unzipping-larger-files-with-php
    Time2
        4
    Time2  
       2017-03-11 08:39:38 +08:00
    可以使用 生成器 的 yield 关键字,来读取 csv 文件 ,会减少发部分内存消耗。
    Time2
        5
    Time2  
       2017-03-11 08:43:09 +08:00
    忽略 4 楼。我没看清 lz 需求...
    likezun
        6
    likezun  
       2017-03-11 09:04:05 +08:00
    看见 Symfony 3 好评!
    Felldeadbird
        7
    Felldeadbird  
       2017-03-11 09:04:08 +08:00 via iPhone   ❤️ 1
    1.每次循环处理完图片,记得释放内存。删除没用的变量数组,降低内存的消耗。
    2.如果还不行,就将图片处理部分异步。先将图片地址丢到数据库和临时目录,然后 cli 命令下定时处理。
    3.楼主上传 csv 是通过 web 形式吧?这个肯定是有内存限制的,或者做异步处理,文件先上传到服务器,然后 cli 定时脚本处理,处理成功发送通知…
    likezun
        8
    likezun  
       2017-03-11 09:05:11 +08:00
    我也觉得 4G 内存解压一个 100M 绰绰有余;
    内存溢出? 内存限制多少?
    MrMike
        9
    MrMike  
    OP
       2017-03-11 09:29:52 +08:00
    @Felldeadbird
    @yangqi
    图片肯定不是存储在数据库的,是存在硬盘上的。
    csv 在压缩包里,是先通过 ftp 上传到服务器后,再通过 web 的方式进入网站后台去进行在线解压缩并解析 csv 插入更新数据库的。
    内存,测试服务器上是 2G,线上服务器是 4G 。

    我想过异步处理,但是对 shell 脚本不熟,所以没有完全实现。

    谢谢。
    yangqi
        10
    yangqi  
       2017-03-11 11:12:22 +08:00
    @MrMike 那你说的只解析 csv, 1 万条数据都能处理是什么意思。

    如果内存溢出那肯定是代码的问题,分段处理,之后释放内存,不太可能导致溢出
    qhxin
        11
    qhxin  
       2017-03-11 11:21:41 +08:00
    php 进程的内存限制配置的多少?
    MrMike
        12
    MrMike  
    OP
       2017-03-11 11:39:59 +08:00
    @yangqi 代码单独解析 csv 文件的话, 1 万条数据没问题,但是如果再解析每一个产品所对应的产品图片,获取对应的图片路径,再插入数据库的话,就内存溢出了。我是把一万条数据组成一条 MYSQL INSERT INTO 语句一次性插入的,我直接将生成的 MYSQL 语句单独使用 PHPMYADMIN 进行插入,是完全可以执行的。但是一旦放在代码里,就不行。
    MrMike
        13
    MrMike  
    OP
       2017-03-11 11:40:22 +08:00
    @qhxin PHP 的内存限制:测试服务器上是 2G,线上服务器是 4G 。
    MrMike
        14
    MrMike  
    OP
       2017-03-11 11:44:51 +08:00
    @yangqi 我试试不用 doctrine 类来插入数据,看看行不行。
    yangqi
        15
    yangqi  
       2017-03-11 11:58:38 +08:00
    @MrMike 一次性插入那迟早会爆掉啊。批量就好了啊,比如每次 1000 条
    MrMike
        16
    MrMike  
    OP
       2017-03-11 12:06:21 +08:00
    @yangqi 就是,我也发觉,我在尝试分段插入,我再检查下我的代码,也可能是用了多次数据库查询和循环,造成了内存溢出了。谢谢哈。
    mingyun
        17
    mingyun  
       2017-03-12 08:20:46 +08:00
    @MrMike 在 foreach 循环插入?改成批量吧
    MrMike
        18
    MrMike  
    OP
       2017-03-12 11:28:47 +08:00
    @mingyun 现在没有在 foreach 里面插入数据了,目前就是在循环里串联字符串,然后在循环外进行数据插入的工作。但是现在如果数据很大的话,一次性将 1 万条数据循环完,就要内存溢出了,在循环内,还有几次数据查询的业务。
    我现在比较困惑如何更有效的分段循环并插入数据了。
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   我们的愿景   ·   实用小工具   ·   1269 人在线   最高记录 6543   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 27ms · UTC 23:30 · PVG 07:30 · LAX 16:30 · JFK 19:30
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.