记录一次神坑操作–导出500万的数据
- 4040
- PHP
- 0
- super_dodo
- 2018/06/15
有时候不得不承认自己笨得可以.
接到任务需求是:有一家重点客户公司想把他们自己的数据导出来,大概有500万条数据。主要的数据存储在mongoDB里面。为了数据的直观性,数据还会关联到MySQL以及PostgreSQL去查询出一些用户或客户的信息。
拿到需求的时候觉得好像没什么难度,虽然对这部分数据和功能不太了解,毕竟界面上有接口,参照接口层就可以理清逻辑。后来又各种杂事和开会,到快下班了,发现自己的任务还没开始写,略显恐慌。
开始自己的搬砖过程。先在测试环境把需要的逻辑和数据组织一下,因为不清楚原先的逻辑,还是有些费神。问人是个好办法,但是问之前自己得先理清楚逻辑,而且有时候自己觉得应该自立一些,独立完成任务。就这样拼拼凑凑少量的数据能够导出了。
接来下放到线上的测试服务器去跑脚本了。各种错误一箩筐,登录态,异常数据,超时等等.先一个一个的解决。继续跑,几千条没什么问题,多了之后又内存不足。
开始我怀疑是我写文件的大小限制了,首先拆分文件。再去跑,发现不是,当数据跑到十几万的时候,是mongodb内存不够了。我把每页查询的数值从1000条,不断的调整成了500,200,100,当数据跑到十几万的时候,进程还是因为内存不足而挂掉了。比较疑惑,后来问了一下有过大数据处理经验的大神。mongodb里面数据量大的时候不能用limit().skip()的形式进行分页查询。要用上一次的最大id进行下一次的查询,而且不能用排序,mongodb大了之后索引会很大,排序会导致内存不足。修改之后再次上线去做实验。
public function actionRunTask() { header('content-type:text/html; charset=utf-8'); set_time_limit(0); //不限时 ini_set('memory_limit','2048M'); //内存 $client_id = 1; $user_id = 1; $pageSize = 1000; $maxId = 0; for ($i = 0; $i <= 5000; $i++) { $maxId = $this->trailList($client_id, $user_id, $maxId, $pageSize); file_put_contents('/tmp/dodo_trial_cnt.log', $i.PHP_EOL, FILE_APPEND); usleep(200); } } public function trailList($client_id, $user_id, $maxId, $pageSize) { $params['client_id'] = MongoObject::int32Value($client_id); $params['_id'] = ['$gt' => $maxId]; $limit['limit'] = MongoObject::int32Value($pageSize); User::setLoginUserById($user_id); $cursor = CompanyTrail::getCollection()->find($params, $limit); $dataList = []; foreach ($cursor as $item) { $itemData = MongoObject::trimBsonDocument($item); $dataList[] = $itemData; } $resultPath = '/tmp/dodo_trial_all.csv'; $fp = fopen($resultPath, 'a+'); fwrite($fp,chr(0xEF).chr(0xBB).chr(0xBF)); //防止乱码 $result = []; foreach ($dataList as $item) { $row['trial_id'] = $item['_id']; //动态ID //.........此处省略其他字段和逻辑 $row['create_time'] = $item['create_time']; //创建时间 fputcsv($fp, array_values($row)); } fclose($fp); $maxKey = count($dataList) - 1; $maxId = intval($dataList[$maxKey]['_id']); return $maxId; }
其他相关注意的点
/data/app/protected/yiic dodo runTask & //& 后台运行 但是终端断开,进程就结束了 后台去跑脚本 //nohup /data/app/protected/yiic dodo runTask >> /tmp/dodo_error.log 不要担心文件太大会影响内存 //file_put_csv //fwrite($fp,chr(0xEF).chr(0xBB).chr(0xBF)); //解决乱码 导出来的文件需要按照行来分割的语句 //split -l 10000 demo.csv -d -a 4 do_ //mongodb 数据量大的时候 不能用 limit(100).skip(300000) 不要使用排序 //多任务去跑
因为数据量大且查询逻辑复杂,且脚本写的不好,造成了导出500万数据用了差不多5个多小时,导出的文件1G左右。原本是晚上跑的,前半段正常,就睡了,第二天早上来看任务失败了。所以跑了好几次,问题定位也不够准确。要完善和提高的地方很多。
要反思的地方还很多,马上端午了。先去武功山浪一下,回来好好反思和学习。祝大家端午节快乐。
相关阅读
- 通过Google API客户端访问Google Play帐户报告PHP库
- PHP执行文件的压缩和解压缩方法
- 消息中间件MQ与RabbitMQ面试题
- 如何搭建一个拖垮公司的技术架构?
- Yii2中ElasticSearch的使用示例
热门文章
- 通过Google API客户端访问Google Play帐户报告PHP库
- PHP执行文件的压缩和解压缩方法
- 消息中间件MQ与RabbitMQ面试题
- 如何搭建一个拖垮公司的技术架构?
- Yii2中ElasticSearch的使用示例
最新文章
- 通过Google API客户端访问Google Play帐户报告PHP库
- PHP执行文件的压缩和解压缩方法
- 消息中间件MQ与RabbitMQ面试题
- 如何搭建一个拖垮公司的技术架构?
- Yii2中ElasticSearch的使用示例