node操作mysql百万量级数据文件队列下载
最近很头疼 PHP写了个下载 一天12小时看护 经常执行到一半出错 只能区区下1000个文件 而这1000个文件你也不能确定是不是都OK的
然后决定还是用Node写一写 之前经历了 断网 + 内存爆的问题 现在终于成功了!
我使用的是centos系统 2G内存 至于用windows处理也可以 不过windows就是个坑 尽量还是别用了
由于nodejs是异步操作 因此在操作大量数据的时候 必须使用队列
队列示例:
var array = [1,2,3,4]; function check(){ if(array.length > 0){ var url = array.pop(); load(url); } } function load(url) { console.log(url); check(); } load(); return;
如果需要做一个sleep的效果 示例
var array = [1,2,3,4]; function sleep(milliSeconds){ var startTime =new Date().getTime(); while(new Date().getTime()< startTime + milliSeconds); } function check(){ if(array.length > 0){ var url = array.pop(); load(url); } } function load(url) { console.log(url); console.log('waiting'); sleep(10000); //暂停10s check(); } load();
我的完整代码是 取出数据库3000个数据 对每个数据指定文件夹下载4个文件 也就是一次性下载12000个文件 下载500个文件夹 2000个文件只用了20分钟 2G内存实际监控内存使用率不超过 15% 因为是异步加载 为了省事 我就先不关闭数据库了 但一次下太多 有时候网络不好 可能会报错 因此 每次可以少运行一点 分多次运行 用命令行命令控制limit 也是很不错的 (这里就不改进了)
"use strict"; var http = require("http"); var fs = require("fs"); // 加载编码转换模块 var iconv = require('iconv-lite'); /* var server = http.createServer(function(req, res){ res.writeHead(200, {'Content-type' : 'text/html'}); res.write('<h1>Node.js</h1>'); res.end('<p>Hello World</p>'); }).listen(50082); console.log("http start");*/ //连接数据库 var mysql = require('mysql'); var connection = mysql.createConnection({ host: 'localhost', user: 'root', password: '', database:'gftest' }); var lee = 1; /* update giphy_posts set nodea = '' where id < 2001; update giphy_posts set nodestate = 0 where id < 2001; http.get('http://media0.giphy.com/media/zXHZWGLWNQkrS/giphy.mp4', function(res){ console.log(res.statusCode +.res.headers['content-type']); //writeFile('a.txt',res); }) return;*/ connection.connect(); //查询 connection.query('select * from `giphy_posts` limit 0,3000', function(err, rows, fields) { if (err) throw err; function check(){ if(rows.length > 0){ var row = rows.pop(); load(row); } } function log(name,data){ //var data = 'hello world'; var date = new Date(); var year = date.getFullYear(); var month = date.getMonth()+1; var day = date.getDate(); var hour = date.getHours(); var minute = date.getMinutes(); var second = date.getSeconds(); var data = year+'.'+month+'.'+day+' '+hour+':'+minute+':'+second + ' - ' + data + '\n'; fs.appendFile('./log/'+name+'.txt',data,'utf8',function(err){ if(err) { console.log('lee log fun 函数出错'+err); }else{ console.log(data); } }); } function load(row){ var url = new Array(); var k =0; //用于记录执行次数 触发check() url[0] = "http://media0.giphy.com/media/"+row.dataid+"/200w_s.gif"; url[1]= "http://media0.giphy.com/media/"+row.dataid+"/giphy.gif"; url[2]= "http://media0.giphy.com/media/"+row.dataid+"/giphy.mp4"; url[3]= "http://media0.giphy.com/media/"+row.dataid+"/giphy_s.gif"; if(row.nodestate > 4 || row.nodestate == 4){ //文件下载状态超过4 log('sqlerr','文件下载状态超过4,取消下载 不操作数据库 - 执行ID'+row.id); check(); //执行函数 return; }else if(row.nodestate > 0){ log('sqlerr','文件超过1,取消下载 不操作数据库 - 执行ID'+row.id); check(); //执行函数 return; } // return; url.forEach(function (v){ // console.log(v); http.get(v, function(res){ if(res.statusCode==200){ var imgData = ""; res.setEncoding("binary"); //一定要设置response的编码为binary否则会下载下来的图片打不开 res.on("data", function(chunk){ imgData+=chunk; }); res.on("end", function(){ //计算文件名 var patharr = v.split('/'); var filename = patharr[patharr.length-1]; //计算出文件名 var fileendarr = patharr[patharr.length-1].split('.'); var fileend = fileendarr[fileendarr.length-1]; //计算出后缀 /*console.log(fileend); return;*/ //创建目录 if(!fs.existsSync('./a/'+row.dataid)){ var creats = fs.mkdirSync('./a/'+row.dataid, 777); } fs.writeFile("./a/"+row.dataid + '/' + Math.random() +"."+fileend, imgData, "binary", function(err){ if(err){ log('filerun',"down fail 下载失败 - "+row.dataid+"."+fileend+' - query id ='.row.id + '\n' +err); if (err) throw err; }else{ //更新数据库把文件名输入进去 connection.query("update giphy_posts set nodea=concat(nodea,'"+fileend+",') where id="+row.id, function(err, result) { if (err) throw err; connection.query("update giphy_posts set nodestate=nodestate+1 where id="+row.id, function(err, result) { if (err) throw err; // console.log('update '+row.id+'OK'); //console.log(result); log('sqlrun','update nodestate +int '+row.id+' -OK'); //console.log('\n'); // console.log("down success - "+row.dataid+"."+fileend+' - query id ='+row.id); }); log('sqlrun','update add nodea file +name '+row.id+' -OK'); //console.log(result); console.log('\n'); log('filerun',"down success - "+row.dataid+"."+fileend+' - query id ='+row.id); if(++k >3){ //大于3执行到最后一次执行chuck check(); } }); } }); }); }else{ if(++k >3){ //大于3执行到最后一次执行chuck check(); } log('filenot200','ID'+row.id+'文件返回状态码不是200下载失败 - '+v); } }); }); //check(); //完整下载完再check执行 内存爆表 } check(); //初次执行队列 // rows.forEach(function(val){ // console.log(val.id + ' - 查询结果为: ', val.dataid); /* var url = "http://media0.giphy.com/media/"+val.dataid+"/giphy.mp4"; http.get(url, function(res){ var imgData = ""; res.setEncoding("binary"); //一定要设置response的编码为binary否则会下载下来的图片打不开 res.on("data", function(chunk){ imgData+=chunk; }); res.on("end", function(){ fs.writeFile("./a/"+val.dataid+".mp4", imgData, "binary", function(err){ if(err){ console.log("down fail - "+val.dataid+'query id ='.val.id); } console.log("down success - "+val.dataid+'query id ='+val.id); }); }); });*/ // }); }); //关闭连接 //connection.end(); return; var sitearr =new Array(); for(var i =0;i<10;i++){ sitearr[i]=i; } //var sitearr = [1,2,3]; sitearr.forEach(function (fname){ var url = "http://daysvr.com/static/lee/images/logo.png"; http.get(url, function(res){ var imgData = ""; res.setEncoding("binary"); //一定要设置response的编码为binary否则会下载下来的图片打不开 res.on("data", function(chunk){ imgData+=chunk; }); res.on("end", function(){ fs.writeFile("./a/"+fname+".png", imgData, "binary", function(err){ if(err){ console.log("down fail - "+fname); } console.log("down success - "+fname); }); }); }); });