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

2017-03-11 06:11:23 +08:00
 MrMike

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

工作流程:将需要更新的产品数据(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 上的朋友,有没有好的解决方案。

3787 次点击
所在节点    PHP
18 条回复
yangqi
2017-03-11 06:16:41 +08:00
你不要告诉我你们图片直接存数据库里面?

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

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

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

如果内存溢出那肯定是代码的问题,分段处理,之后释放内存,不太可能导致溢出
qhxin
2017-03-11 11:21:41 +08:00
php 进程的内存限制配置的多少?
MrMike
2017-03-11 11:39:59 +08:00
@yangqi 代码单独解析 csv 文件的话, 1 万条数据没问题,但是如果再解析每一个产品所对应的产品图片,获取对应的图片路径,再插入数据库的话,就内存溢出了。我是把一万条数据组成一条 MYSQL INSERT INTO 语句一次性插入的,我直接将生成的 MYSQL 语句单独使用 PHPMYADMIN 进行插入,是完全可以执行的。但是一旦放在代码里,就不行。
MrMike
2017-03-11 11:40:22 +08:00
@qhxin PHP 的内存限制:测试服务器上是 2G,线上服务器是 4G 。
MrMike
2017-03-11 11:44:51 +08:00
@yangqi 我试试不用 doctrine 类来插入数据,看看行不行。
yangqi
2017-03-11 11:58:38 +08:00
@MrMike 一次性插入那迟早会爆掉啊。批量就好了啊,比如每次 1000 条
MrMike
2017-03-11 12:06:21 +08:00
@yangqi 就是,我也发觉,我在尝试分段插入,我再检查下我的代码,也可能是用了多次数据库查询和循环,造成了内存溢出了。谢谢哈。
mingyun
2017-03-12 08:20:46 +08:00
@MrMike 在 foreach 循环插入?改成批量吧
MrMike
2017-03-12 11:28:47 +08:00
@mingyun 现在没有在 foreach 里面插入数据了,目前就是在循环里串联字符串,然后在循环外进行数据插入的工作。但是现在如果数据很大的话,一次性将 1 万条数据循环完,就要内存溢出了,在循环内,还有几次数据查询的业务。
我现在比较困惑如何更有效的分段循环并插入数据了。

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/346608

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX